@@ -20,50 +20,95 @@ public static interface ProcessingReference<R extends ProcessingReference<R>> {
20
20
public R getNext ();
21
21
22
22
public void setNext (R next );
23
+
24
+ public ReferenceProcessingService <R > service ();
23
25
}
24
26
25
- /** The head of a doubly-linked list of FinalizerReference, needed to collect finalizer Procs for ObjectSpace. */
26
- private R first = null ;
27
+ public static class ReferenceProcessor {
28
+ protected final ReferenceQueue < Object > processingQueue = new ReferenceQueue <>() ;
27
29
28
- protected final ReferenceQueue <Object > processingQueue = new ReferenceQueue <>();
30
+ protected DynamicObject processingThread ;
31
+ protected final RubyContext context ;
29
32
30
- protected DynamicObject processingThread ;
31
- protected final RubyContext context ;
33
+ public ReferenceProcessor (RubyContext context ) {
34
+ this .context = context ;
35
+ }
32
36
33
- public ReferenceProcessingService (RubyContext context ) {
34
- this .context = context ;
35
- }
37
+ protected void processReferenceQueue () {
38
+ if (context .getOptions ().SINGLE_THREADED ) {
39
+
40
+ drainReferenceQueue ();
41
+
42
+ } else {
36
43
37
- protected final void drainReferenceQueue () {
38
- while (true ) {
39
- @ SuppressWarnings ("unchecked" )
40
- final R reference = (R ) processingQueue .poll ();
44
+ /*
45
+ * We can't create a new thread while the context is initializing or finalizing, as
46
+ * the polyglot API locks on creating new threads, and some core loading does things
47
+ * such as stat files which could allocate memory that is marked to be automatically
48
+ * freed and so would want to start the finalization thread. So don't start the
49
+ * finalization thread if we are initializing. We will rely on some other finalizer
50
+ * to be created to ever free this memory allocated during startup, but that's a
51
+ * reasonable assumption and a low risk of leaking a tiny number of bytes if it
52
+ * doesn't hold.
53
+ */
54
+
55
+ if (processingThread == null && !context .isPreInitializing () && context .isInitialized () && !context .isFinalizing ()) {
56
+ createProcessingThread ();
57
+ }
41
58
42
- if (reference == null ) {
43
- break ;
44
59
}
60
+ }
61
+
62
+ protected void createProcessingThread () {
63
+ final ThreadManager threadManager = context .getThreadManager ();
64
+ processingThread = threadManager .createBootThread (threadName ());
65
+ context .send (processingThread , "internal_thread_initialize" );
45
66
46
- processReference (reference );
67
+ threadManager .initialize (processingThread , null , threadName (), () -> {
68
+ while (true ) {
69
+ final ProcessingReference <?> reference = (ProcessingReference <?>) threadManager .runUntilResult (null , processingQueue ::remove );
70
+
71
+ reference .service ().processReference (reference );
72
+ }
73
+ });
47
74
}
48
- }
49
75
50
- protected void createProcessingThread () {
51
- final ThreadManager threadManager = context .getThreadManager ();
52
- processingThread = threadManager .createBootThread (getThreadName ());
53
- context .send (processingThread , "internal_thread_initialize" );
76
+ protected final String threadName () {
77
+ return "Ruby-reference-processor" ;
78
+ }
54
79
55
- threadManager . initialize ( processingThread , null , getThreadName (), () -> {
80
+ protected final void drainReferenceQueue () {
56
81
while (true ) {
57
82
@ SuppressWarnings ("unchecked" )
58
- final R reference = (R ) threadManager .runUntilResult (null , processingQueue ::remove );
83
+ final ProcessingReference <?> reference = (ProcessingReference <?>) processingQueue .poll ();
84
+
85
+ if (reference == null ) {
86
+ break ;
87
+ }
59
88
60
- processReference (reference );
89
+ reference . service (). processReference (reference );
61
90
}
62
- });
91
+ }
92
+
63
93
}
64
94
65
- protected void processReference (R reference ) {
66
- remove (reference );
95
+ /**
96
+ * The head of a doubly-linked list of FinalizerReference, needed to collect finalizer Procs for
97
+ * ObjectSpace.
98
+ */
99
+ private R first = null ;
100
+
101
+ protected final ReferenceProcessor referenceProcessor ;
102
+ protected final RubyContext context ;
103
+
104
+ public ReferenceProcessingService (RubyContext context , ReferenceProcessor referenceProcessor ) {
105
+ this .context = context ;
106
+ this .referenceProcessor = referenceProcessor ;
107
+ }
108
+
109
+ @ SuppressWarnings ("unchecked" )
110
+ protected void processReference (ProcessingReference <?> reference ) {
111
+ remove ((R ) reference );
67
112
}
68
113
69
114
protected void runCatchingErrors (Consumer <R > action , R reference ) {
@@ -81,32 +126,6 @@ protected void runCatchingErrors(Consumer<R> action, R reference) {
81
126
}
82
127
}
83
128
84
- protected abstract String getThreadName ();
85
-
86
- protected void processReferenceQueue () {
87
- if (context .getOptions ().SINGLE_THREADED ) {
88
-
89
- drainReferenceQueue ();
90
-
91
- } else {
92
-
93
- /*
94
- * We can't create a new thread while the context is initializing or finalizing, as the
95
- * polyglot API locks on creating new threads, and some core loading does things such as
96
- * stat files which could allocate memory that is marked to be automatically freed and
97
- * so would want to start the finalization thread. So don't start the finalization
98
- * thread if we are initializing. We will rely on some other finalizer to be created to
99
- * ever free this memory allocated during startup, but that's a reasonable assumption
100
- * and a low risk of leaking a tiny number of bytes if it doesn't hold.
101
- */
102
-
103
- if (processingThread == null && !context .isPreInitializing () && context .isInitialized () && !context .isFinalizing ()) {
104
- createProcessingThread ();
105
- }
106
-
107
- }
108
- }
109
-
110
129
protected synchronized void remove (R ref ) {
111
130
if (ref .getNext () == ref ) {
112
131
// Already removed.
0 commit comments