Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: jruby/jruby
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: 06be1c005ce0
Choose a base ref
...
head repository: jruby/jruby
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: 2956b446d5aa
Choose a head ref
  • 3 commits
  • 6 files changed
  • 1 contributor

Commits on Mar 12, 2015

  1. Verified

    This commit was signed with the committer’s verified signature.
    headius Charles Oliver Nutter
    Copy the full SHA
    262f6a9 View commit details
  2. [Truffle] Add ConditionVariable, Queue and SizedQueue from MRI thread…

    ….rb.
    
    * ConditionVariable's initialization of a Mutex in an instance variable might prove problematic.
    eregon committed Mar 12, 2015
    Copy the full SHA
    fd6c0e2 View commit details
  3. Copy the full SHA
    2956b44 View commit details
372 changes: 372 additions & 0 deletions lib/ruby/truffle/mri/thread.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,372 @@
# Truffle: Last version of lib/thread.rb in MRI @ r42801 (324df61e).
# Truffle: we shim Thread.handle_interrupt as we do not support it yet.
def Thread.handle_interrupt(h)
yield
end

#
# thread.rb - thread support classes
# by Yukihiro Matsumoto <matz@netlab.co.jp>
#
# Copyright (C) 2001 Yukihiro Matsumoto
# Copyright (C) 2000 Network Applied Communication Laboratory, Inc.
# Copyright (C) 2000 Information-technology Promotion Agency, Japan
#

unless defined? Thread
raise "Thread not available for this ruby interpreter"
end

unless defined? ThreadError
class ThreadError < StandardError
end
end

if $DEBUG
Thread.abort_on_exception = true
end

#
# 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
@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

#
# This class provides a way to synchronize communication between threads.
#
# Example:
#
# require 'thread'
#
# queue = Queue.new
#
# producer = Thread.new do
# 5.times do |i|
# sleep rand(i) # simulate expense
# queue << i
# puts "#{i} produced"
# end
# end
#
# consumer = Thread.new do
# 5.times do |i|
# value = queue.pop
# sleep rand(i/2) # simulate expense
# puts "consumed #{value}"
# end
# end
#
# consumer.join
#
class Queue
#
# Creates a new queue.
#
def initialize
@que = []
@que.taint # enable tainted communication
@num_waiting = 0
self.taint
@mutex = Mutex.new
@cond = ConditionVariable.new
end

#
# Pushes +obj+ to the queue.
#
def push(obj)
Thread.handle_interrupt(StandardError => :on_blocking) do
@mutex.synchronize do
@que.push obj
@cond.signal
end
self
end
end

#
# Alias of push
#
alias << push

#
# Alias of push
#
alias enq push

#
# Retrieves data from the queue. If the queue is empty, the calling thread is
# suspended until data is pushed onto the queue. If +non_block+ is true, the
# thread isn't suspended, and an exception is raised.
#
def pop(non_block=false)
Thread.handle_interrupt(StandardError => :on_blocking) do
@mutex.synchronize do
while true
if @que.empty?
if non_block
raise ThreadError, "queue empty"
else
begin
@num_waiting += 1
@cond.wait @mutex
ensure
@num_waiting -= 1
end
end
else
return @que.shift
end
end
end
end
end

#
# Alias of pop
#
alias shift pop

#
# Alias of pop
#
alias deq pop

#
# Returns +true+ if the queue is empty.
#
def empty?
@que.empty?
end

#
# Removes all objects from the queue.
#
def clear
@que.clear
self
end

#
# Returns the length of the queue.
#
def length
@que.length
end

#
# Alias of length.
#
alias size length

#
# Returns the number of threads waiting on the queue.
#
def num_waiting
@num_waiting
end
end

#
# This class represents queues of specified size capacity. The push operation
# may be blocked if the capacity is full.
#
# See Queue for an example of how a SizedQueue works.
#
class SizedQueue < Queue
#
# Creates a fixed-length queue with a maximum size of +max+.
#
def initialize(max)
raise ArgumentError, "queue size must be positive" unless max > 0
@max = max
@enque_cond = ConditionVariable.new
@num_enqueue_waiting = 0
super()
end

#
# Returns the maximum size of the queue.
#
def max
@max
end

#
# Sets the maximum size of the queue.
#
def max=(max)
raise ArgumentError, "queue size must be positive" unless max > 0

@mutex.synchronize do
if max <= @max
@max = max
else
diff = max - @max
@max = max
diff.times do
@enque_cond.signal
end
end
end
max
end

#
# Pushes +obj+ to the queue. If there is no space left in the queue, waits
# until space becomes available.
#
def push(obj)
Thread.handle_interrupt(RuntimeError => :on_blocking) do
@mutex.synchronize do
while true
break if @que.length < @max
@num_enqueue_waiting += 1
begin
@enque_cond.wait @mutex
ensure
@num_enqueue_waiting -= 1
end
end

@que.push obj
@cond.signal
end
self
end
end

#
# Alias of push
#
alias << push

#
# Alias of push
#
alias enq push

#
# Retrieves data from the queue and runs a waiting thread, if any.
#
def pop(*args)
retval = super
@mutex.synchronize do
if @que.length < @max
@enque_cond.signal
end
end
retval
end

#
# Alias of pop
#
alias shift pop

#
# Alias of pop
#
alias deq pop

#
# Returns the number of threads waiting on the queue.
#
def num_waiting
@num_waiting + @num_enqueue_waiting
end
end

# Documentation comments:
# - How do you make RDoc inherit documentation from superclass?
9 changes: 0 additions & 9 deletions lib/ruby/truffle/shims/thread.rb

This file was deleted.

10 changes: 10 additions & 0 deletions truffle/src/main/java/org/jruby/truffle/TruffleBridgeImpl.java
Original file line number Diff line number Diff line change
@@ -34,6 +34,7 @@
import org.jruby.util.cli.Options;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

@@ -142,6 +143,15 @@ public void init() {
// Shims
loadPath.slowPush(truffleContext.makeString(new File(home, "lib/ruby/truffle/shims").toString()));

// Load libraries required from the command line (-r LIBRARY)
for (String requiredLibrary : truffleContext.getRuntime().getInstanceConfig().getRequiredLibraries()) {
try {
truffleContext.getFeatureManager().require(requiredLibrary, null);
} catch (IOException e) {
throw new RuntimeException(e);
}
}

// Hook

if (truffleContext.getHooks() != null) {
Original file line number Diff line number Diff line change
@@ -20,7 +20,7 @@ public class OnceNode extends RubyNode {
@Child private RubyNode child;

// TODO(CS): need to always copy this with cloned nodes
private final AssumedValue<Object> valueMemo = new AssumedValue<>(null);
private final AssumedValue<Object> valueMemo = new AssumedValue<>("OnceNode", null);

public OnceNode(RubyContext context, SourceSection sourceSection, RubyNode child) {
super(context, sourceSection);
Original file line number Diff line number Diff line change
@@ -19,7 +19,7 @@ public class ArrayAllocationSite {
public static final boolean ARRAYS_OPTIMISTIC_LONG = Options.TRUFFLE_ARRAYS_OPTIMISTIC_LONG.load();

@CompilerDirectives.CompilationFinal private boolean convertedIntToLong = false;
private final Assumption assumption = Truffle.getRuntime().createAssumption();
private final Assumption assumption = Truffle.getRuntime().createAssumption("ArrayAllocationSite");

@CompilerDirectives.TruffleBoundary
public void convertedIntToLong() {
Original file line number Diff line number Diff line change
@@ -34,7 +34,7 @@ public class SafepointManager {

private final Set<Thread> runningThreads = Collections.newSetFromMap(new ConcurrentHashMap<Thread, Boolean>());

@CompilerDirectives.CompilationFinal private Assumption assumption = Truffle.getRuntime().createAssumption();
@CompilerDirectives.CompilationFinal private Assumption assumption = Truffle.getRuntime().createAssumption("SafepointManager");
private final ReentrantLock lock = new ReentrantLock();
private final Phaser phaser = new Phaser();
private volatile SafepointAction action;
@@ -110,7 +110,7 @@ private void step(Node currentNode, RubyThread thread, boolean isDrivingThread)
phaser.arriveAndAwaitAdvance();

if (isDrivingThread) {
assumption = Truffle.getRuntime().createAssumption();
assumption = Truffle.getRuntime().createAssumption("SafepointManager");
}

// wait the assumption to be renewed