Skip to content

Commit

Permalink
[Truffle] Put ConditionVariable back in Ruby.
Browse files Browse the repository at this point in the history
* Using Object#wait() or Condition#await() has the problem that
  it can only be interrupted by Thread#interrupt() if it can acquire
  the monitor back. Otherwise, it stays stuck and in the case
  we are trying to get to a guest-language safepoint, deadlocks.
  The problem comes from the fact that the monitor must be hold while
  invoking the safepoint as we need to prevent another thread to notify() while
  we are interrupted and not in wait(). Since the monitor can be held
  during the safepoint, then interrupts sent by the safepoint are not unblocking
  threads waiting on the same condition with wait(), as they would need to acquire
  the lock that only one of the thread that got unblocked holds.
* The logic in Ruby is clearer and relies on Mutex#sleep and Thread#wakeup,
  which are simpler primitives and easier to implement.
  • Loading branch information
eregon committed Dec 1, 2016
1 parent 310801f commit a663ee3
Show file tree
Hide file tree
Showing 6 changed files with 100 additions and 220 deletions.
101 changes: 100 additions & 1 deletion lib/ruby/truffle/truffle/thread.rb
Expand Up @@ -22,4 +22,103 @@ class ThreadError < StandardError
Thread.abort_on_exception = true
end

# Truffle: ConditionVariable, Queue and SizedQueue are defined in Java
#
# ConditionVariable objects augment class Mutex. Using condition variables,
# it is possible to suspend while in the middle of a critical section until a
# resource becomes available.
#
# Example:
#
# require 'thread'
#
# mutex = Mutex.new
# resource = ConditionVariable.new
#
# a = Thread.new {
# mutex.synchronize {
# # Thread 'a' now needs the resource
# resource.wait(mutex)
# # 'a' can now have the resource
# }
# }
#
# b = Thread.new {
# mutex.synchronize {
# # Thread 'b' has finished using the resource
# resource.signal
# }
# }
#
class ConditionVariable
#
# Creates a new ConditionVariable
#
def initialize
@waiters = {}
@waiters_mutex = Mutex.new
end

#
# Releases the lock held in +mutex+ and waits; reacquires the lock on wakeup.
#
# If +timeout+ is given, this method returns after +timeout+ seconds passed,
# even if no other thread has signaled.
#
def wait(mutex, timeout=nil)
Thread.handle_interrupt(StandardError => :never) do
begin
Thread.handle_interrupt(StandardError => :on_blocking) do
@waiters_mutex.synchronize do
@waiters[Thread.current] = true
end
mutex.sleep timeout
end
ensure
@waiters_mutex.synchronize do
# Truffle: this Hash must never need a guest-language safepoint,
# or threads will get struck while acquiring the Mutex in
# interrupt-never mode.
@waiters.delete(Thread.current)
end
end
end
self
end

#
# Wakes up the first thread in line waiting for this lock.
#
def signal
Thread.handle_interrupt(StandardError => :on_blocking) do
begin
t, _ = @waiters_mutex.synchronize { @waiters.shift }
t.run if t
rescue ThreadError
retry # t was already dead?
end
end
self
end

#
# Wakes up all threads waiting for this lock.
#
def broadcast
Thread.handle_interrupt(StandardError => :on_blocking) do
threads = nil
@waiters_mutex.synchronize do
threads = @waiters.keys
@waiters.clear
end
for t in threads
begin
t.run
rescue ThreadError
end
end
end
self
end
end

# Truffle: Queue and SizedQueue are defined in Java
3 changes: 0 additions & 3 deletions truffle/src/main/java/org/jruby/truffle/Layouts.java
Expand Up @@ -44,8 +44,6 @@
import org.jruby.truffle.core.method.UnboundMethodLayoutImpl;
import org.jruby.truffle.core.module.ModuleLayout;
import org.jruby.truffle.core.module.ModuleLayoutImpl;
import org.jruby.truffle.core.mutex.ConditionVariableLayout;
import org.jruby.truffle.core.mutex.ConditionVariableLayoutImpl;
import org.jruby.truffle.core.mutex.MutexLayout;
import org.jruby.truffle.core.mutex.MutexLayoutImpl;
import org.jruby.truffle.core.numeric.BignumLayout;
Expand Down Expand Up @@ -112,7 +110,6 @@ public abstract class Layouts {
public static final BindingLayout BINDING = BindingLayoutImpl.INSTANCE;
public static final ByteArrayLayout BYTE_ARRAY = ByteArrayLayoutImpl.INSTANCE;
public static final ClassLayout CLASS = ClassLayoutImpl.INSTANCE;
public static final ConditionVariableLayout CONDITION_VARIABLE = ConditionVariableLayoutImpl.INSTANCE;
public static final DirLayout DIR = DirLayoutImpl.INSTANCE;
public static final EncodingConverterLayout ENCODING_CONVERTER = EncodingConverterLayoutImpl.INSTANCE;
public static final EncodingLayout ENCODING = EncodingLayoutImpl.INSTANCE;
Expand Down
4 changes: 0 additions & 4 deletions truffle/src/main/java/org/jruby/truffle/core/CoreLibrary.java
Expand Up @@ -66,7 +66,6 @@
import org.jruby.truffle.core.method.UnboundMethodNodesFactory;
import org.jruby.truffle.core.module.ModuleNodes;
import org.jruby.truffle.core.module.ModuleNodesFactory;
import org.jruby.truffle.core.mutex.ConditionVariableNodesFactory;
import org.jruby.truffle.core.mutex.MutexNodesFactory;
import org.jruby.truffle.core.numeric.BignumNodesFactory;
import org.jruby.truffle.core.numeric.FixnumNodesFactory;
Expand Down Expand Up @@ -491,8 +490,6 @@ public CoreLibrary(RubyContext context) {
bindingClass = defineClass("Binding");
bindingFactory = Layouts.BINDING.createBindingShape(bindingClass, bindingClass);
Layouts.CLASS.setInstanceFactoryUnsafe(bindingClass, bindingFactory);
final DynamicObject conditionVariableClass = defineClass("ConditionVariable");
Layouts.CLASS.setInstanceFactoryUnsafe(conditionVariableClass, Layouts.CONDITION_VARIABLE.createConditionVariableShape(conditionVariableClass, conditionVariableClass));
dirClass = defineClass("Dir");
Layouts.CLASS.setInstanceFactoryUnsafe(dirClass, Layouts.DIR.createDirShape(dirClass, dirClass));
encodingClass = defineClass("Encoding");
Expand Down Expand Up @@ -712,7 +709,6 @@ public void addCoreMethods(PrimitiveManager primitiveManager) {
ByteArrayNodesFactory.getFactories(),
CExtNodesFactory.getFactories(),
ClassNodesFactory.getFactories(),
ConditionVariableNodesFactory.getFactories(),
CoverageNodesFactory.getFactories(),
DigestNodesFactory.getFactories(),
DirNodesFactory.getFactories(),
Expand Down

This file was deleted.

This file was deleted.

This file was deleted.

0 comments on commit a663ee3

Please sign in to comment.