22
22
import org .truffleruby .builtins .PrimitiveArrayArgumentsNode ;
23
23
import org .truffleruby .core .thread .GetCurrentRubyThreadNode ;
24
24
import org .truffleruby .core .thread .ThreadManager ;
25
- import org .truffleruby .core .thread .ThreadManager .BlockingAction ;
26
25
import org .truffleruby .core .thread .ThreadStatus ;
27
26
import org .truffleruby .language .Visibility ;
28
27
import org .truffleruby .language .objects .AllocateObjectNode ;
@@ -44,8 +43,12 @@ public abstract static class AllocateNode extends CoreMethodArrayArgumentsNode {
44
43
45
44
@ Specialization
46
45
protected DynamicObject allocate (DynamicObject rubyClass ) {
47
- ReentrantLock lock = new ReentrantLock ();
48
- return allocateNode .allocate (rubyClass , lock , lock .newCondition (), 0 , 0 );
46
+ // condLock is only held for a short number of non-blocking instructions,
47
+ // so there is no need to poll for safepoints while locking it.
48
+ // It is an internal lock and so locking should be done with condLock.lock()
49
+ // to avoid changing the Ruby Thread status and consume Java thread interrupts.
50
+ final ReentrantLock condLock = new ReentrantLock ();
51
+ return allocateNode .allocate (rubyClass , condLock , condLock .newCondition (), 0 , 0 );
49
52
}
50
53
51
54
}
@@ -95,29 +98,34 @@ private void waitInternal(DynamicObject conditionVariable, ReentrantLock mutexLo
95
98
// it should only be considered if we are inside Mutex#sleep when Thread#{run,wakeup} is called.
96
99
Layouts .THREAD .getWakeUp (thread ).set (false );
97
100
98
- // condLock must be locked before unlocking mutexLock, to avoid losing potential signals
99
- getContext ().getThreadManager ().runUntilResult (this , () -> {
100
- condLock .lockInterruptibly ();
101
- return BlockingAction .SUCCESS ;
102
- });
103
- mutexLock .unlock ();
104
-
105
- Layouts .CONDITION_VARIABLE
106
- .setWaiters (conditionVariable , Layouts .CONDITION_VARIABLE .getWaiters (conditionVariable ) + 1 );
101
+ // condLock must be locked before unlocking mutexLock, to avoid losing potential signals.
102
+ // We must not change the Ruby Thread status and not consume a Java thread interrupt while locking condLock.
103
+ // If there is an interrupt, it should be consumed by condition.await() and the Ruby Thread sleep status
104
+ // must imply being ready to be interrupted by Thread#{run,wakeup}.
105
+ condLock .lock ();
107
106
try {
108
- awaitSignal (conditionVariable , thread , durationInNanos , condLock , condition , endNanoTime );
109
- } catch (Error | RuntimeException e ) {
110
- /*
111
- * Consume a signal if one was waiting. We do this because the error may have
112
- * occurred while we were waiting, or at some point after exiting a safepoint that
113
- * throws an exception and another thread has attempted to signal us. It is valid
114
- * for us to consume this signal because we are still marked as waiting for it.
115
- */
116
- consumeSignal (conditionVariable );
117
- throw e ;
107
+ mutexLock .unlock ();
108
+
109
+ Layouts .CONDITION_VARIABLE .setWaiters (
110
+ conditionVariable ,
111
+ Layouts .CONDITION_VARIABLE .getWaiters (conditionVariable ) + 1 );
112
+ try {
113
+ awaitSignal (conditionVariable , thread , durationInNanos , condLock , condition , endNanoTime );
114
+ } catch (Error | RuntimeException e ) {
115
+ /*
116
+ * Consume a signal if one was waiting. We do this because the error may have
117
+ * occurred while we were waiting, or at some point after exiting a safepoint that
118
+ * throws an exception and another thread has attempted to signal us. It is valid
119
+ * for us to consume this signal because we are still marked as waiting for it.
120
+ */
121
+ consumeSignal (conditionVariable );
122
+ throw e ;
123
+ } finally {
124
+ Layouts .CONDITION_VARIABLE .setWaiters (
125
+ conditionVariable ,
126
+ Layouts .CONDITION_VARIABLE .getWaiters (conditionVariable ) - 1 );
127
+ }
118
128
} finally {
119
- Layouts .CONDITION_VARIABLE
120
- .setWaiters (conditionVariable , Layouts .CONDITION_VARIABLE .getWaiters (conditionVariable ) - 1 );
121
129
condLock .unlock ();
122
130
MutexOperations .internalLockEvenWithException (mutexLock , this , getContext ());
123
131
}
@@ -205,13 +213,7 @@ protected DynamicObject signal(DynamicObject self) {
205
213
final ReentrantLock condLock = Layouts .CONDITION_VARIABLE .getLock (self );
206
214
final Condition condition = Layouts .CONDITION_VARIABLE .getCondition (self );
207
215
208
- if (!condLock .tryLock ()) {
209
- getContext ().getThreadManager ().runUntilResult (this , () -> {
210
- condLock .lockInterruptibly ();
211
- return BlockingAction .SUCCESS ;
212
- });
213
- }
214
-
216
+ condLock .lock ();
215
217
try {
216
218
if (Layouts .CONDITION_VARIABLE .getWaiters (self ) > 0 ) {
217
219
Layouts .CONDITION_VARIABLE .setSignals (self , Layouts .CONDITION_VARIABLE .getSignals (self ) + 1 );
@@ -234,13 +236,7 @@ protected DynamicObject broadcast(DynamicObject self) {
234
236
final ReentrantLock condLock = Layouts .CONDITION_VARIABLE .getLock (self );
235
237
final Condition condition = Layouts .CONDITION_VARIABLE .getCondition (self );
236
238
237
- if (!condLock .tryLock ()) {
238
- getContext ().getThreadManager ().runUntilResult (this , () -> {
239
- condLock .lockInterruptibly ();
240
- return BlockingAction .SUCCESS ;
241
- });
242
- }
243
-
239
+ condLock .lock ();
244
240
try {
245
241
if (Layouts .CONDITION_VARIABLE .getWaiters (self ) > 0 ) {
246
242
Layouts .CONDITION_VARIABLE .setSignals (
0 commit comments