stdlib/thread.rb
# This shim implementation of Thread is meant to only appease code that tries
# to be safe in the presence of threads, but does not actually utilize them,
# e.g., uses thread- or fiber-local variables.
class ThreadError < StandardError
end
class Thread
def self.current
unless @current
@current = allocate
@current.core_initialize!
end
@current
end
def self.list
[current]
end
# Do not allow creation of new instances.
def initialize(*args)
raise NotImplementedError, 'Thread creation not available'
end
# fiber-local attribute access.
def [](key)
@fiber_locals[coerce_key_name(key)]
end
def []=(key, value)
@fiber_locals[coerce_key_name(key)] = value
end
def key?(key)
@fiber_locals.key?(coerce_key_name(key))
end
def keys
@fiber_locals.keys
end
# thread-local attribute access.
def thread_variable_get(key)
@thread_locals[coerce_key_name(key)]
end
def thread_variable_set(key, value)
@thread_locals[coerce_key_name(key)] = value
end
def thread_variable?(key)
@thread_locals.key?(coerce_key_name(key))
end
def thread_variables
@thread_locals.keys
end
private
def core_initialize!
@thread_locals = {}
@fiber_locals = {}
end
def coerce_key_name(key)
::Opal.coerce_to!(key, String, :to_s)
end
class Queue
def initialize
clear
end
def clear
@storage = []
end
def empty?
@storage.empty?
end
def size
@storage.size
end
def pop(non_block = false)
if empty?
raise ThreadError, 'Queue empty' if non_block
raise ThreadError, 'Deadlock'
end
@storage.shift
end
def push(value)
@storage.push(value)
end
def each(&block)
@storage.each(&block)
end
alias << push
alias deq pop
alias enq push
alias length size
alias shift pop
end
class Backtrace
class Location
def initialize(str)
@str = str
str =~ /^(.*?):(\d+):(\d+):in `(.*?)'$/
@path = Regexp.last_match(1)
@label = Regexp.last_match(4)
@lineno = Regexp.last_match(2).to_i
@label =~ /(\w+)$/
@base_label = Regexp.last_match(1) || @label
end
def to_s
@str
end
def inspect
@str.inspect
end
attr_reader :base_label, :label, :lineno, :path
# TODO: Make it somehow provide the absolute path.
alias absolute_path path
end
end
end
Queue = Thread::Queue
class Mutex
def initialize
# We still keep the @locked state so any logic based on try_lock while
# held yields reasonable results.
@locked = false
end
def lock
raise ThreadError, 'Deadlock' if @locked
@locked = true
self
end
def locked?
@locked
end
def owned?
# Being the only "thread", we implicitly own any locked mutex.
@locked
end
def try_lock
if locked?
false
else
lock
true
end
end
def unlock
raise ThreadError, 'Mutex not locked' unless @locked
@locked = false
self
end
def synchronize
lock
begin
yield
ensure
unlock
end
end
end