Skip to content

Commit 4574e95

Browse files
committed
[GR-18163] Use a ReentrantLock for TruffleRuby.synchronized
PullRequest: truffleruby/2479
2 parents a6a2174 + 6690899 commit 4574e95

File tree

7 files changed

+39
-54
lines changed

7 files changed

+39
-54
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ Bug fixes:
1919
* Fix `Enumerator::Lazy#{chunk_while, slice_before, slice_after, slice_when}` to return instances of `Enumerator::Lazy` (#2273).
2020
* Fix `Truffle::Interop.source_location` to return unavailable source sections for modules instead of null (#2257).
2121
* Fix usage of `Thread.handle_interrupt` in `MonitorMixin#mon_synchronize`.
22+
* Fixed `TruffleRuby.synchronized` to handle guest safepoints (#2277).
2223

2324
Compatibility:
2425

@@ -78,6 +79,7 @@ Changes:
7879

7980
* Standalone builds of TruffleRuby are now based on JDK11 (they used JDK8 previously). There should be no user-visible changes. Similarly, JDK11 is now used by default in development instead of JDK8.
8081
* The deprecated `Truffle::System.synchronized` has been removed.
82+
* `Java.synchronized` has been removed, it did not work on host objects.
8183

8284
# 21.0.0
8385

doc/user/polyglot.md

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -128,8 +128,6 @@ To access instance methods use `.class`, such as `MyClass.class.getName`.
128128

129129
To import a Java class as a top-level constant, use `Java.import 'name'`.
130130

131-
`Java.synchronized(object) { }` will use the Java object's monitor to synchronize.
132-
133131
## Embedding in Java
134132

135133
TruffleRuby is embedded via the Polyglot API, which is part of GraalVM.

lib/truffle/timeout.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@ def self.ensure_timeout_thread_running
131131
def self.add_timeout(time, exc, message)
132132
r = TimeoutRequest.new(time, Thread.current, exc, message)
133133
@chan << r
134-
ensure_timeout_thread_running
134+
ensure_timeout_thread_running unless defined?(@controller)
135135
r
136136
end
137137

spec/truffle/interop/java_synchronized_spec.rb

Lines changed: 0 additions & 43 deletions
This file was deleted.

src/main/java/org/truffleruby/Layouts.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ public abstract class Layouts {
1919

2020
public static final HiddenKey OBJECT_ID_IDENTIFIER = new HiddenKey("object_id"); // long
2121
public static final HiddenKey FROZEN_IDENTIFIER = new HiddenKey("frozen?"); // boolean
22+
public static final HiddenKey OBJECT_LOCK = new HiddenKey("object_lock"); // ReentrantLock
2223
public static final HiddenKey ASSOCIATED_IDENTIFIER = new HiddenKey("associated"); // Pointer[]
2324
public static final HiddenKey FINALIZER_REF_IDENTIFIER = new HiddenKey("finalizerRef"); // FinalizerReference
2425
public static final HiddenKey MARKED_OBJECTS_IDENTIFIER = new HiddenKey("marked_objects"); // Object[]

src/main/java/org/truffleruby/extra/TruffleRubyNodes.java

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,17 @@
1010
package org.truffleruby.extra;
1111

1212
import com.oracle.truffle.api.dsl.Cached;
13+
import com.oracle.truffle.api.object.DynamicObjectLibrary;
1314
import org.jcodings.specific.UTF8Encoding;
15+
import org.truffleruby.Layouts;
1416
import org.truffleruby.RubyContext;
1517
import org.truffleruby.RubyLanguage;
1618
import org.truffleruby.builtins.CoreMethod;
1719
import org.truffleruby.builtins.CoreMethodArrayArgumentsNode;
1820
import org.truffleruby.builtins.CoreMethodNode;
1921
import org.truffleruby.builtins.CoreModule;
2022
import org.truffleruby.builtins.YieldingCoreMethodNode;
23+
import org.truffleruby.core.mutex.MutexOperations;
2124
import org.truffleruby.core.proc.RubyProc;
2225
import org.truffleruby.core.rope.CodeRange;
2326
import org.truffleruby.core.string.StringNodes;
@@ -29,6 +32,8 @@
2932
import com.oracle.truffle.api.dsl.Specialization;
3033
import org.truffleruby.language.RubyDynamicObject;
3134

35+
import java.util.concurrent.locks.ReentrantLock;
36+
3237
@CoreModule("TruffleRuby")
3338
public abstract class TruffleRubyNodes {
3439

@@ -104,11 +109,39 @@ protected Object fullMemoryBarrier() {
104109
@CoreMethod(names = "synchronized", onSingleton = true, required = 1, needsBlock = true)
105110
public abstract static class SynchronizedNode extends YieldingCoreMethodNode {
106111

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) {}}. */
108115
@Specialization
109116
protected Object synchronize(RubyDynamicObject object, RubyProc block) {
110-
synchronized (object) {
117+
final ReentrantLock lock = getLockAndLock(object);
118+
try {
111119
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+
}
112145
}
113146
}
114147

src/main/ruby/truffleruby/core/truffle/polyglot.rb

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -58,12 +58,6 @@ def self.import(name)
5858
type
5959
end
6060

61-
def self.synchronized(object)
62-
TruffleRuby.synchronized(object) do
63-
yield
64-
end
65-
end
66-
6761
# test-unit expects `Java::JavaLang::Throwable` to be resolvable if `::Java` is defined (see assertions.rb).
6862
# When doing JRuby-style interop, that's a fine assumption. However, we have `::Java` defined for Truffle-style
6963
# interop and in that case, the assumption does not hold. In order to allow the gem to work properly, we define

0 commit comments

Comments
 (0)