Skip to content

Commit

Permalink
Showing 63 changed files with 1,169 additions and 1,777 deletions.
11 changes: 0 additions & 11 deletions configure
Original file line number Diff line number Diff line change
@@ -72,7 +72,6 @@ class Configure
@libc = nil
@x86_32 = false
@x86_64 = false
@fibers = false
@dtrace = false
@dtrace_const = false
@have_lchmod = false
@@ -458,7 +457,6 @@ class Configure
feature "vendor-zlib", false
feature "vendor-libsodium", true
feature "alloc-tracking", false
feature "fibers", true
feature "dtrace", false
feature "rpath", true

@@ -1198,10 +1196,6 @@ int main() { return tgetnum(""); }
@defines << "RBX_ALLOC_TRACKING"
end

if @features["fibers"].value
@fibers = true if @x86_32 or @x86_64
end

if @features["dtrace"].value and has_dtrace
@defines << "HAVE_DTRACE"
end
@@ -1583,7 +1577,6 @@ int main() { return tgetnum(""); }
:x86_64 => @x86_64,
:dtrace => @dtrace,
:dtrace_const => @dtrace_const,
:fibers => @fibers,
:debug_build => @debug_build,
:sourcedir => @sourcedir,
:stagingdir => @stagingdir,
@@ -1690,10 +1683,6 @@ int main() { return tgetnum(""); }
end
end

if @fibers
f.puts "#define RBX_FIBER_ENABLED 1"
end

f.puts "#define RBX_DTRACE_CONST #{@dtrace_const ? "const" : ""}"

write_have_defines f
119 changes: 13 additions & 106 deletions core/enumerator.rb
Original file line number Diff line number Diff line change
@@ -98,19 +98,7 @@ def each_with_index
def next
return @lookahead.shift unless @lookahead.empty?

unless @generator
# Allow #to_generator to return nil, indicating it has none for
# this method.
if @object.respond_to? :to_generator
@generator = @object.to_generator(@iter)
end

if !@generator and gen = FiberGenerator
@generator = gen.new(self)
else
@generator = ThreadGenerator.new(self, @object, @iter, @args)
end
end
@generator ||= Iterator.new self

begin
return @generator.next if @generator.next?
@@ -198,6 +186,7 @@ def <<(*args)

class Generator
include Enumerable

def initialize(&block)
raise LocalJumpError, "Expected a block to be given" unless block_given?

@@ -447,123 +436,41 @@ def zip(*lists)
end
end

if Rubinius::Fiber::ENABLED
class FiberGenerator
STACK_SIZE = 1_048_576

attr_reader :result
class Iterator
STACK_SIZE = 1_048_576

def initialize(obj)
@object = obj
rewind
end

def next?
!@done
end

def next
reset unless @fiber

val = @fiber.resume

raise StopIteration, "iteration has ended" if @done

return val
end

def rewind
@fiber = nil
@done = false
end

def reset
@done = false
@fiber = Rubinius::Fiber.new stack_size: STACK_SIZE do
obj = @object
@result = obj.each do |*val|
Rubinius::Fiber.yield *val
end
@done = true
end
end
end
else
FiberGenerator = nil
end

class ThreadGenerator
attr_reader :result

def initialize(enum, obj, meth, args)
def initialize(obj)
@object = obj
@method = meth
@args = args

ObjectSpace.define_finalizer(enum, method(:kill))

rewind
end

# Used to cleanup the background thread when the enumerator
# is GC'd.
def kill(obj_id)
if @thread
@thread.kill
end
end

def next?
if @done
@thread.join if @thread
@thread = nil
return false
end

true
!@done
end

def next
reset unless @thread
reset unless @fiber

@hold_channel << nil
vals = @channel.receive
val = @fiber.resume

raise StopIteration, "iteration has ended" if @done

# return *[1] == [1], unfortunately.
return vals.size == 1 ? vals.first : vals
return val
end

def rewind
if @thread
@thread.kill
end

@fiber = nil
@done = false
@thread = nil
end

def reset
@done = false
@channel = Rubinius::Channel.new
@hold_channel = Rubinius::Channel.new

@thread = Thread.new do
@result = @object.__send__(@method, *@args) do |*vals|
@hold_channel.receive
@channel << vals
end

# Hold to indicate done to avoid race conditions setting
# the ivar.
@hold_channel.receive
@fiber = Fiber.new stack_size: STACK_SIZE do
obj = @object
@result = obj.each { |*val| Fiber.yield *val }
@done = true

# Put an extra value in the channel, so that main
# thread doesn't accidentally block if it doesn't
# detect @done in time.
@channel << nil
end
end
end
89 changes: 52 additions & 37 deletions core/fiber.rb
Original file line number Diff line number Diff line change
@@ -1,48 +1,63 @@
module Rubinius
class Fiber
attr_reader :stack_size
attr_reader :thread_name
attr_reader :fiber_id
attr_reader :source
class Fiber
attr_reader :stack_size
attr_reader :thread_name
attr_reader :fiber_id
attr_reader :source

def self.new(**kw, &block)
if block.nil?
raise ArgumentError, "Fiber.new requires a block"
end

def self.new(**kw, &block)
if block.nil?
raise ArgumentError, "Fiber.new requires a block"
end
stack_size = Rubinius::Type.try_convert kw[:stack_size], Fixnum, :to_int

stack_size = Rubinius::Type.try_convert kw[:stack_size], Fixnum, :to_int
Rubinius.invoke_primitive :fiber_new, stack_size, block, self
end

Rubinius.invoke_primitive :fiber_new, stack_size, block, self
end
def self.current
Rubinius.primitive :fiber_s_current
raise PrimitiveFailure, "Fiber.current primitive failed"
end

def self.current
Rubinius.primitive :fiber_s_current
raise PrimitiveFailure, "Rubinius::Fiber.current failed"
end
def self.yield(*args)
Rubinius.primitive :fiber_s_yield
raise PrimitiveFailure, "Fiber.yield primitive failed"
end

def self.yield(*args)
Rubinius.primitive :fiber_s_yield
raise PrimitiveFailure, "Rubinius::Fiber.yield failed"
end
def self.list
Rubinius.primitive :fiber_s_list
raise PrimitiveFailure, "Fiber.list primitive failed"
end

def resume(*args)
Rubinius.primitive :fiber_resume
raise PrimitiveFailure, "Rubinius::Fiber#resume failed"
end
def self.main
Rubinius.primitive :fiber_s_main
raise PrimitiveFailure, "Fiber.main primitive failed"
end

def transfer(*args)
Rubinius.primitive :fiber_transfer
raise PrimitiveFailure, "Rubinius::Fiber#transfer failed"
end
def status
Rubinius.primitive :fiber_status
raise PrimitiveFailure, "Fiber#status primitive failed"
end

def alive?
!@dead
end
def resume(*args)
Rubinius.primitive :fiber_resume
raise PrimitiveFailure, "Fiber#resume primitive failed"
end

def inspect
str = "#<#{self.class}:0x#{object_id.to_s(16)} thread_name=#{@thread_name} fiber_id=#{@fiber_id} status=#{alive? ? "alive" : "dead"}"
str << " source=#{@source}" if @source
str << ">"
end
def transfer(*args)
Rubinius.primitive :fiber_transfer
raise PrimitiveFailure, "Fiber#transfer primitive failed"
end

def alive?
status != "dead"
end

def inspect
str = "#<#{self.class}:0x#{object_id.to_s(16)} thread_name=#{@thread_name} fiber_id=#{@fiber_id} status=#{status}"
str << " source=#{@source}" if @source
str << ">"
end

alias_method :to_s, :inspect
end
50 changes: 30 additions & 20 deletions core/thread.rb
Original file line number Diff line number Diff line change
@@ -84,6 +84,11 @@ def self.stop
nil
end

def fiber_list
Rubinius.primitive :thread_fiber_list
Kernel.raise PrimitiveFailure, "Thread.fiber_list primitive failed"
end

def wakeup
Rubinius.primitive :thread_wakeup
Kernel.raise ThreadError, "Thread#wakeup primitive failed, thread may be dead"
@@ -224,40 +229,45 @@ def raise(exc=undefined, msg=nil, trace=nil)
end
end

def [](key)
locals_aref(Rubinius::Type.coerce_to_symbol(key))
def thread_variable_get(key)
key = Rubinius::Type.coerce_to_symbol key
Rubinius.invoke_primitive :thread_variable_get, self, key
end

def locals_aref(key)
Rubinius.primitive :thread_locals_aref
raise PrimitiveFailure, "Thread#locals_aref primitive failed"
def thread_variable_set(key, value)
key = Rubinius::Type.coerce_to_symbol key
Rubinius.invoke_primitive :thread_variable_set, self, key, value
end
private :locals_aref

def []=(key, value)
locals_store(Rubinius::Type.coerce_to_symbol(key), value)
def thread_variable?(key)
key = Rubinius::Type.coerce_to_symbol key
Rubinius.invoke_primitive :thread_variable_key_p, self, key
end

def locals_store(key, value)
Rubinius.primitive :thread_locals_store
raise PrimitiveFailure, "Thread#locals_store primitive failed"
def thread_variables
Rubinius.primitive :thread_variables
raise PrimitiveFailure, "Thread#thread_variables primitive failed"
end
private :locals_store

def keys
Rubinius.primitive :thread_locals_keys
raise PrimitiveFailure, "Thread#keys primitive failed"
def [](key)
key = Rubinius::Type.coerce_to_symbol key
Rubinius.invoke_primitive :thread_fiber_variable_get, self, key
end

def []=(key, value)
key = Rubinius::Type.coerce_to_symbol key
Rubinius.invoke_primitive :thread_fiber_variable_set, self, key, value
end

def key?(key)
locals_key?(Rubinius::Type.coerce_to_symbol(key))
key = Rubinius::Type.coerce_to_symbol key
Rubinius.invoke_primitive :thread_fiber_variable_key_p, self, key
end

def locals_key?(key)
Rubinius.primitive :thread_locals_has_key
raise PrimitiveFailure, "Thread#locals_key? primitive failed"
def keys
Rubinius.primitive :thread_fiber_variables
raise PrimitiveFailure, "Thread#keys primitive failed"
end
private :locals_key?

# Register another Thread object +thr+ as the Thread where the debugger
# is running. When the current thread hits a breakpoint, it uses this
2 changes: 0 additions & 2 deletions core/zed.rb
Original file line number Diff line number Diff line change
@@ -1062,8 +1062,6 @@ module Errno
FFI = Rubinius::FFI
end

Fiber = Rubinius::Fiber

class File < IO
# these will be necessary when we run on Windows
DOSISH = false # !!(RUBY_PLATFORM =~ /mswin/)
2 changes: 1 addition & 1 deletion machine/builtin/channel.cpp
Original file line number Diff line number Diff line change
@@ -168,7 +168,7 @@ namespace rubinius {
}

state->vm()->clear_waiter();
state->vm()->thread->sleep(state, cFalse);
state->vm()->thread()->sleep(state, cFalse);

self->unpin();
self->_waiters_--;
5 changes: 4 additions & 1 deletion machine/builtin/exception.cpp
Original file line number Diff line number Diff line change
@@ -245,7 +245,10 @@ namespace rubinius {
}

void Exception::raise_fiber_error(STATE, const char* reason) {
RubyException::raise(make_exception(state, get_fiber_error(state), reason));
Exception* exc = make_exception(state, get_fiber_error(state), reason);
exc->locations(state, Location::from_call_stack(state, 0));

RubyException::raise(exc);
}

void Exception::raise_memory_error(STATE) {
510 changes: 288 additions & 222 deletions machine/builtin/fiber.cpp

Large diffs are not rendered by default.

131 changes: 68 additions & 63 deletions machine/builtin/fiber.hpp
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
#ifndef RBX_BUILTIN_FIBER
#define RBX_BUILTIN_FIBER

#include "fiber_data.hpp"
#include "object_utils.hpp"

#include "builtin/array.hpp"
#include "builtin/exception.hpp"
#include "builtin/lookup_table.hpp"
#include "builtin/object.hpp"
#include "builtin/string.hpp"
#include "builtin/thread.hpp"

#include "instruments/timing.hpp"

@@ -17,6 +17,7 @@

namespace rubinius {
class LookupTable;
class VM;

class Fiber : public Object {
public:
@@ -25,112 +26,116 @@ namespace rubinius {
static std::atomic<uint32_t> fiber_ids_;

enum Status {
eNotStarted, eSleeping, eRunning, eDead
eCreated,
eRunning,
eYielding,
eTransfer,
eDead
};

attr_accessor(starter, Object);
attr_accessor(value, Array);
attr_accessor(prev, Fiber);
attr_accessor(block, Object);
attr_accessor(exception, Exception);
attr_accessor(locals, LookupTable);
attr_accessor(dead, Object);
attr_accessor(pid, Fixnum);
attr_accessor(stack_size, Fixnum);
attr_accessor(thread_name, String);
attr_accessor(fiber_id, Fixnum);
attr_accessor(source, String);
attr_accessor(thread, Thread);

private:
attr_field(status, Status);
attr_field(root, bool);
attr_field(data, FiberData*);
attr_field(start_time, uint64_t);

attr_field(vm, VM*);
attr_field(invoke_context, VM*);
attr_field(restart_context, VM*);

std::atomic<Status> status_;
std::atomic<bool> wakeup_;

public:
bool root_p() const {
return root();
static void bootstrap(STATE);
static void initialize(STATE, Fiber* obj) {
obj->block(nil<Object>());
obj->exception(nil<Exception>());
obj->locals(LookupTable::create(state));
obj->pid(Fixnum::from(0));
obj->stack_size(Fixnum::from(state->shared().config.machine_fiber_stack_size.value));
obj->thread_name(String::create(state, state->vm()->name().c_str()));
obj->fiber_id(Fixnum::from(++Fiber::fiber_ids_));
obj->source(nil<String>());
obj->thread(state->vm()->thread());
obj->start_time(get_current_time());
obj->vm(NULL);
obj->invoke_context(state->vm());
obj->restart_context(state->vm());
obj->status(eCreated);
obj->clear_wakeup();
}

CallFrame* call_frame(STATE) const {
if(!data()) Exception::raise_fiber_error(state, "corrupt Fiber");
static void finalize(STATE, Fiber* fib);
static void* run(void*);

return data()->call_frame();
}
static Fiber* create(STATE, VM* vm);

void set_call_frame(STATE, CallFrame* call_frame) {
if(!data()) Exception::raise_fiber_error(state, "corrupt Fiber");
// Rubinius.primitive :fiber_new
static Fiber* create(STATE, Object* self, Object* stack_size, Object* block);

data()->set_call_frame(call_frame);
}
// Rubinius.primitive :fiber_s_current
static Fiber* current(STATE);

void sleep(STATE) {
if(!data()) Exception::raise_fiber_error(state, "corrupt Fiber");
// Rubinius.primitive :fiber_s_yield
static Object* s_yield(STATE, Arguments& args);

data()->set_call_frame(state->vm()->call_frame());
status(eSleeping);
}
// Rubinius.primitive :fiber_s_list
static Array* s_list(STATE);

// Rubinius.primitive :fiber_s_main
static Fiber* s_main(STATE);

void run(STATE) {
if(!data()) Exception::raise_fiber_error(state, "corrupt Fiber");
bool root_p();

state->vm()->set_current_fiber(this);
state->vm()->set_call_frame(data()->call_frame());
data()->set_call_frame(NULL);
status(eRunning);
Status status() {
return status_;
}

fiber_context_t* ucontext() const {
return data()->machine();
void status(Status status) {
status_ = status;
}

memory::VariableRootBuffers& variable_root_buffers() {
return data()->variable_root_buffers();
void wakeup() {
wakeup_ = true;
}

public:
static void bootstrap(STATE);
static void initialize(STATE, Fiber* obj) {
obj->starter(nil<Object>());
obj->value(nil<Array>());
obj->prev(nil<Fiber>());
obj->exception(nil<Exception>());
obj->locals(nil<LookupTable>());
obj->dead(nil<Object>());
obj->stack_size(Fixnum::from(state->shared().config.machine_fiber_stack_size.value));
obj->thread_name(String::create(state, state->vm()->name().c_str()));
obj->fiber_id(Fixnum::from(++Fiber::fiber_ids_));
obj->source(nil<String>());
obj->status(eNotStarted);
obj->root(false);
obj->data(NULL);
obj->start_time(get_current_time());
void clear_wakeup() {
wakeup_ = false;
}

// Rubinius.primitive :fiber_new
static Fiber* create(STATE, Object* self, Object* stack_size, Object* callable);
static void start_on_stack();
bool wakeup_p() {
return wakeup_;
}

double run_time();
void unpack_arguments(STATE, Arguments& args);
Object* return_value(STATE);

// Rubinius.primitive :fiber_s_current
static Fiber* current(STATE);
void start(STATE, Arguments& args);
void restart(STATE);
void suspend_and_continue(STATE);

// Rubinius.primitive :fiber_status
String* status(STATE);

// Rubinius.primitive :fiber_resume
Object* resume(STATE, Arguments& args);

// Rubinius.primitive :fiber_transfer
Object* transfer(STATE, Arguments& args);

// Rubinius.primitive :fiber_s_yield
static Object* s_yield(STATE, Arguments& args);

static void finalize(STATE, Fiber* fib);

public: /* TypeInfo */

class Info : public TypeInfo {
public:
BASIC_TYPEINFO(TypeInfo)
virtual void mark(Object* t, memory::ObjectMark& mark);
};
};
}
20 changes: 10 additions & 10 deletions machine/builtin/io.cpp
Original file line number Diff line number Diff line change
@@ -273,7 +273,7 @@ namespace rubinius {
/* And the main event, pun intended */
retry:
state->vm()->interrupt_with_signal();
state->vm()->thread->sleep(state, cTrue);
state->vm()->thread()->sleep(state, cTrue);

{
UnmanagedPhase unmanaged(state);
@@ -283,7 +283,7 @@ namespace rubinius {
maybe_limit);
}

state->vm()->thread->sleep(state, cFalse);
state->vm()->thread()->sleep(state, cFalse);
state->vm()->clear_waiter();

if(events == -1) {
@@ -666,14 +666,14 @@ namespace rubinius {

retry:
state->vm()->interrupt_with_signal();
state->vm()->thread->sleep(state, cTrue);
state->vm()->thread()->sleep(state, cTrue);

{
UnmanagedPhase unmanaged(state);
bytes_read = ::read(fd, buf, count);
}

state->vm()->thread->sleep(state, cFalse);
state->vm()->thread()->sleep(state, cFalse);
state->vm()->clear_waiter();

if(bytes_read == -1) {
@@ -950,7 +950,7 @@ namespace rubinius {

retry:
state->vm()->interrupt_with_signal();
state->vm()->thread->sleep(state, cTrue);
state->vm()->thread()->sleep(state, cTrue);

{
UnmanagedPhase unmanaged(state);
@@ -960,7 +960,7 @@ namespace rubinius {
(struct sockaddr*)buf, &alen);
}

state->vm()->thread->sleep(state, cFalse);
state->vm()->thread()->sleep(state, cFalse);
state->vm()->clear_waiter();

buffer->unpin();
@@ -1309,14 +1309,14 @@ namespace rubinius {

retry:
state->vm()->interrupt_with_signal();
state->vm()->thread->sleep(state, cTrue);
state->vm()->thread()->sleep(state, cTrue);

{
UnmanagedPhase unmanaged(state);
code = recvmsg(read_fd, &msg, 0);
}

state->vm()->thread->sleep(state, cFalse);
state->vm()->thread()->sleep(state, cFalse);
state->vm()->clear_waiter();

if(code == -1) {
@@ -1410,14 +1410,14 @@ namespace rubinius {

retry:
state->vm()->interrupt_with_signal();
state->vm()->thread->sleep(state, cTrue);
state->vm()->thread()->sleep(state, cTrue);

{
UnmanagedPhase unmanaged(state);
bytes_read = ::read(fd, temp_buffer, count);
}

state->vm()->thread->sleep(state, cFalse);
state->vm()->thread()->sleep(state, cFalse);
state->vm()->clear_waiter();

if(bytes_read == -1) {
6 changes: 3 additions & 3 deletions machine/builtin/system.cpp
Original file line number Diff line number Diff line change
@@ -440,7 +440,7 @@ namespace rubinius {
if(pid == 0) {
close(errors[0]);

state->vm()->thread->init_lock();
state->vm()->thread()->init_lock();
state->shared().machine_threads()->after_fork_exec_child(state);

// Setup ENV, redirects, groups, etc. in the child before exec().
@@ -572,7 +572,7 @@ namespace rubinius {
}

if(pid == 0) {
state->vm()->thread->init_lock();
state->vm()->thread()->init_lock();
state->shared().machine_threads()->after_fork_exec_child(state);

close(errors[0]);
@@ -895,7 +895,7 @@ namespace rubinius {
// We're in the child...
state->vm()->after_fork_child(state);

state->vm()->thread->init_lock();
state->vm()->thread()->init_lock();
state->shared().after_fork_child(state);
state->shared().machine_threads()->after_fork_child(state);

145 changes: 49 additions & 96 deletions machine/builtin/thread.cpp
Original file line number Diff line number Diff line change
@@ -74,7 +74,10 @@ namespace rubinius {
thr->vm(vm);
thr->thread_id(state, Fixnum::from(vm->thread_id()));

vm->thread.set(thr);
vm->set_thread(thr);

thr->fiber(state, Fiber::create(state, vm));
thr->current_fiber(state, thr->fiber());

return thr;
}
@@ -112,6 +115,7 @@ namespace rubinius {

void Thread::finalize_instance(STATE) {
if(vm() && vm()->zombie_p()) {
fiber_mutex_.std::mutex::~mutex();
VM::discard(state, vm());
vm(NULL);
}
@@ -121,7 +125,7 @@ namespace rubinius {
/* These are all referenced, so OnStack is not necessary. Additionally,
* thread is pinned, so we do not need to worry about it moving.
*/
Thread* thread = state->vm()->thread.get();
Thread* thread = state->vm()->thread();
Array* args = thread->args();
Object* block = thread->block();

@@ -226,7 +230,7 @@ namespace rubinius {
}

Thread* Thread::current(STATE) {
return state->vm()->thread.get();
return state->vm()->thread();
}

void Thread::unlock_after_fork(STATE) {
@@ -244,92 +248,41 @@ namespace rubinius {
los.clear();
}

Object* Thread::locals_aref(STATE, Symbol* key) {
/*
* If we're not trying to set values on the current thread,
* we will set thread locals anyway and not use fiber locals.
*/
if(state->vm() != vm()) {
return locals()->aref(state, key);
}
Fiber* fib = state->vm()->current_fiber.get();
if(fib->nil_p() || fib->root_p()) {
return locals()->aref(state, key);
}
if(try_as<LookupTable>(fib->locals())) {
return fib->locals()->aref(state, key);
}
return cNil;
Object* Thread::variable_get(STATE, Symbol* key) {
return locals()->aref(state, key);
}

Object* Thread::locals_store(STATE, Symbol* key, Object* value) {
/*
* If we're not trying to set values on the current thread,
* we will set thread locals anyway and not use fiber locals.
*/
check_frozen(state);
if(state->vm() != vm()) {
return locals()->store(state, key, value);
}
Fiber* fib = state->vm()->current_fiber.get();
if(fib->nil_p() || fib->root_p()) {
return locals()->store(state, key, value);
}
if(fib->locals()->nil_p()) {
fib->locals(state, LookupTable::create(state));
}
return fib->locals()->store(state, key, value);
Object* Thread::variable_set(STATE, Symbol* key, Object* value) {
return locals()->store(state, key, value);
}

Object* Thread::variable_key_p(STATE, Symbol* key) {
return locals()->has_key(state, key);
}

Array* Thread::variables(STATE) {
return locals()->all_keys(state);
}

Object* Thread::locals_remove(STATE, Symbol* key) {
Array* Thread::fiber_list(STATE) {
return state->shared().vm_thread_fibers(state, this);
}

Object* Thread::fiber_variable_get(STATE, Symbol* key) {
return current_fiber()->locals()->aref(state, key);
}

Object* Thread::fiber_variable_set(STATE, Symbol* key, Object* value) {
check_frozen(state);
if(state->vm() != vm()) {
return locals()->remove(state, key);
}
Fiber* fib = state->vm()->current_fiber.get();
if(fib->nil_p() || fib->root_p()) {
return locals()->remove(state, key);
}
if(fib->locals()->nil_p()) {
return cNil;
}
return fib->locals()->remove(state, key);
return current_fiber()->locals()->store(state, key, value);
}

Array* Thread::locals_keys(STATE) {
/*
* If we're not trying to set values on the current thread,
* we will set thread locals anyway and not use fiber locals.
*/
if(state->vm() != vm()) {
return locals()->all_keys(state);
}
Fiber* fib = state->vm()->current_fiber.get();
if(fib->nil_p() || fib->root_p()) {
return locals()->all_keys(state);
}
if(try_as<LookupTable>(fib->locals())) {
return fib->locals()->all_keys(state);
}
return Array::create(state, 0);
Object* Thread::fiber_variable_key_p(STATE, Symbol* key) {
return current_fiber()->locals()->has_key(state, key);
}

Object* Thread::locals_has_key(STATE, Symbol* key) {
/*
* If we're not trying to set values on the current thread,
* we will set thread locals anyway and not use fiber locals.
*/
if(state->vm() != vm()) {
return locals()->has_key(state, key);
}
Fiber* fib = state->vm()->current_fiber.get();
if(fib->nil_p() || fib->root_p()) {
return locals()->has_key(state, key);
}
if(try_as<LookupTable>(fib->locals())) {
return fib->locals()->has_key(state, key);
}
return cFalse;
Array* Thread::fiber_variables(STATE) {
return current_fiber()->locals()->all_keys(state);
}

int Thread::start_thread(STATE, void* (*function)(void*)) {
@@ -360,7 +313,7 @@ namespace rubinius {
G(rubinius)->set_const(state, "RUNTIME_PATH", String::create(state,
runtime.c_str(), runtime.size()));

state->vm()->thread->pid(state, Fixnum::from(gettid()));
state->vm()->thread()->pid(state, Fixnum::from(gettid()));

state->shared().env()->load_core(state, runtime);

@@ -401,30 +354,30 @@ namespace rubinius {
VM* vm = reinterpret_cast<VM*>(ptr);
State state_obj(vm), *state = &state_obj;

vm->set_stack_bounds(vm->thread->stack_size()->to_native());
vm->set_stack_bounds(vm->thread()->stack_size()->to_native());
vm->set_current_thread();
vm->set_start_time();

RUBINIUS_THREAD_START(
const_cast<RBX_DTRACE_CHAR_P>(vm->name().c_str()), vm->thread_id(), 0);

vm->thread->pid(state, Fixnum::from(gettid()));
vm->thread()->pid(state, Fixnum::from(gettid()));

if(state->shared().config.machine_thread_log_lifetime.value) {
logger::write("thread: run: %s, %d, %#x",
vm->name().c_str(), vm->thread->pid()->to_native(),
vm->name().c_str(), vm->thread()->pid()->to_native(),
(unsigned int)thread_debug_self());
}

NativeMethod::init_thread(state);

state->vm()->managed_phase();

Object* value = vm->thread->function()(state);
Object* value = vm->thread()->function()(state);
vm->set_call_frame(NULL);

vm->thread->join_lock_.lock();
vm->thread->stopped();
vm->thread()->join_lock_.lock();
vm->thread()->stopped();

state->shared().report_profile(state);

@@ -437,8 +390,8 @@ namespace rubinius {
}
locked_objects.clear();

vm->thread->join_cond_.broadcast();
vm->thread->join_lock_.unlock();
vm->thread()->join_cond_.broadcast();
vm->thread()->join_lock_.unlock();

NativeMethod::cleanup_thread(state);

@@ -491,8 +444,8 @@ namespace rubinius {

if(!vm()) return cNil;

vm()->register_raise(state, exc);
vm()->wakeup(state);
current_fiber()->vm()->register_raise(state, exc);
current_fiber()->vm()->wakeup(state);

return exc;
}
@@ -502,12 +455,12 @@ namespace rubinius {

if(!vm()) return cNil;

if(state->vm()->thread.get() == this) {
vm()->thread_state_.raise_thread_kill();
if(state->vm()->thread() == this) {
current_fiber()->vm()->thread_state()->raise_thread_kill();
return NULL;
} else {
vm()->register_kill(state);
vm()->wakeup(state);
current_fiber()->vm()->register_kill(state);
current_fiber()->vm()->wakeup(state);
return this;
}
}
@@ -519,7 +472,7 @@ namespace rubinius {
return force_as<Thread>(Primitives::failure());
}

vm()->wakeup(state);
current_fiber()->vm()->wakeup(state);

return this;
}
74 changes: 39 additions & 35 deletions machine/builtin/thread.hpp
Original file line number Diff line number Diff line change
@@ -5,11 +5,15 @@

#include "builtin/channel.hpp"
#include "builtin/exception.hpp"
#include "builtin/fiber.hpp"
#include "builtin/fixnum.hpp"
#include "builtin/lookup_table.hpp"
#include "builtin/object.hpp"
#include "builtin/randomizer.hpp"

#include <list>
#include <mutex>

namespace rubinius {
class Array;

@@ -43,11 +47,15 @@ namespace rubinius {
attr_accessor(initialized, Object);
attr_accessor(stack_size, Fixnum);
attr_accessor(source, String);
attr_accessor(fiber, Fiber);
attr_accessor(current_fiber, Fiber);
attr_accessor(fiber_value, Object);

private:
utilities::thread::SpinLock init_lock_;
utilities::thread::Mutex join_lock_;
utilities::thread::Condition join_cond_;
std::mutex fiber_mutex_;

/// The VM state for this thread and this thread alone
attr_field(vm, VM*);
@@ -78,14 +86,23 @@ namespace rubinius {
obj->initialized(cFalse);
obj->stack_size(Fixnum::from(state->shared().config.machine_thread_stack_size.value));
obj->source(nil<String>());
obj->fiber(nil<Fiber>());
obj->current_fiber(nil<Fiber>());
obj->fiber_value(nil<Object>());

obj->init_lock_.init();
obj->join_lock_.init();
obj->join_cond_.init();

new(&obj->fiber_mutex_) std::mutex;

obj->vm(0);
}

std::mutex& fiber_mutex() {
return fiber_mutex_;
}

public:

// Rubinius.primitive :thread_s_new
@@ -173,45 +190,32 @@ namespace rubinius {
// thread.
void unlock_after_fork(STATE);

/**
* Retrieve a value store in the thread locals.
* This is done in a primitive because it also has
* to consider any running fibers.
*/
// Rubinius.primitive+ :thread_locals_aref
Object* locals_aref(STATE, Symbol* key);
// Rubinius.primitive :thread_fiber_list
Array* fiber_list(STATE);

/**
* Store a value in the thread locals.
* This is done in a primitive because it also has
* to consider any running fibers.
*/
// Rubinius.primitive :thread_locals_store
Object* locals_store(STATE, Symbol* key, Object* value);
// Rubinius.primitive :thread_fiber_variable_get
Object* fiber_variable_get(STATE, Symbol* key);

/**
* Remove a value from the thread locals.
* This is done in a primitive because it also has
* to consider any running fibers.
*/
// Rubinius.primitive :thread_locals_remove
Object* locals_remove(STATE, Symbol* key);
// Rubinius.primitive :thread_fiber_variable_set
Object* fiber_variable_set(STATE, Symbol* key, Object* value);

/**
* Retrieve the keys for all thread locals.
* This is done in a primitive because it also has
* to consider any running fibers.
*/
// Rubinius.primitive :thread_locals_keys
Array* locals_keys(STATE);
// Rubinius.primitive :thread_fiber_variable_key_p
Object* fiber_variable_key_p(STATE, Symbol* key);

/**
* Check whether a given key has a value store in the thread locals.
* This is done in a primitive because it also has
* to consider any running fibers.
*/
// Rubinius.primitive+ :thread_locals_has_key
Object* locals_has_key(STATE, Symbol* key);
// Rubinius.primitive :thread_fiber_variables
Array* fiber_variables(STATE);

// Rubinius.primitive :thread_variable_get
Object* variable_get(STATE, Symbol* key);

// Rubinius.primitive :thread_variable_set
Object* variable_set(STATE, Symbol* key, Object* value);

// Rubinius.primitive :thread_variable_key_p
Object* variable_key_p(STATE, Symbol* key);

// Rubinius.primitive :thread_variables
Array* variables(STATE);

void init_lock();
void stopped();
29 changes: 0 additions & 29 deletions machine/builtin/variable_scope.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
#include "memory.hpp"
#include "call_frame.hpp"
#include "fiber_data.hpp"

#include "builtin/class.hpp"
#include "builtin/exception.hpp"
@@ -131,15 +130,6 @@ namespace rubinius {
void VariableScope::set_local(int pos, Object* val) {
Object** ary = locals();

if(Fiber* fib = try_as<Fiber>(fiber())) {
FiberData* data = fib->data();
if(data) {
memory::AddressDisplacement dis(data->data_offset(),
data->data_lower_bound(), data->data_upper_bound());

ary = dis.displace(ary);
}
}
ary[pos] = val;
}

@@ -162,15 +152,7 @@ namespace rubinius {

Object* VariableScope::get_local(int pos) {
Object** ary = locals();
if(Fiber* fib = try_as<Fiber>(fiber())) {
FiberData* data = fib->data();
if(data) {
memory::AddressDisplacement dis(data->data_offset(),
data->data_lower_bound(), data->data_upper_bound());

ary = dis.displace(ary);
}
}
return ary[pos];
}

@@ -213,17 +195,6 @@ namespace rubinius {
if(!vs->isolated_p()) {
Object** ary = vs->locals();

if(Fiber* fib = try_as<Fiber>(vs->fiber())) {
FiberData* data = fib->data();

if(data) {
memory::AddressDisplacement dis(data->data_offset(),
data->data_lower_bound(), data->data_upper_bound());

ary = dis.displace(ary);
}
}

size_t locals = vs->number_of_locals();

for(size_t i = 0; i < locals; i++) {
20 changes: 10 additions & 10 deletions machine/capi/io.cpp
Original file line number Diff line number Diff line change
@@ -180,11 +180,11 @@ extern "C" {
{
UnmanagedPhase unmanaged(state);
state->vm()->interrupt_with_signal();
state->vm()->thread->sleep(state, cTrue);
state->vm()->thread()->sleep(state, cTrue);

ret = fread(ptr, 1, len, f);

state->vm()->thread->sleep(state, cFalse);
state->vm()->thread()->sleep(state, cFalse);
state->vm()->clear_waiter();
}

@@ -226,15 +226,15 @@ extern "C" {
{
UnmanagedPhase unmanaged(state);
state->vm()->interrupt_with_signal();
state->vm()->thread->sleep(state, cTrue);
state->vm()->thread()->sleep(state, cTrue);

int ready = 0;
while(!ready) {
ready = select(fd+1, &fds, 0, 0, 0);
if(!retry) break;
}

state->vm()->thread->sleep(state, cFalse);
state->vm()->thread()->sleep(state, cFalse);
state->vm()->clear_waiter();
}

@@ -277,15 +277,15 @@ extern "C" {
{
UnmanagedPhase unmanaged(state);
state->vm()->interrupt_with_signal();
state->vm()->thread->sleep(state, cTrue);
state->vm()->thread()->sleep(state, cTrue);

int ready = 0;
while(!ready) {
ready = select(fd+1, 0, &fds, 0, 0);
if(!retry) break;
}

state->vm()->thread->sleep(state, cFalse);
state->vm()->thread()->sleep(state, cFalse);
state->vm()->clear_waiter();
}
ENTER_CAPI(env->state());
@@ -308,14 +308,14 @@ extern "C" {
{
UnmanagedPhase unmanaged(state);
state->vm()->interrupt_with_signal();
state->vm()->thread->sleep(state, cTrue);
state->vm()->thread()->sleep(state, cTrue);

int ready = 0;
while(!ready) {
ready = select(fd+1, &fds, 0, 0, 0);
}

state->vm()->thread->sleep(state, cFalse);
state->vm()->thread()->sleep(state, cFalse);
state->vm()->clear_waiter();
}
ENTER_CAPI(env->state());
@@ -337,14 +337,14 @@ extern "C" {
{
UnmanagedPhase unmanaged(state);
state->vm()->interrupt_with_signal();
state->vm()->thread->sleep(state, cTrue);
state->vm()->thread()->sleep(state, cTrue);

int ready = 0;
while(!ready) {
ready = select(fd+1, 0, &fds, 0, 0);
}

state->vm()->thread->sleep(state, cFalse);
state->vm()->thread()->sleep(state, cFalse);
state->vm()->clear_waiter();
}

28 changes: 14 additions & 14 deletions machine/capi/thread.cpp
Original file line number Diff line number Diff line change
@@ -98,7 +98,7 @@ extern "C" {

VALUE rb_thread_current(void) {
NativeMethodEnvironment* env = NativeMethodEnvironment::get();
Thread* thread = env->state()->vm()->thread.get();
Thread* thread = env->state()->vm()->thread();

return env->get_handle(thread);
}
@@ -114,14 +114,16 @@ extern "C" {
VALUE rb_thread_local_aref(VALUE thread, ID id) {
NativeMethodEnvironment* env = NativeMethodEnvironment::get();
Thread* thr = capi::c_as<Thread>(env->get_object(thread));
return env->get_handle(thr->locals_aref(env->state(), reinterpret_cast<Symbol*>(id)));
return env->get_handle(
thr->fiber_variable_get(env->state(), reinterpret_cast<Symbol*>(id)));
}

VALUE rb_thread_local_aset(VALUE thread, ID id, VALUE value) {
NativeMethodEnvironment* env = NativeMethodEnvironment::get();
Thread* thr = capi::c_as<Thread>(env->get_object(thread));
return env->get_handle(thr->locals_store(env->state(), reinterpret_cast<Symbol*>(id),
env->get_object(value)));
return env->get_handle(
thr->fiber_variable_set(
env->state(), reinterpret_cast<Symbol*>(id), env->get_object(value)));
}

VALUE rb_thread_wakeup(VALUE thread) {
@@ -239,14 +241,12 @@ extern "C" {
Object* run_function(STATE) {
NativeMethodEnvironment* env = NativeMethodEnvironment::get();

Thread* thread = state->vm()->thread.get();
Thread* thread = state->vm()->thread();

NativeMethod* nm = capi::c_as<NativeMethod>(
thread->locals_aref(state, state->symbol("function")));
Pointer* ptr = capi::c_as<Pointer>(thread->locals_aref(state, state->symbol("argument")));

thread->locals_remove(state, state->symbol("function"));
thread->locals_remove(state, state->symbol("argument"));
thread->variable_get(state, state->symbol("function")));
Pointer* ptr = capi::c_as<Pointer>(
thread->variable_get(state, state->symbol("argument")));

NativeMethodFrame nmf(env, 0, nm);
CallFrame call_frame;
@@ -282,7 +282,7 @@ extern "C" {
LEAVE_CAPI(state);

// Set exception in thread so it's raised when joining.
state->vm()->thread.get()->exception(state,
state->vm()->thread()->exception(state,
capi::c_as<Exception>(state->vm()->thread_state()->current_exception()));
} else {
value = env->get_object(nm->func()(ptr->pointer));
@@ -310,10 +310,10 @@ extern "C" {

Thread* thr = Thread::create(env->state(), G(thread), run_function);

thr->locals_store(state, state->symbol("function"), nm);
thr->locals_store(state, state->symbol("argument"), ptr);
thr->variable_set(state, state->symbol("function"), nm);
thr->variable_set(state, state->symbol("argument"), ptr);

thr->group(state, state->vm()->thread.get()->group());
thr->group(state, state->vm()->thread()->group());

VALUE thr_handle = env->get_handle(thr);
thr->fork(state);
40 changes: 24 additions & 16 deletions machine/codegen/field_extract.rb
Original file line number Diff line number Diff line change
@@ -58,8 +58,9 @@ def output_call(str, call, args)
str << " ret = #{call}(#{args.join(', ')});\n"
str << " RUBINIUS_METHOD_PRIMITIVE_RETURN_HOOK(state, mod, args.name());\n"
str << " } catch(const RubyException& exc) {\n"
str << " exc.exception->locations(state,\n"
str << " Location::from_call_stack(state));\n"
str << " if(exc.exception->locations()->nil_p()) {\n"
str << " exc.exception->locations(state, Location::from_call_stack(state));\n"
str << " }\n"
str << " state->raise_exception(exc.exception);\n"
str << " RUBINIUS_METHOD_PRIMITIVE_RETURN_HOOK(state, mod, args.name());\n"
str << " return NULL;\n"
@@ -106,8 +107,9 @@ def generate_glue
str << " try {\n"
str << " ret = recv->#{@cpp_name}(state, exec, mod, args);\n"
str << " } catch(const RubyException& exc) {\n"
str << " exc.exception->locations(state,\n"
str << " Location::from_call_stack(state));\n"
str << " if(exc.exception->locations()->nil_p()) {\n"
str << " exc.exception->locations(state, Location::from_call_stack(state));\n"
str << " }\n"
str << " state->raise_exception(exc.exception);\n"
str << " return NULL;\n"
str << " }\n"
@@ -182,8 +184,9 @@ def generate_jit_stub
str << " try {\n"
str << " ret = self->#{@cpp_name}(#{args.join(', ')});\n"
str << " } catch(const RubyException& exc) {\n"
str << " exc.exception->locations(state,\n"
str << " Location::from_call_stack(state));\n"
str << " if(exc.exception->locations()->nil_p()) {\n"
str << " exc.exception->locations(state, Location::from_call_stack(state));\n"
str << " }\n"
str << " state->raise_exception(exc.exception);\n"
str << " return NULL;\n"
str << " }\n"
@@ -260,8 +263,9 @@ def generate_invoke_stub
str << " try {\n"
str << " ret = self->#{@cpp_name}(#{args.join(', ')});\n"
str << " } catch(const RubyException& exc) {\n"
str << " exc.exception->locations(state,\n"
str << " Location::from_call_stack(state));\n"
str << " if(exc.exception->locations()->nil_p()) {\n"
str << " exc.exception->locations(state, Location::from_call_stack(state));\n"
str << " }\n"
str << " state->raise_exception(exc.exception);\n"
str << " return NULL;\n"
str << " }\n"
@@ -288,8 +292,9 @@ def generate_glue
str << " try {\n"
str << " return #{@type}::#{@cpp_name}(state, exec, mod);\n"
str << " } catch(const RubyException& exc) {\n"
str << " exc.exception->locations(state,\n"
str << " Location::from_call_stack(state));\n"
str << " if(exc.exception->locations()->nil_p()) {\n"
str << " exc.exception->locations(state, Location::from_call_stack(state));\n"
str << " }\n"
str << " state->raise_exception(exc.exception);\n"
str << " return NULL;\n"
str << " }\n"
@@ -350,8 +355,9 @@ def generate_jit_stub
str << " try {\n"
str << " ret = #{@type}::#{@cpp_name}(#{args.join(', ')});\n"
str << " } catch(const RubyException& exc) {\n"
str << " exc.exception->locations(state,\n"
str << " Location::from_call_stack(state));\n"
str << " if(exc.exception->locations()->nil_p()) {\n"
str << " exc.exception->locations(state, Location::from_call_stack(state));\n"
str << " }\n"
str << " state->raise_exception(exc.exception);\n"
str << " return NULL;\n"
str << " }\n"
@@ -414,8 +420,9 @@ def generate_invoke_stub
str << " try {\n"
str << " ret = #{@type}::#{@cpp_name}(#{args.join(', ')});\n"
str << " } catch(const RubyException& exc) {\n"
str << " exc.exception->locations(state,\n"
str << " Location::from_call_stack(state));\n"
str << " if(exc.exception->locations()->nil_p()) {\n"
str << " exc.exception->locations(state, Location::from_call_stack(state));\n"
str << " }\n"
str << " state->raise_exception(exc.exception);\n"
str << " return NULL;\n"
str << " }\n"
@@ -466,8 +473,9 @@ def generate_glue
end
str << call
str << " } catch(const RubyException& exc) {\n"
str << " exc.exception->locations(state,\n"
str << " Location::from_call_stack(state));\n"
str << " if(exc.exception->locations()->nil_p()) {\n"
str << " exc.exception->locations(state, Location::from_call_stack(state));\n"
str << " }\n"
str << " state->raise_exception(exc.exception);\n"
str << " return NULL;\n"
str << " }\n"
234 changes: 0 additions & 234 deletions machine/fiber_data.cpp

This file was deleted.

190 changes: 0 additions & 190 deletions machine/fiber_data.hpp

This file was deleted.

167 changes: 0 additions & 167 deletions machine/fiber_stack.cpp

This file was deleted.

108 changes: 0 additions & 108 deletions machine/fiber_stack.hpp

This file was deleted.

20 changes: 12 additions & 8 deletions machine/instructions.cpp
Original file line number Diff line number Diff line change
@@ -116,8 +116,9 @@ Object* MachineCode::interpreter(STATE, MachineCode* const mcode) {
call_frame->scope->flush_to_heap(state);
return NULL;
} catch(const RubyException& exc) {
exc.exception->locations(state,
Location::from_call_stack(state));
if(exc.exception->locations()->nil_p()) {
exc.exception->locations(state, Location::from_call_stack(state));
}
state->raise_exception(exc.exception);
return NULL;
}
@@ -267,8 +268,9 @@ Object* MachineCode::uncommon_interpreter(STATE,
call_frame->scope->flush_to_heap(state);
return NULL;
} catch(const RubyException& exc) {
exc.exception->locations(state,
Location::from_call_stack(state));
if(exc.exception->locations()->nil_p()) {
exc.exception->locations(state, Location::from_call_stack(state));
}
state->raise_exception(exc.exception);
return NULL;
}
@@ -412,8 +414,9 @@ Object* MachineCode::debugger_interpreter(STATE, MachineCode* const mcode) {
call_frame->scope->flush_to_heap(state);
return NULL;
} catch(const RubyException& exc) {
exc.exception->locations(state,
Location::from_call_stack(state));
if(exc.exception->locations()->nil_p()) {
exc.exception->locations(state, Location::from_call_stack(state));
}
state->raise_exception(exc.exception);
return NULL;
}
@@ -547,8 +550,9 @@ Object* MachineCode::debugger_interpreter_continue(STATE,
call_frame->scope->flush_to_heap(state);
return NULL;
} catch(const RubyException& exc) {
exc.exception->locations(state,
Location::from_call_stack(state));
if(exc.exception->locations()->nil_p()) {
exc.exception->locations(state, Location::from_call_stack(state));
}
state->raise_exception(exc.exception);
return NULL;
}
1 change: 1 addition & 0 deletions machine/machine_threads.cpp
Original file line number Diff line number Diff line change
@@ -19,6 +19,7 @@ namespace rubinius {
, thread_running_(false)
, thread_exit_(false)
{
vm_->set_kind(memory::ManagedThread::eSystem);
state->shared().machine_threads()->register_thread(this);
}

13 changes: 0 additions & 13 deletions machine/memory.cpp
Original file line number Diff line number Diff line change
@@ -603,7 +603,6 @@ namespace rubinius {
{
memory::GCData data(state->vm());

clear_fiber_marks(&data);
immix_->collect(&data);
collect_full_finish(state, &data);
}
@@ -732,18 +731,6 @@ namespace rubinius {
handles->deallocate_handles(cached, mark(), young);
}

void Memory::clear_fiber_marks(memory::GCData* data) {
std::lock_guard<std::mutex> guard(data->thread_nexus()->threads_mutex());

for(ThreadList::iterator i = data->thread_nexus()->threads()->begin();
i != data->thread_nexus()->threads()->end();
++i) {
if(VM* vm = (*i)->as_vm()) {
vm->gc_fiber_clear_mark();
}
}
}

void Memory::add_type_info(TypeInfo* ti) {
utilities::thread::SpinLock::LockGuard guard(shared_.type_info_lock());

1 change: 0 additions & 1 deletion machine/memory.hpp
Original file line number Diff line number Diff line change
@@ -537,7 +537,6 @@ namespace rubinius {

void validate_handles(capi::Handles* handles);
void prune_handles(capi::Handles* handles, std::list<capi::Handle*>* cached, /* BakerGC */ void* young);
void clear_fiber_marks(memory::GCData* data);

ObjectPosition validate_object(Object* obj);

13 changes: 0 additions & 13 deletions machine/memory/gc.cpp
Original file line number Diff line number Diff line change
@@ -373,19 +373,6 @@ namespace memory {
}
}

void GarbageCollector::scan_fibers(GCData* data, bool marked_only) {
std::lock_guard<std::mutex> guard(data->thread_nexus()->threads_mutex());

for(ThreadList::iterator i = data->thread_nexus()->threads()->begin();
i != data->thread_nexus()->threads()->end();
++i)
{
if(VM* vm = (*i)->as_vm()) {
vm->gc_fiber_scan(this, marked_only);
}
}
}

void GarbageCollector::clean_weakrefs(bool check_forwards) {
if(!weak_refs_) return;

1 change: 0 additions & 1 deletion machine/memory/gc.hpp
Original file line number Diff line number Diff line change
@@ -151,7 +151,6 @@ namespace memory {
return obj;
}

void scan_fibers(GCData* data, bool marked_only = true);
void clean_weakrefs(bool check_forwards=false);
void clean_locked_objects(ManagedThread* thr, bool young_only);

3 changes: 0 additions & 3 deletions machine/memory/immix_collector.cpp
Original file line number Diff line number Diff line change
@@ -310,9 +310,6 @@ namespace memory {
// objects through weakrefs.
clean_weakrefs();

scan_fibers(data, true);
process_mark_stack();

if(FinalizerThread* finalizer = memory_->finalizer()) {
std::lock_guard<std::mutex> guard(finalizer->list_mutex());

18 changes: 17 additions & 1 deletion machine/memory/managed.hpp
Original file line number Diff line number Diff line change
@@ -21,7 +21,8 @@ namespace memory {
class ManagedThread {
public:
enum Kind {
eRuby,
eThread,
eFiber,
eSystem
};

@@ -87,10 +88,25 @@ namespace memory {
}
}

const char* kind_name() const {
switch(kind_) {
case eThread:
return "Thread";
case eFiber:
return "Fiber";
case eSystem:
return "MachineThread";
}
}

Kind kind() const {
return kind_;
}

void set_kind(Kind kind) {
kind_ = kind;
}

VM* as_vm() {
return reinterpret_cast<VM*>(this);
}
19 changes: 10 additions & 9 deletions machine/oop.cpp
Original file line number Diff line number Diff line change
@@ -10,6 +10,7 @@
#include "builtin/object.hpp"
#include "builtin/class.hpp"
#include "builtin/exception.hpp"
#include "builtin/thread.hpp"

#include "capi/handles.hpp"
#include "memory/inflated_headers.hpp"
@@ -351,7 +352,7 @@ namespace rubinius {
}

// wonderful! Locked! weeeee!
state->vm()->add_locked_object(self);
state->vm()->thread()->vm()->add_locked_object(self);
return eLocked;
}

@@ -425,7 +426,7 @@ namespace rubinius {
// the whole locking procedure again.
OnStack<1> os(state, self);
if(!state->memory()->inflate_and_lock(state, self)) goto step1;
state->vm()->add_locked_object(self);
state->vm()->thread()->vm()->add_locked_object(self);
return eLocked;
}
case eAuxWordInflated: {
@@ -468,7 +469,7 @@ namespace rubinius {

if(self->header.atomic_set(orig, new_val)) {
// wonderful! Locked! weeeee!
state->vm()->add_locked_object(self);
state->vm()->thread()->vm()->add_locked_object(self);
return eLocked;
}

@@ -574,7 +575,7 @@ namespace rubinius {
unsigned int locker_tid = header.f.aux_word >> cAuxLockTIDShift;

if(locker_tid == state->vm()->thread_id()) {
state->vm()->del_locked_object(this);
state->vm()->thread()->vm()->del_locked_object(this);
}
}
// Fall through
@@ -623,7 +624,7 @@ namespace rubinius {
// consistent.
if(!state->memory()->inflate_for_contention(state, this)) continue;

state->vm()->del_locked_object(this);
state->vm()->thread()->vm()->del_locked_object(this);
state->memory()->release_contention(state);

return eUnlocked;
@@ -641,7 +642,7 @@ namespace rubinius {
if(new_val.f.meaning == eAuxWordEmpty) {
// Since we no longer have any association with this lock,
// remove it from the current threads lock list
state->vm()->del_locked_object(this);
state->vm()->thread()->vm()->del_locked_object(this);

if(cDebugThreading) {
if(new_val.f.LockContended == 1) {
@@ -875,7 +876,7 @@ namespace rubinius {
// OWNED.

owner_id_ = state->vm()->thread_id();
state->vm()->add_locked_object(obj);
state->vm()->thread()->vm()->add_locked_object(obj);

if(cDebugThreading) {
std::cerr << "[LOCK " << state->vm()->thread_id() << " locked inflated header: " << this << "]\n";
@@ -914,7 +915,7 @@ namespace rubinius {
} else if(owner_id_ == 0) {
owner_id_ = state->vm()->thread_id();
locked = true;
state->vm()->add_locked_object(obj);
state->vm()->thread()->vm()->add_locked_object(obj);

// OWNED.

@@ -952,7 +953,7 @@ namespace rubinius {
// If the count has dropped to 0, we're truly done, so tell anyone
// blocking on mutex_.
if(rec_lock_count_ == 0) {
state->vm()->del_locked_object(obj);
state->vm()->thread()->vm()->del_locked_object(obj);

owner_id_ = 0;
if(cDebugThreading) {
8 changes: 4 additions & 4 deletions machine/park.cpp
Original file line number Diff line number Diff line change
@@ -15,7 +15,7 @@ namespace rubinius {

wake_ = false;
sleeping_ = true;
state->vm()->thread->sleep(state, cTrue);
state->vm()->thread()->sleep(state, cTrue);

Object* result = cNil;
while(!wake_) {
@@ -34,7 +34,7 @@ namespace rubinius {
}

sleeping_ = false;
state->vm()->thread->sleep(state, cFalse);
state->vm()->thread()->sleep(state, cFalse);
return result;
}

@@ -44,7 +44,7 @@ namespace rubinius {

wake_ = false;
sleeping_ = true;
state->vm()->thread->sleep(state, cTrue);
state->vm()->thread()->sleep(state, cTrue);

Object* timeout = cFalse;

@@ -68,7 +68,7 @@ namespace rubinius {
}

sleeping_ = false;
state->vm()->thread->sleep(state, cFalse);
state->vm()->thread()->sleep(state, cFalse);

return timeout;
}
48 changes: 46 additions & 2 deletions machine/shared_state.cpp
Original file line number Diff line number Diff line change
@@ -117,8 +117,9 @@ namespace rubinius {
++i)
{
if(VM* vm = (*i)->as_vm()) {
Thread *thread = vm->thread.get();
if(!thread->nil_p() && CBOOL(thread->alive())) {
Thread *thread = vm->thread();
if(vm->kind() == memory::ManagedThread::eThread
&&!thread->nil_p() && CBOOL(thread->alive())) {
threads->append(state, thread);
}
}
@@ -127,6 +128,49 @@ namespace rubinius {
return threads;
}

Array* SharedState::vm_fibers(STATE) {
std::lock_guard<std::mutex> guard(thread_nexus_->threads_mutex());

Array* fibers = Array::create(state, 0);

for(ThreadList::iterator i = thread_nexus_->threads()->begin();
i != thread_nexus_->threads()->end();
++i)
{
if(VM* vm = (*i)->as_vm()) {
if(vm->kind() == memory::ManagedThread::eFiber
&& !vm->fiber()->nil_p()
&& vm->fiber()->status() != Fiber::eDead) {
fibers->append(state, vm->fiber());
}
}
}

return fibers;
}

Array* SharedState::vm_thread_fibers(STATE, Thread* thread) {
std::lock_guard<std::mutex> guard(thread_nexus_->threads_mutex());

Array* fibers = Array::create(state, 0);

for(ThreadList::iterator i = thread_nexus_->threads()->begin();
i != thread_nexus_->threads()->end();
++i)
{
if(VM* vm = (*i)->as_vm()) {
if(vm->kind() == memory::ManagedThread::eFiber
&& !vm->fiber()->nil_p()
&& vm->fiber()->status() != Fiber::eDead
&& vm->fiber()->thread() == thread) {
fibers->append(state, vm->fiber());
}
}
}

return fibers;
}

double SharedState::run_time() {
return timer::time_elapsed_seconds(start_time_);
}
3 changes: 2 additions & 1 deletion machine/shared_state.hpp
Original file line number Diff line number Diff line change
@@ -58,7 +58,6 @@ namespace rubinius {
class State;
class VM;
class Configuration;
class LLVMState;
class QueryAgent;
class Environment;

@@ -167,6 +166,8 @@ namespace rubinius {
}

Array* vm_threads(STATE);
Array* vm_fibers(STATE);
Array* vm_thread_fibers(STATE, Thread* thread);

int global_serial() const {
return global_serial_;
6 changes: 4 additions & 2 deletions machine/signal.cpp
Original file line number Diff line number Diff line change
@@ -70,7 +70,9 @@ namespace rubinius {
}

VM* SignalThread::new_vm(STATE) {
return state->shared().thread_nexus()->new_vm(&state->shared(), "rbx.system");
VM* vm = state->shared().thread_nexus()->new_vm(&state->shared(), "rbx.system");
vm->set_kind(memory::ManagedThread::eSystem);
return vm;
}

void SignalThread::set_exit_code(Object* exit_code) {
@@ -308,7 +310,7 @@ namespace rubinius {

while(frame) {
if(first) {
logger::fatal("--- Thread %d backtrace ---", vm->thread_id());
logger::fatal("--- %s %d backtrace ---", vm->kind_name(), vm->thread_id());
first = false;
}

3 changes: 2 additions & 1 deletion machine/stack_variables.cpp
Original file line number Diff line number Diff line change
@@ -6,6 +6,7 @@

#include "builtin/fiber.hpp"
#include "builtin/lookup_table.hpp"
#include "builtin/thread.hpp"
#include "builtin/variable_scope.hpp"

namespace rubinius {
@@ -31,7 +32,7 @@ namespace rubinius {
scope->method(state, call_frame->compiled_code);
scope->heap_locals(state, nil<Tuple>());
scope->last_match(state, last_match_);
scope->fiber(state, state->vm()->current_fiber.get());
scope->fiber(state, state->vm()->thread()->current_fiber());

scope->number_of_locals(mcode->number_of_locals);
scope->isolated(0);
4 changes: 0 additions & 4 deletions machine/state.hpp
Original file line number Diff line number Diff line change
@@ -50,10 +50,6 @@ namespace rubinius {
vm_ = vm;
}

void set_call_site_information(CallSiteInformation* info) {
vm_->set_call_site_information(info);
}

Globals& globals() {
return shared_.globals;
}
4 changes: 2 additions & 2 deletions machine/test/test.hpp
Original file line number Diff line number Diff line change
@@ -44,8 +44,8 @@ class VMTest {
// Setup the main Thread, which is wrapper of the main native thread
// when the VM boots.
Thread::create(&state, vm);
vm->thread->alive(&state, cTrue);
vm->thread->sleep(&state, cFalse);
vm->thread()->alive(&state, cTrue);
vm->thread()->sleep(&state, cFalse);
}

void create() {
2 changes: 1 addition & 1 deletion machine/test/test_thread.hpp
Original file line number Diff line number Diff line change
@@ -16,7 +16,7 @@ class TestThread : public CxxTest::TestSuite, public VMTest {
}

void test_current() {
TS_ASSERT_EQUALS(Thread::current(state), state->vm()->thread.get());
TS_ASSERT_EQUALS(Thread::current(state), state->vm()->thread());
}

void test_create() {
10 changes: 6 additions & 4 deletions machine/thread_nexus.cpp
Original file line number Diff line number Diff line change
@@ -93,10 +93,12 @@ namespace rubinius {
if(VM* vm = (*i)->as_vm()) {
if(vm == current) continue;

if(Thread* thread = vm->thread.get()) {
if(!thread->nil_p()) {
thread->unlock_after_fork(state);
thread->stopped();
if(vm->kind() == memory::ManagedThread::eThread) {
if(Thread* thread = vm->thread()) {
if(!thread->nil_p()) {
thread->unlock_after_fork(state);
thread->stopped();
}
}
}

90 changes: 32 additions & 58 deletions machine/vm.cpp
Original file line number Diff line number Diff line change
@@ -10,25 +10,25 @@
#include "object_utils.hpp"

#include "builtin/array.hpp"
#include "builtin/array.hpp"
#include "builtin/call_site.hpp"
#include "builtin/channel.hpp"
#include "builtin/class.hpp"
#include "builtin/exception.hpp"
#include "builtin/fiber.hpp"
#include "builtin/fixnum.hpp"
#include "builtin/array.hpp"
#include "builtin/jit.hpp"
#include "builtin/list.hpp"
#include "builtin/location.hpp"
#include "builtin/lookup_table.hpp"
#include "builtin/native_method.hpp"
#include "builtin/string.hpp"
#include "builtin/symbol.hpp"
#include "builtin/system.hpp"
#include "builtin/thread.hpp"
#include "builtin/tuple.hpp"
#include "builtin/string.hpp"
#include "builtin/system.hpp"
#include "builtin/fiber.hpp"
#include "builtin/location.hpp"
#include "builtin/native_method.hpp"
#include "builtin/channel.hpp"
#include "builtin/call_site.hpp"
#include "builtin/exception.hpp"
#include "builtin/jit.hpp"
#include "variable_scope.hpp"

#include "variable_scope.hpp"
#include "config_parser.hpp"
#include "config.h"

@@ -59,21 +59,20 @@

namespace rubinius {
VM::VM(uint32_t id, SharedState& shared, const char* name)
: memory::ManagedThread(id, shared, memory::ManagedThread::eRuby, name)
: memory::ManagedThread(id, shared, memory::ManagedThread::eThread, name)
, call_frame_(NULL)
, thread_nexus_(shared.thread_nexus())
, saved_call_site_information_(NULL)
, fiber_stacks_(this, shared)
, park_(new Park)
, stack_start_(0)
, stack_size_(0)
, stack_cushion_(shared.config.machine_stack_cushion.value)
, current_stack_start_(0)
, current_stack_size_(0)
, interrupt_with_signal_(false)
, interrupt_by_kill_(false)
, check_local_interrupts_(false)
, thread_step_(false)
, wait_mutex_()
, wait_condition_()
, transition_flag_(eSuspending)
, interrupt_lock_()
, method_missing_reason_(eNone)
, constant_missing_reason_(vFound)
@@ -90,9 +89,8 @@ namespace rubinius {
, shared(shared)
, waiting_channel_(this, nil<Channel>())
, interrupted_exception_(this, nil<Exception>())
, thread(this, nil<Thread>())
, current_fiber(this, nil<Fiber>())
, root_fiber(this, nil<Fiber>())
, thread_(this, nil<Thread>())
, fiber_(this, nil<Fiber>())
, waiting_object_(this, cNil)
, start_time_(0)
, native_method_environment(NULL)
@@ -126,6 +124,14 @@ namespace rubinius {
delete vm;
}

void VM::set_thread(Thread* thread) {
thread_.set(thread);
}

void VM::set_fiber(Fiber* fiber) {
fiber_.set(fiber);
}

void VM::set_start_time() {
start_time_ = get_current_time();
}
@@ -134,15 +140,6 @@ namespace rubinius {
return timer::time_elapsed_seconds(start_time_);
}

void VM::set_stack_bounds(size_t size) {
void* stack_address;

stack_size_ = size;
stack_start_ = &stack_address;

set_stack_bounds(stack_start_, stack_size_);
}

void VM::raise_stack_error(STATE) {
state->raise_stack_error(state);
}
@@ -180,9 +177,7 @@ namespace rubinius {
}

if(interrupt_by_kill()) {
Fiber* fib = current_fiber.get();

if(fib->nil_p() || fib->root_p()) {
if(state->vm()->thread()->current_fiber()->root_p()) {
clear_interrupt_by_kill();
} else {
set_check_local_interrupts();
@@ -392,7 +387,8 @@ namespace rubinius {

void VM::set_zombie(STATE) {
state->shared().thread_nexus()->delete_vm(this);
thread.set(nil<Thread>());
set_thread(nil<Thread>());
set_fiber(nil<Fiber>());
zombie_ = true;
}

@@ -536,7 +532,7 @@ namespace rubinius {
void VM::wait_on_channel(Channel* chan) {
utilities::thread::SpinLock::LockGuard guard(interrupt_lock_);

thread->sleep(this, cTrue);
thread()->sleep(this, cTrue);
waiting_channel_.set(chan);
}

@@ -554,11 +550,11 @@ namespace rubinius {
}

void VM::set_sleeping() {
thread->sleep(this, cTrue);
thread()->sleep(this, cTrue);
}

void VM::clear_sleeping() {
thread->sleep(this, cFalse);
thread()->sleep(this, cFalse);
}

void VM::reset_parked() {
@@ -577,22 +573,8 @@ namespace rubinius {
set_check_local_interrupts();
}

void VM::set_current_fiber(Fiber* fib) {
if(fib->root_p()) {
restore_stack_bounds();
} else {
set_stack_bounds(fib->data()->stack_start(), fib->data()->stack_size());
}

current_fiber.set(fib);
}

memory::VariableRootBuffers& VM::current_root_buffers() {
if(current_fiber->nil_p() || current_fiber->root_p()) {
return variable_root_buffers();
}

return current_fiber->variable_root_buffers();
return variable_root_buffers();
}

void VM::gc_scan(memory::GarbageCollector* gc) {
@@ -605,14 +587,6 @@ namespace rubinius {
}
}

void VM::gc_fiber_clear_mark() {
fiber_stacks_.gc_clear_mark();
}

void VM::gc_fiber_scan(memory::GarbageCollector* gc, bool only_marked) {
fiber_stacks_.gc_scan(gc, only_marked);
}

void VM::gc_verify(memory::GarbageCollector* gc) {
gc->verify_call_frame(call_frame_);
}
155 changes: 88 additions & 67 deletions machine/vm.hpp
Original file line number Diff line number Diff line change
@@ -19,29 +19,21 @@
#include "shared_state.hpp"

#include "unwind_info.hpp"
#include "fiber_stack.hpp"

#include "sodium/randombytes.h"

#include <atomic>
#include <condition_variable>
#include <mutex>
#include <regex>
#include <string>
#include <vector>
#include <setjmp.h>
#include <stdint.h>

namespace llvm {
class Module;
}

namespace rbxti {
class Env;
}

namespace rubinius {

class Fiber;
class Exception;
class LLVMState;

namespace event {
class Loop;
@@ -58,7 +50,6 @@ namespace rubinius {
class CompiledCode;
class ConfigParser;
class Configuration;
class Fiber;
class GlobalCache;
class LookupTable;
class Memory;
@@ -102,22 +93,30 @@ namespace rubinius {

CallFrame* call_frame_;
ThreadNexus* thread_nexus_;
CallSiteInformation* saved_call_site_information_;
FiberStacks fiber_stacks_;
Park* park_;

void* stack_start_;
size_t stack_size_;
size_t stack_cushion_;

void* current_stack_start_;
size_t current_stack_size_;

bool interrupt_with_signal_;
bool interrupt_by_kill_;
bool check_local_interrupts_;
bool thread_step_;

std::mutex wait_mutex_;
std::condition_variable wait_condition_;

enum FiberTransition {
eSuspending,
eSuspended,
eResuming,
eRunning,
eFinished
};

std::atomic<FiberTransition> transition_flag_;

utilities::thread::SpinLock interrupt_lock_;

MethodMissingReason method_missing_reason_;
@@ -143,13 +142,8 @@ namespace rubinius {
memory::TypedRoot<Channel*> waiting_channel_;
memory::TypedRoot<Exception*> interrupted_exception_;
/// The Thread object for this VM state
memory::TypedRoot<Thread*> thread;

/// The current fiber running on this thread
memory::TypedRoot<Fiber*> current_fiber;

/// Root fiber, if any (lazily initialized)
memory::TypedRoot<Fiber*> root_fiber;
memory::TypedRoot<Thread*> thread_;
memory::TypedRoot<Fiber*> fiber_;

/// Object that waits for inflation
memory::TypedRoot<Object*> waiting_object_;
@@ -189,6 +183,69 @@ namespace rubinius {
return interrupt_lock_;
}

std::mutex& wait_mutex() {
return wait_mutex_;
}

std::condition_variable& wait_condition() {
return wait_condition_;
}

FiberTransition transition_flag() {
return transition_flag_;
}

bool suspending_p() const {
return transition_flag_ == eSuspending;
}

bool suspended_p() const {
return transition_flag_ == eSuspended;
}

bool resuming_p() const {
return transition_flag_ == eResuming;
}

bool running_p() const {
return transition_flag_ == eRunning;
}

bool finished_p() const {
return transition_flag_ == eFinished;
}

void set_suspending() {
transition_flag_ = eSuspending;
}

void set_suspended() {
transition_flag_ = eSuspended;
}

void set_resuming() {
transition_flag_ = eResuming;
}

void set_running() {
transition_flag_ = eRunning;
}

void set_finished() {
transition_flag_ = eFinished;
}

void set_thread(Thread* thread);
void set_fiber(Fiber* fiber);

Thread* thread() {
return thread_.get();
}

Fiber* fiber() {
return fiber_.get();
}

void set_zombie(STATE);

bool zombie_p() {
@@ -218,29 +275,24 @@ namespace rubinius {
void validate_stack_size(STATE, size_t size);

size_t stack_size() {
return current_stack_size_;
return stack_size_;
}

void restore_stack_bounds() {
current_stack_start_ = stack_start_;
current_stack_size_ = stack_size_;
}
void set_stack_bounds(size_t size) {
void* stack_address;

void set_stack_bounds(void* start, size_t size) {
current_stack_start_ = start;
current_stack_size_ = size - stack_cushion_;
stack_size_ = size - stack_cushion_;
stack_start_ = &stack_address;
}

void set_stack_bounds(size_t size);

bool check_stack(STATE, void* stack_address) {
ssize_t stack_used =
(reinterpret_cast<intptr_t>(current_stack_start_)
(reinterpret_cast<intptr_t>(stack_start_)
- reinterpret_cast<intptr_t>(stack_address));

if(stack_used < 0) stack_used = -stack_used;

if(static_cast<size_t>(stack_used) > current_stack_size_) {
if(static_cast<size_t>(stack_used) > stack_size_) {
raise_stack_error(state);
return false;
}
@@ -284,14 +336,6 @@ namespace rubinius {

bool scope_valid_p(VariableScope* scope);

void set_call_site_information(CallSiteInformation* info) {
saved_call_site_information_ = info;
}

CallSiteInformation* saved_call_site_information() {
return saved_call_site_information_;
}

GlobalCache* global_cache() const {
return shared.global_cache;
}
@@ -376,22 +420,6 @@ namespace rubinius {
allocation_tracking_ = false;
}

FiberStack* allocate_fiber_stack(size_t stack_size) {
return fiber_stacks_.allocate(stack_size);
}

void* fiber_trampoline() {
return fiber_stacks_.trampoline();
}

FiberData* new_fiber_data(size_t stack_size, bool root=false) {
return fiber_stacks_.new_data(stack_size, root);
}

void remove_fiber_data(FiberData* data) {
fiber_stacks_.remove_data(data);
}

memory::VariableRootBuffers& current_root_buffers();

public:
@@ -478,8 +506,6 @@ namespace rubinius {
void initialize_platform_data(STATE);
Object* ruby_lib_version();

void set_current_fiber(Fiber* fib);

TypeInfo* find_type(int type);

static void init_ffi(STATE);
@@ -494,9 +520,6 @@ namespace rubinius {

Object* path2class(const char* name);

llvm::Module* llvm_module();
void llvm_cleanup();

void print_backtrace();

void wait_on_channel(Channel* channel);
@@ -518,8 +541,6 @@ namespace rubinius {
void register_kill(STATE);

void gc_scan(memory::GarbageCollector* gc);
void gc_fiber_clear_mark();
void gc_fiber_scan(memory::GarbageCollector* gc, bool only_marked = true);
void gc_verify(memory::GarbageCollector* gc);
};
}
8 changes: 8 additions & 0 deletions machine/vm_thread_state.cpp
Original file line number Diff line number Diff line change
@@ -36,6 +36,14 @@ namespace rubinius {
throw_dest_.set(thread_state->throw_dest());
}

void VMThreadState::set_state(VMThreadState* thread_state) {
raise_reason_ = thread_state->raise_reason();
current_exception_.set(thread_state->current_exception());
raise_value_.set(thread_state->raise_value());
destination_scope_.set(thread_state->destination_scope());
throw_dest_.set(thread_state->throw_dest());
}

void VMThreadState::clear() {
raise_value_.set(cNil);
raise_reason_ = cNone;
1 change: 1 addition & 0 deletions machine/vm_thread_state.hpp
Original file line number Diff line number Diff line change
@@ -53,6 +53,7 @@ namespace rubinius {

ThreadState* state_as_object(STATE);
void set_state(STATE, ThreadState* obj);
void set_state(VMThreadState* thread_state);

void raise_exception(Exception* exc);
void raise_return(Object* value, VariableScope* dest);
23 changes: 4 additions & 19 deletions spec/default.mspec
Original file line number Diff line number Diff line change
@@ -27,23 +27,8 @@ class MSpecScript

# Enable language features
MSpec.enable_feature :fork

rbx = defined?(RUBY_ENGINE) and RUBY_ENGINE == "rbx"

if RUBY_VERSION >= "1.9" or rbx
MSpec.enable_feature :require_19
end

if RUBY_VERSION >= "1.9"
MSpec.enable_feature :encoding
end

if Object.const_defined?(:Rubinius) && Rubinius.const_get(:Fiber)
if Rubinius::Fiber::ENABLED
::Fiber = Rubinius::Fiber
MSpec.enable_feature :fiber
MSpec.enable_feature :fiber_library
MSpec.enable_feature :generator
end
end
MSpec.enable_feature :encoding
MSpec.enable_feature :fiber
MSpec.enable_feature :fiber_library
MSpec.enable_feature :generator
end
19 changes: 19 additions & 0 deletions spec/ruby/core/fiber/list_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
require File.expand_path('../../../spec_helper', __FILE__)

describe "Fiber.list" do
it "returns an Array" do
Fiber.list.should be_an_instance_of(Array)
end

it "returns non-dead Fibers" do
f1 = Fiber.new { }
f2 = Fiber.new { }

f2.resume

Fiber.list.should include(f1)
Fiber.list.should_not include(f2)

f1.resume
end
end
7 changes: 7 additions & 0 deletions spec/ruby/core/fiber/main_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
require File.expand_path('../../../spec_helper', __FILE__)

describe "Fiber.list" do
it "returns the root Fiber for the thread" do
Fiber.main.should be_an_instance_of(Fiber)
end
end
5 changes: 5 additions & 0 deletions spec/ruby/core/fiber/new_spec.rb
Original file line number Diff line number Diff line change
@@ -5,6 +5,7 @@
it "accepts an optional keyword argument to set the Fiber's stack size" do
size = 81920
f = Fiber.new(stack_size: size) { }
f.resume
f.stack_size.should == size
end

@@ -13,18 +14,21 @@
size.should_receive(:to_int).and_return(81920)

f = Fiber.new(stack_size: size) { }
f.resume
f.stack_size.should == 81920
end

it "creates a fiber from the given block" do
fiber = Fiber.new {}
fiber.resume
fiber.should be_an_instance_of(Fiber)
end

it "creates a fiber from a subclass" do
class MyFiber < Fiber
end
fiber = MyFiber.new {}
fiber.resume
fiber.should be_an_instance_of(MyFiber)
end

@@ -36,6 +40,7 @@ class MyFiber < Fiber
invoked = false
fiber = Fiber.new { invoked = true }
invoked.should be_false
fiber.resume
end

it "closes over lexical environments" do
128 changes: 106 additions & 22 deletions spec/ruby/core/fiber/resume_spec.rb
Original file line number Diff line number Diff line change
@@ -1,39 +1,123 @@
require File.expand_path('../../../spec_helper', __FILE__)
require File.expand_path('../../../shared/fiber/resume', __FILE__)

with_feature :fiber do
describe "Fiber#resume" do
it_behaves_like :fiber_resume, :resume
end
it "can be invoked from the root Fiber" do
fiber = Fiber.new { :fiber }
fiber.resume.should == :fiber
end

it "raises a FiberError if invoked from a different Thread" do
fiber = Fiber.new { }
lambda do
Thread.new do
fiber.resume
end.join
end.should raise_error(FiberError)
fiber.resume
end

describe "Fiber#resume" do
it "returns control to the calling Fiber if called from one" do
fiber1 = Fiber.new { :fiber1 }
fiber2 = Fiber.new { fiber1.resume; :fiber2 }
fiber2.resume.should == :fiber2
end

with_feature :fork do
ruby_bug "redmine #595", "2.1.0" do
it "executes the ensure clause" do
rd, wr = IO.pipe
if Kernel::fork then
wr.close
rd.read.should == "executed"
rd.close
else
rd.close
Fiber.new {
begin
Fiber.yield
ensure
wr.write "executed"
end
}.resume
exit 0
it "passes control to the beginning of the block on first invocation" do
invoked = false
fiber = Fiber.new { invoked = true }
fiber.resume
invoked.should be_true
end

it "returns the last value encountered on first invocation" do
fiber = Fiber.new { false; true }
fiber.resume.should be_true
end

it "runs until the end of the block" do
obj = mock('obj')
obj.should_receive(:do).once
fiber = Fiber.new { 1 + 2; a = "glark"; obj.do }
fiber.resume
end

it "runs until Fiber.yield" do
obj = mock('obj')
obj.should_not_receive(:do)
fiber = Fiber.new { 1 + 2; Fiber.yield; obj.do }
fiber.resume
end

it "resumes from the last call to Fiber.yield on subsequent invocations" do
fiber = Fiber.new { Fiber.yield :first; :second }
fiber.resume.should == :first
fiber.resume.should == :second
end

it "accepts any number of arguments" do
fiber = Fiber.new { |a| }
lambda { fiber.resume(*(1..10).to_a) }.should_not raise_error
end

it "sets the block parameters to its arguments on the first invocation" do
first = mock('first')
first.should_receive(:arg).with(:first).twice
fiber = Fiber.new { |arg| first.arg arg; Fiber.yield; first.arg arg; }
fiber.resume(:first)
fiber.resume(:second)
end

it "raises a FiberError if the Fiber is dead" do
fiber = Fiber.new { true }
fiber.resume
lambda { fiber.resume }.should raise_error(FiberError)
end

it "raises a LocalJumpError if the block includes a return statement" do
fiber = Fiber.new { return; }
lambda { fiber.resume }.should raise_error(LocalJumpError)
end

it "raises a LocalJumpError if the block includes a break statement" do
fiber = Fiber.new { break; }
lambda { fiber.resume }.should raise_error(LocalJumpError)
end

# ruby_bug "redmine #595", "2.1.0"
it "executes the ensure clause" do
rd, wr = IO.pipe

pid = Kernel::fork do
rd.close
f = Fiber.new do
begin
Fiber.yield
ensure
wr.write "executed"
end
end

# The apparent issue is that when Fiber.yield executes, control
# "leaves" the "ensure block" and so the ensure clause should run. But
# control really does NOT leave the ensure block when Fiber.yield
# executes. It merely pauses there. To require ensure to run when a
# Fiber is suspended then makes ensure-in-a-Fiber-context different
# than ensure-in-a-Thread-context and this would be very confusing.
f.resume

# When we execute the second #resume call, the ensure block DOES exit,
# the ensure clause runs. This is Ruby behavior as of 2.3.1.
f.resume

exit 0
end

wr.close
Process.waitpid pid

rd.read.should == "executed"
rd.close
end
end
end
28 changes: 28 additions & 0 deletions spec/ruby/core/fiber/transfer_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
require File.expand_path('../../../spec_helper', __FILE__)

with_feature :fiber do
describe "Fiber#transfer" do
it "can be invoked from the root Fiber" do
fiber = Fiber.new { :fiber }
fiber.transfer.should == :fiber
end

it "returns to the root Fiber when finished" do
f1 = Fiber.new { :fiber_1 }
f2 = Fiber.new { f1.transfer; :fiber_2 }

f2.transfer.should == :fiber_1
f2.transfer.should == :fiber_2
end

it "raises a FiberError if invoked from a different Thread" do
fiber = Fiber.new { }
lambda do
Thread.new do
fiber.transfer
end.join
end.should raise_error(FiberError)
fiber.resume
end
end
end
2 changes: 2 additions & 0 deletions spec/ruby/core/fiber/yield_spec.rb
Original file line number Diff line number Diff line change
@@ -14,11 +14,13 @@
it "returns its arguments to the caller" do
fiber = Fiber.new { true; Fiber.yield :glark; true }
fiber.resume.should == :glark
fiber.resume
end

it "returns nil to the caller if given no arguments" do
fiber = Fiber.new { true; Fiber.yield; true }
fiber.resume.should be_nil
fiber.resume
end

it "returns to the Fiber the value of the #resume call that invoked it" do
Loading

0 comments on commit f5ddf1e

Please sign in to comment.