|
115 | 115 | import com.oracle.graal.python.runtime.exception.PException;
|
116 | 116 | import com.oracle.graal.python.util.Function;
|
117 | 117 | import com.oracle.graal.python.util.PythonUtils;
|
| 118 | +import com.oracle.graal.python.util.PythonSystemThreadTask; |
118 | 119 | import com.oracle.graal.python.util.Supplier;
|
119 | 120 | import com.oracle.graal.python.util.SuppressFBWarnings;
|
120 | 121 | import com.oracle.graal.python.util.WeakIdentityHashMap;
|
@@ -613,9 +614,10 @@ public void untrackObject(Object ptr, PFrame.Reference curFrame, TruffleString c
|
613 | 614 | // TODO(fa): implement untracking of container objects
|
614 | 615 | }
|
615 | 616 |
|
616 |
| - private static final class BackgroundGCTask implements Runnable { |
| 617 | + private static final class BackgroundGCTask extends PythonSystemThreadTask { |
617 | 618 |
|
618 | 619 | private BackgroundGCTask(PythonContext context) {
|
| 620 | + super("Python GC", LOGGER); |
619 | 621 | this.ctx = new WeakReference<>(context);
|
620 | 622 | this.rssInterval = context.getOption(PythonOptions.BackgroundGCTaskInterval);
|
621 | 623 | this.gcRSSThreshold = context.getOption(PythonOptions.BackgroundGCTaskThreshold) / (double) 100;
|
@@ -667,17 +669,25 @@ Long getCurrentRSS() {
|
667 | 669 | }
|
668 | 670 |
|
669 | 671 | @Override
|
670 |
| - public void run() { |
671 |
| - try { |
672 |
| - while (true) { |
673 |
| - Thread.sleep(rssInterval); |
674 |
| - perform(); |
675 |
| - } |
676 |
| - } catch (InterruptedException e) { |
677 |
| - Thread.currentThread().interrupt(); |
| 672 | + protected void doRun() { |
| 673 | + Node location = getSafepointLocation(); |
| 674 | + if (location == null) { |
| 675 | + return; |
| 676 | + } |
| 677 | + while (true) { |
| 678 | + TruffleSafepoint.setBlockedThreadInterruptible(location, Thread::sleep, rssInterval); |
| 679 | + perform(); |
678 | 680 | }
|
679 | 681 | }
|
680 | 682 |
|
| 683 | + private Node getSafepointLocation() { |
| 684 | + PythonContext context = ctx.get(); |
| 685 | + if (context == null) { |
| 686 | + return null; |
| 687 | + } |
| 688 | + return context.getLanguage().unavailableSafepointLocation; |
| 689 | + } |
| 690 | + |
681 | 691 | private void perform() {
|
682 | 692 | PythonContext context = ctx.get();
|
683 | 693 | if (context == null) {
|
@@ -764,9 +774,7 @@ void runBackgroundGCTask(PythonContext context) {
|
764 | 774 | || !context.getOption(PythonOptions.BackgroundGCTask)) {
|
765 | 775 | return;
|
766 | 776 | }
|
767 |
| - backgroundGCTaskThread = context.getEnv().newTruffleThreadBuilder(gcTask).context(context.getEnv().getContext()).build(); |
768 |
| - backgroundGCTaskThread.setDaemon(true); |
769 |
| - backgroundGCTaskThread.setName("python-gc-task"); |
| 777 | + backgroundGCTaskThread = context.createSystemThread(gcTask); |
770 | 778 | backgroundGCTaskThread.start();
|
771 | 779 | }
|
772 | 780 |
|
@@ -865,7 +873,7 @@ public static CApiContext ensureCapiWasLoaded(Node node, PythonContext context,
|
865 | 873 | Object finalizeSignature = env.parseInternal(Source.newBuilder(J_NFI_LANGUAGE, "():POINTER", "exec").build()).call();
|
866 | 874 | Object finalizingPointer = SignatureLibrary.getUncached().call(finalizeSignature, finalizeFunction);
|
867 | 875 | try {
|
868 |
| - cApiContext.addNativeFinalizer(env, finalizingPointer); |
| 876 | + cApiContext.addNativeFinalizer(context, finalizingPointer); |
869 | 877 | cApiContext.runBackgroundGCTask(context);
|
870 | 878 | } catch (RuntimeException e) {
|
871 | 879 | // This can happen when other languages restrict multithreading
|
@@ -898,14 +906,16 @@ public static CApiContext ensureCapiWasLoaded(Node node, PythonContext context,
|
898 | 906 | * We need to do it in a VM shutdown hook to make sure C atexit won't crash even if our context
|
899 | 907 | * finalization didn't run.
|
900 | 908 | */
|
901 |
| - private void addNativeFinalizer(Env env, Object finalizingPointerObj) { |
902 |
| - final Unsafe unsafe = getContext().getUnsafe(); |
| 909 | + private void addNativeFinalizer(PythonContext context, Object finalizingPointerObj) { |
| 910 | + final Unsafe unsafe = context.getUnsafe(); |
903 | 911 | InteropLibrary lib = InteropLibrary.getUncached(finalizingPointerObj);
|
904 | 912 | if (!lib.isNull(finalizingPointerObj) && lib.isPointer(finalizingPointerObj)) {
|
905 | 913 | try {
|
906 | 914 | long finalizingPointer = lib.asPointer(finalizingPointerObj);
|
| 915 | + // We are writing off heap memory and registering a VM shutdown hook, there is no |
| 916 | + // point in creating this thread via Truffle sandbox at this point |
907 | 917 | nativeFinalizerRunnable = () -> unsafe.putInt(finalizingPointer, 1);
|
908 |
| - nativeFinalizerShutdownHook = env.newTruffleThreadBuilder(nativeFinalizerRunnable).build(); |
| 918 | + nativeFinalizerShutdownHook = new Thread(nativeFinalizerRunnable); |
909 | 919 | Runtime.getRuntime().addShutdownHook(nativeFinalizerShutdownHook);
|
910 | 920 | } catch (UnsupportedMessageException e) {
|
911 | 921 | throw new RuntimeException(e);
|
@@ -946,14 +956,16 @@ public void exitCApiContext() {
|
946 | 956 | @SuppressWarnings("try")
|
947 | 957 | public void finalizeCApi() {
|
948 | 958 | CompilerAsserts.neverPartOfCompilation();
|
949 |
| - HandleContext handleContext = getContext().nativeContext; |
| 959 | + PythonContext context = getContext(); |
| 960 | + HandleContext handleContext = context.nativeContext; |
950 | 961 | if (backgroundGCTaskThread != null && backgroundGCTaskThread.isAlive()) {
|
| 962 | + context.killSystemThread(backgroundGCTaskThread); |
951 | 963 | try {
|
952 |
| - backgroundGCTaskThread.interrupt(); |
953 |
| - backgroundGCTaskThread.join(); |
| 964 | + backgroundGCTaskThread.join(10); |
954 | 965 | } catch (InterruptedException e) {
|
955 |
| - Thread.currentThread().interrupt(); |
| 966 | + LOGGER.finest("got interrupt while joining GC thread before cleaning up C API state"); |
956 | 967 | }
|
| 968 | + backgroundGCTaskThread = null; |
957 | 969 | }
|
958 | 970 |
|
959 | 971 | /*
|
|
0 commit comments