|
10 | 10 | package org.truffleruby.extra;
|
11 | 11 |
|
12 | 12 | import com.oracle.truffle.api.dsl.Cached;
|
| 13 | +import com.oracle.truffle.api.object.DynamicObjectLibrary; |
13 | 14 | import org.jcodings.specific.UTF8Encoding;
|
| 15 | +import org.truffleruby.Layouts; |
14 | 16 | import org.truffleruby.RubyContext;
|
15 | 17 | import org.truffleruby.RubyLanguage;
|
16 | 18 | import org.truffleruby.builtins.CoreMethod;
|
17 | 19 | import org.truffleruby.builtins.CoreMethodArrayArgumentsNode;
|
18 | 20 | import org.truffleruby.builtins.CoreMethodNode;
|
19 | 21 | import org.truffleruby.builtins.CoreModule;
|
20 | 22 | import org.truffleruby.builtins.YieldingCoreMethodNode;
|
| 23 | +import org.truffleruby.core.mutex.MutexOperations; |
21 | 24 | import org.truffleruby.core.proc.RubyProc;
|
22 | 25 | import org.truffleruby.core.rope.CodeRange;
|
23 | 26 | import org.truffleruby.core.string.StringNodes;
|
|
29 | 32 | import com.oracle.truffle.api.dsl.Specialization;
|
30 | 33 | import org.truffleruby.language.RubyDynamicObject;
|
31 | 34 |
|
| 35 | +import java.util.concurrent.locks.ReentrantLock; |
| 36 | + |
32 | 37 | @CoreModule("TruffleRuby")
|
33 | 38 | public abstract class TruffleRubyNodes {
|
34 | 39 |
|
@@ -104,11 +109,39 @@ protected Object fullMemoryBarrier() {
|
104 | 109 | @CoreMethod(names = "synchronized", onSingleton = true, required = 1, needsBlock = true)
|
105 | 110 | public abstract static class SynchronizedNode extends YieldingCoreMethodNode {
|
106 | 111 |
|
107 |
| - // We must not allow to synchronize on boxed primitives. |
| 112 | + /** We must not allow to synchronize on boxed primitives as that would be misleading. We use a ReentrantLock and |
| 113 | + * not simply Java's {@code synchronized} here as we need to be able to interrupt for guest safepoints and it is |
| 114 | + * not possible to interrupt Java's {@code synchronized (object) {}}. */ |
108 | 115 | @Specialization
|
109 | 116 | protected Object synchronize(RubyDynamicObject object, RubyProc block) {
|
110 |
| - synchronized (object) { |
| 117 | + final ReentrantLock lock = getLockAndLock(object); |
| 118 | + try { |
111 | 119 | return yield(block);
|
| 120 | + } finally { |
| 121 | + MutexOperations.unlockInternal(lock); |
| 122 | + } |
| 123 | + } |
| 124 | + |
| 125 | + @TruffleBoundary |
| 126 | + private ReentrantLock getLockAndLock(RubyDynamicObject object) { |
| 127 | + final ReentrantLock lock = getLock(object); |
| 128 | + MutexOperations.lockInternal(getContext(), lock, this); |
| 129 | + return lock; |
| 130 | + } |
| 131 | + |
| 132 | + @TruffleBoundary |
| 133 | + private ReentrantLock getLock(RubyDynamicObject object) { |
| 134 | + final DynamicObjectLibrary objectLibrary = DynamicObjectLibrary.getUncached(); |
| 135 | + |
| 136 | + synchronized (object) { |
| 137 | + ReentrantLock lock = (ReentrantLock) objectLibrary.getOrDefault(object, Layouts.OBJECT_LOCK, null); |
| 138 | + if (lock != null) { |
| 139 | + return lock; |
| 140 | + } else { |
| 141 | + lock = new ReentrantLock(); |
| 142 | + objectLibrary.put(object, Layouts.OBJECT_LOCK, lock); |
| 143 | + return lock; |
| 144 | + } |
112 | 145 | }
|
113 | 146 | }
|
114 | 147 |
|
|
0 commit comments