Skip to content

Commit

Permalink
Reworked starting and operating on Threads.
Browse files Browse the repository at this point in the history
  • Loading branch information
brixen committed Mar 19, 2016
1 parent bcd14db commit 9524ae1
Show file tree
Hide file tree
Showing 5 changed files with 154 additions and 330 deletions.
191 changes: 41 additions & 150 deletions core/thread.rb
Expand Up @@ -6,30 +6,54 @@
class Thread
attr_reader :recursive_objects
attr_reader :pid
attr_reader :exception

def self.start(*args)
raise ArgumentError.new("no block passed to Thread.start") unless block_given?
def self.new(*args, &block)
thr = Rubinius.invoke_primitive :thread_s_new, args, block, self

thr = Rubinius.invoke_primitive :thread_allocate, self

Rubinius.asm(args, thr) do |args, obj|
run obj
dup
Rubinius::VariableScope.of_sender.locked!

run args
push_block
send_with_splat :__thread_initialize__, 0, true
# no pop here, as .asm blocks imply a pop as they're not
# allowed to leak a stack value
unless thr.send :initialized?
raise ThreadError, "Thread#initialize not called"
end

return thr
end

def self.start(*args, &block)
raise ArgumentError.new("no block passed to Thread.start") unless block

Rubinius.invoke_primitive :thread_s_start, args, block, self
end

class << self
alias_method :fork, :start
end

def initialize(*args, &block)
unless block
Kernel.raise ThreadError, "no block passed to Thread#initialize"
end

if @initialized
Kernel.raise ThreadError, "already initialized thread"
end

@args = args
@block = block
@initialized = true

Thread.current.group.add self
end

alias_method :__thread_initialize__, :initialize

def initialized?
@initialized
end

private :initialized?

def self.current
Rubinius.primitive :thread_current
Kernel.raise PrimitiveFailure, "Thread.current primitive failed"
Expand All @@ -54,21 +78,6 @@ def self.stop
nil
end

def fork
Rubinius.primitive :thread_fork
Kernel.raise ThreadError, "Thread#fork failed, thread already started or dead"
end

def raise_prim(exc)
Rubinius.primitive :thread_raise
Kernel.raise PrimitiveFailure, "Thread#raise primitive failed"
end

def kill_prim
Rubinius.primitive :thread_kill
Kernel.raise PrimitiveFailure, "Thread#kill primitive failed"
end

def wakeup
Rubinius.primitive :thread_wakeup
Kernel.raise ThreadError, "Thread#wakeup primitive failed, thread may be dead"
Expand All @@ -95,21 +104,6 @@ def mri_backtrace
Kernel.raise PrimitiveFailure, "Thread#mri_backtrace primitive failed"
end

def unlock_locks
Rubinius.primitive :thread_unlock_locks
Kernel.raise PrimitiveFailure, "Thread#unlock_locks primitive failed"
end

def current_exception
Rubinius.primitive :thread_current_exception
Kernel.raise PrimitiveFailure, "Thread#current_exception primitive failed"
end

def set_exception(exception)
Rubinius.primitive :thread_set_exception
Kernel.raise PrimitiveFailure, "Thread#set_exception primitive failed"
end

@abort_on_exception = false

def self.abort_on_exception
Expand Down Expand Up @@ -138,50 +132,6 @@ def inspect

alias_method :to_s, :inspect

def self.new(*args)
thr = Rubinius.invoke_primitive :thread_allocate, self

Rubinius::VariableScope.of_sender.locked!

Rubinius.asm(args, thr) do |args, obj|
run obj
dup

run args
push_block
send_with_splat :initialize, 0, true
# no pop here, as .asm blocks imply a pop as they're not
# allowed to leak a stack value
end

unless thr.thread_is_setup?
raise ThreadError, "Thread#initialize not called"
end

return thr
end

def initialize(*args, &block)
unless block
Kernel.raise ThreadError, "no block passed to Thread#initialize"
end

@args = args
@block = block

th_group = Thread.current.group

th_group.add self

fork
end

alias_method :__thread_initialize__, :initialize

def thread_is_setup?
@block != nil
end

def alive?
Rubinius.synchronize(self) do
@alive
Expand Down Expand Up @@ -233,14 +183,9 @@ def group
end

def raise(exc=undefined, msg=nil, trace=nil)
Rubinius.lock(self)

unless @alive
Rubinius.unlock(self)
return self
end
Rubinius.synchronize(self) do
return self unless @alive

begin
if undefined.equal? exc
no_argument = true
exc = active_exception
Expand All @@ -265,13 +210,10 @@ def raise(exc=undefined, msg=nil, trace=nil)
if self == Thread.current
Kernel.raise exc
else
raise_prim exc
Rubinius.invoke_primitive :thread_raise, self, exc
end
ensure
Rubinius.unlock(self)
end
end
private :raise_prim

def [](key)
locals_aref(Rubinius::Type.coerce_to_symbol(key))
Expand Down Expand Up @@ -355,61 +297,10 @@ def self.kill(thread)

alias_method :run, :wakeup

# Called by Thread#fork in the new thread
#
def __run__
begin
begin
Rubinius.unlock(self)
@result = @block.call(*@args)
ensure
begin
# We must lock self in a careful way.
#
# At this point, it's possible that an other thread does Thread#raise
# and then our execution is interrupted AT ANY GIVEN TIME. We
# absolutely must make sure to lock self as soon as possible to lock
# out interrupts from other threads.
#
# Rubinius.uninterrupted_lock(self) just does that.
#
# Notice that this can't be moved to other methods and there should be
# no preceding code before it in the enclosing ensure clause.
# These are to prevent any interrupted lock failures.
Rubinius.uninterrupted_lock(self)

# Now, we locked self. No other thread can interrupt this thread
# anymore.
# If there is any not-triggered interrupt, check and process it. In
# either case, we jump to the following ensure clause.
Rubinius.check_interrupts
ensure
unlock_locks
end
end
rescue Exception => e
set_exception e

STDERR.puts "Exception in thread: #{e.message} (#{e.class})" if $DEBUG

if abort_on_exception or Thread.abort_on_exception
Thread.main.raise e
end
ensure
Rubinius::Mirror.reflect(@group).remove self

if Rubinius.thread_state[0] == :thread_kill
@killed = true
end

Rubinius.unlock(self)
end
end

def kill
@sleep = false
Rubinius.synchronize(self) do
kill_prim
Rubinius.invoke_primitive :thread_kill, self
end
self
end
Expand All @@ -419,7 +310,7 @@ def kill

def value
join
@killed ? nil : @result
@value
end

def active_exception
Expand Down
14 changes: 14 additions & 0 deletions core/thread_mirror.rb
Expand Up @@ -4,6 +4,20 @@ class Thread < Mirror
def group=(group)
Rubinius.invoke_primitive :object_set_ivar, @object, :@group, group
end

def finish
Rubinius::Mirror.reflect(@object.group).remove @object

if exception = @object.exception
if $DEBUG
STDERR.puts "Exception in thread: #{exception.message} (#{exception.class})"
end

if @object.abort_on_exception or Thread.abort_on_exception
Thread.main.raise exception
end
end
end
end
end
end

0 comments on commit 9524ae1

Please sign in to comment.