25
25
26
26
import java .util .concurrent .ArrayBlockingQueue ;
27
27
import java .util .concurrent .BlockingQueue ;
28
+ import java .util .concurrent .Callable ;
29
+ import java .util .concurrent .LinkedBlockingQueue ;
28
30
29
31
/**
30
32
* Represents the Ruby {@code Fiber} class. The current implementation uses Java threads and message
@@ -80,67 +82,100 @@ public RubyException getException() {
80
82
}
81
83
82
84
public static class FiberExitException extends ControlFlowException {
83
-
84
85
private static final long serialVersionUID = 1522270454305076317L ;
85
-
86
86
}
87
87
88
88
private final FiberManager fiberManager ;
89
89
private final ThreadManager threadManager ;
90
90
private final RubyThread rubyThread ;
91
91
92
92
private String name ;
93
- private final boolean topLevel ;
94
- private final BlockingQueue <FiberMessage > messageQueue = new ArrayBlockingQueue <>(1 );
93
+ private final boolean isRootFiber ;
94
+ // we need 2 slots when the safepoint manager sends the kill message and there is another message unprocessed
95
+ private final BlockingQueue <FiberMessage > messageQueue = new LinkedBlockingQueue <>(2 );
95
96
private RubyFiber lastResumedByFiber = null ;
96
97
private boolean alive = true ;
97
98
98
- public RubyFiber (RubyClass rubyClass , FiberManager fiberManager , ThreadManager threadManager , String name , boolean topLevel ) {
99
+ protected volatile Thread thread ;
100
+
101
+ public RubyFiber (RubyThread parent , RubyClass rubyClass , String name ) {
102
+ this (parent , parent .getFiberManager (), parent .getThreadManager (), rubyClass , name , false );
103
+ }
104
+
105
+ public static RubyFiber newRootFiber (RubyThread thread , FiberManager fiberManager , ThreadManager threadManager ) {
106
+ RubyContext context = thread .getContext ();
107
+ return new RubyFiber (thread , fiberManager , threadManager , context .getCoreLibrary ().getFiberClass (), "root Fiber for Thread" , true );
108
+ }
109
+
110
+ private RubyFiber (RubyThread parent , FiberManager fiberManager , ThreadManager threadManager , RubyClass rubyClass , String name , boolean isRootFiber ) {
99
111
super (rubyClass );
112
+ this .rubyThread = parent ;
100
113
this .fiberManager = fiberManager ;
101
114
this .threadManager = threadManager ;
102
115
this .name = name ;
103
- this .topLevel = topLevel ;
104
- this .rubyThread = threadManager .getCurrentThread ();
116
+ this .isRootFiber = isRootFiber ;
105
117
}
106
118
107
- public void initialize (RubyProc block ) {
119
+ public void initialize (final RubyProc block ) {
108
120
RubyNode .notDesignedForCompilation ();
109
121
110
122
name = "Ruby Fiber@" + block .getSharedMethodInfo ().getSourceSection ().getShortDescription ();
111
-
112
- final RubyFiber finalFiber = this ;
113
- final RubyProc finalBlock = block ;
114
-
115
123
final Thread thread = new Thread (new Runnable () {
116
-
117
124
@ Override
118
125
public void run () {
119
- fiberManager .registerFiber (finalFiber );
120
- finalFiber .getContext ().getSafepointManager ().enterThread ();
121
- threadManager .enterGlobalLock (rubyThread );
126
+ handleFiberExceptions (block );
127
+ }
128
+ });
129
+ thread .setName (name );
130
+ thread .start ();
131
+ }
122
132
133
+ private void handleFiberExceptions (final RubyProc block ) {
134
+ run (new Runnable () {
135
+ @ Override
136
+ public void run () {
123
137
try {
124
- final Object [] args = finalFiber .waitForResume ();
125
- final Object result = finalBlock .rootCall (args );
126
- finalFiber .resume (finalFiber .lastResumedByFiber , true , result );
127
- } catch (FiberExitException | ThreadExitException e ) { // TODO (eregon, 21 Apr. 2015): The thread should cleanly kill its fibers when dying.
128
- // Naturally exit the thread on catching this
138
+ final Object [] args = waitForResume ();
139
+ final Object result = block .rootCall (args );
140
+ resume (lastResumedByFiber , true , result );
141
+ } catch (FiberExitException e ) {
142
+ assert !isRootFiber ;
143
+ // Naturally exit the Java thread on catching this
129
144
} catch (ReturnException e ) {
130
- sendMessageTo (finalFiber . lastResumedByFiber , new FiberExceptionMessage (finalFiber . getContext ().getCoreLibrary ().unexpectedReturn (null )));
145
+ sendMessageTo (lastResumedByFiber , new FiberExceptionMessage (getContext ().getCoreLibrary ().unexpectedReturn (null )));
131
146
} catch (RaiseException e ) {
132
- sendMessageTo (finalFiber .lastResumedByFiber , new FiberExceptionMessage (e .getRubyException ()));
133
- } finally {
134
- alive = false ;
135
- threadManager .leaveGlobalLock ();
136
- finalFiber .getContext ().getSafepointManager ().leaveThread ();
137
- fiberManager .unregisterFiber (finalFiber );
147
+ sendMessageTo (lastResumedByFiber , new FiberExceptionMessage (e .getRubyException ()));
138
148
}
139
149
}
140
-
141
150
});
142
- thread .setName (name );
143
- thread .start ();
151
+ }
152
+
153
+ protected void run (final Runnable task ) {
154
+ RubyNode .notDesignedForCompilation ();
155
+
156
+ start ();
157
+ try {
158
+ task .run ();
159
+ } finally {
160
+ cleanup ();
161
+ }
162
+ }
163
+
164
+ // Only used by the main thread which cannot easily wrap everything inside a try/finally.
165
+ public void start () {
166
+ thread = Thread .currentThread ();
167
+ fiberManager .registerFiber (this );
168
+ getContext ().getSafepointManager ().enterThread ();
169
+ threadManager .enterGlobalLock (rubyThread );
170
+ }
171
+
172
+ // Only used by the main thread which cannot easily wrap everything inside a try/finally.
173
+ public void cleanup () {
174
+ alive = false ;
175
+ threadManager .leaveGlobalLock ();
176
+ getContext ().getSafepointManager ().leaveThread ();
177
+ fiberManager .unregisterFiber (this );
178
+ thread = null ;
144
179
}
145
180
146
181
public RubyThread getRubyThread () {
@@ -201,6 +236,7 @@ public Object[] transferControlTo(RubyFiber fiber, boolean yield, Object[] args)
201
236
}
202
237
203
238
public void shutdown () {
239
+ assert !isRootFiber ;
204
240
RubyNode .notDesignedForCompilation ();
205
241
206
242
sendMessageTo (this , new FiberExitMessage ());
@@ -216,8 +252,8 @@ public RubyFiber getLastResumedByFiber() {
216
252
return lastResumedByFiber ;
217
253
}
218
254
219
- public boolean isTopLevel () {
220
- return topLevel ;
255
+ public boolean isRootFiber () {
256
+ return isRootFiber ;
221
257
}
222
258
223
259
public String getName () {
@@ -228,7 +264,8 @@ public static class FiberAllocator implements Allocator {
228
264
229
265
@ Override
230
266
public RubyBasicObject allocate (RubyContext context , RubyClass rubyClass , Node currentNode ) {
231
- return new RubyFiber (rubyClass , context .getFiberManager (), context .getThreadManager (), null , false );
267
+ RubyThread parent = context .getThreadManager ().getCurrentThread ();
268
+ return new RubyFiber (parent , rubyClass , null );
232
269
}
233
270
234
271
}
0 commit comments