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: rubinius/rubinius
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: 50aa7356e3c7
Choose a base ref
...
head repository: rubinius/rubinius
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: dfb9c9af5ec7
Choose a head ref
  • 3 commits
  • 8 files changed
  • 1 contributor

Commits on Apr 14, 2015

  1. Removed Thread 'dying' attribute.

    There is no Thread API to put a thread into a 'dying' state, so any such
    possible observation of a thread is a non-deterministic race.
    brixen committed Apr 14, 2015

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    6075425 View commit details

Commits on Apr 17, 2015

  1. Reworked Thread#join.

    brixen committed Apr 17, 2015

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    e55d2ec View commit details
  2. Removed C-API specs for deprecated functions.

    The C-API rb_thread_select and rb_thread_blocking_region functions have been deprecated
    for a long time and finally removed in Ruby 2.2.0 (or trunk). The C-API specs for these
    functions were not compiling properly on 2.2.0 and the specs for rb_thread_blocking_region
    had serious race condition flaws. While Rubinius is not yet 2.2.0 compatible at this point
    and the C-API will continue to have these functions until we tick to 2.2.0 compatibility,
    there's no point wasting time fixing these specs.
    brixen committed Apr 17, 2015

    Verified

    This commit was signed with the committer’s verified signature. The key has expired.
    chris-huxtable Chris Huxtable
    Copy the full SHA
    dfb9c9a View commit details
78 changes: 17 additions & 61 deletions kernel/bootstrap/thread.rb
Original file line number Diff line number Diff line change
@@ -82,11 +82,6 @@ def __context__
Kernel.raise PrimitiveFailure, "Thread#__context__ primitive failed"
end

def native_join
Rubinius.primitive :thread_join
Kernel.raise PrimitiveFailure, "Thread#native_join primitive failed"
end

def mri_backtrace
Rubinius.primitive :thread_mri_backtrace
Kernel.raise PrimitiveFailure, "Thread#mri_backtrace primitive failed"
@@ -194,8 +189,6 @@ def status
if @alive
if @sleep
"sleep"
elsif @dying
"aborting"
else
"run"
end
@@ -206,8 +199,20 @@ def status
end
end

def join(timeout = undefined)
join_inner(timeout) { @alive ? nil : self }
def join(timeout=undefined)
if undefined.equal? timeout or nil.equal? timeout
timeout = nil
else
timeout = Rubinius::Type.coerce_to_float timeout
end

value = Rubinius.invoke_primitive :thread_join, self, timeout

if @exception
Kernel.raise @exception
else
value
end
end

def group
@@ -218,51 +223,6 @@ def add_to_group(group)
@group = group
end

def join_inner(timeout = undefined)
if undefined.equal?(timeout) || nil.equal?(timeout)
timeout = nil
else
timeout = Rubinius::Type.coerce_to_float(timeout)
end
result = nil
Rubinius.lock(self)
begin
if @alive
jc = Rubinius::Channel.new
@joins << jc
Rubinius.unlock(self)
begin
if !timeout
while true
res = jc.receive
# receive returns false if it was a spurious wakeup
break if res != false
end
else
duration = timeout
while true
start = Time.now.to_f
res = jc.receive_timeout duration
# receive returns false if it was a spurious wakeup
break if res != false
elapsed = Time.now.to_f - start
duration -= elapsed
break if duration < 0
end
end
ensure
Rubinius.lock(self)
end
end
Kernel.raise @exception if @exception
result = yield
ensure
Rubinius.unlock(self)
end
result
end
private :join_inner

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

@@ -421,9 +381,7 @@ def __run__
end
end
rescue Exception => e
# I don't really get this, but this is MRI's behavior. If we're dying
# by request, ignore any raised exception.
exception = e # unless @dying
exception = e
ensure
unless exception && (abort_on_exception || Thread.abort_on_exception)
@exception = exception
@@ -445,7 +403,6 @@ def __run__
end

def kill
@dying = true
@sleep = false
Rubinius.synchronize(self) do
kill_prim
@@ -457,9 +414,8 @@ def kill
alias_method :terminate, :kill

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

def active_exception
3 changes: 0 additions & 3 deletions spec/ruby/optional/capi/ext/mri.h
Original file line number Diff line number Diff line change
@@ -10,8 +10,6 @@
#undef HAVE_RB_STR_PTR
#undef HAVE_RB_STR_PTR_READONLY

#undef HAVE_THREAD_BLOCKING_REGION

#ifdef RUBY_VERSION_IS_1_8_EX_1_8_7
#undef HAVE_RB_EXEC_RECURSIVE
#undef HAVE_RBIGNUM_SIGN
@@ -23,7 +21,6 @@
#endif

#ifdef RUBY_VERSION_IS_1_8_EX_1_9
#undef HAVE_RB_THREAD_BLOCKING_REGION
#undef HAVE_RB_RATIONAL
#undef HAVE_RB_RATIONAL1
#undef HAVE_RB_RATIONAL2
2 changes: 0 additions & 2 deletions spec/ruby/optional/capi/ext/rubyspec.h
Original file line number Diff line number Diff line change
@@ -608,15 +608,13 @@

/* Thread */
#define HAVE_RB_THREAD_ALONE 1
#define HAVE_RB_THREAD_BLOCKING_REGION 1
#ifdef RUBY_VERSION_IS_2_0
#define HAVE_RB_THREAD_CALL_WITHOUT_GVL 1
#define HAVE_RB_THREAD_CALL_WITHOUT_GVL2 1
#endif
#define HAVE_RB_THREAD_CURRENT 1
#define HAVE_RB_THREAD_LOCAL_AREF 1
#define HAVE_RB_THREAD_LOCAL_ASET 1
#define HAVE_RB_THREAD_SELECT 1
#define HAVE_RB_THREAD_WAIT_FOR 1
#define HAVE_RB_THREAD_WAKEUP 1
#define HAVE_RB_THREAD_CREATE 1
108 changes: 4 additions & 104 deletions spec/ruby/optional/capi/ext/thread_spec.c
Original file line number Diff line number Diff line change
@@ -5,6 +5,10 @@
#include "ruby.h"
#include "rubyspec.h"

#ifdef RUBY_VERSION_IS_2_0
#include "ruby/thread.h"
#endif

#ifdef __cplusplus
extern "C" {
#endif
@@ -15,74 +19,6 @@ static VALUE thread_spec_rb_thread_alone() {
}
#endif

#ifdef HAVE_RB_THREAD_BLOCKING_REGION
/* This is unblocked by unblock_func(). */
static VALUE blocking_func(void* data) {
int rfd = (int)(size_t)data;
char dummy;
ssize_t rv;

do {
rv = read(rfd, &dummy, 1);
} while (rv == -1 && errno == EINTR);

return (rv == 1) ? Qtrue : Qfalse;
}

static void unblock_func(void *data) {
int wfd = (int)(size_t)data;
char dummy = 0;
ssize_t rv;

do {
rv = write(wfd, &dummy, 1);
} while (rv == -1 && errno == EINTR);
}

/* Returns true if the thread is interrupted. */
static VALUE thread_spec_rb_thread_blocking_region(VALUE self) {
int fds[2];
VALUE ret;

if (pipe(fds) == -1) {
return Qfalse;
}
ret = rb_thread_blocking_region(blocking_func, (void*)(size_t)fds[0],
unblock_func, (void*)(size_t)fds[1]);
close(fds[0]);
close(fds[1]);
return ret;
}

/* This is unblocked by a signal. */
static VALUE blocking_func_for_udf_io(void *data) {
int rfd = (int)(size_t)data;
char dummy;

if (read(rfd, &dummy, 1) == -1 && errno == EINTR) {
return Qtrue;
} else {
return Qfalse;
}
}

/* Returns true if the thread is interrupted. */
static VALUE thread_spec_rb_thread_blocking_region_with_ubf_io(VALUE self) {
int fds[2];
VALUE ret;

if (pipe(fds) == -1) {
return Qfalse;
}

ret = rb_thread_blocking_region(blocking_func_for_udf_io,
(void*)(size_t)fds[0], RUBY_UBF_IO, 0);
close(fds[0]);
close(fds[1]);
return ret;
}
#endif

#ifdef HAVE_RB_THREAD_CALL_WITHOUT_GVL
/* This is unblocked by unblock_func(). */
static void* blocking_gvl_func(void* data) {
@@ -237,32 +173,6 @@ static VALUE thread_spec_rb_thread_local_aset(VALUE self, VALUE thr, VALUE sym,
}
#endif

#ifdef HAVE_RB_THREAD_SELECT
static VALUE thread_spec_rb_thread_select_fd(VALUE self, VALUE fd_num, VALUE msec) {
int fd = NUM2INT(fd_num);
struct timeval tv;
int n;

fd_set read;
FD_ZERO(&read);
FD_SET(fd, &read);
tv.tv_sec = 0;
tv.tv_usec = NUM2INT(msec);

n = rb_thread_select(fd + 1, &read, NULL, NULL, &tv);
if(n == 1 && FD_ISSET(fd, &read)) return Qtrue;
return Qfalse;
}

static VALUE thread_spec_rb_thread_select(VALUE self, VALUE msec) {
struct timeval tv;
tv.tv_sec = 0;
tv.tv_usec = NUM2INT(msec);
rb_thread_select(0, NULL, NULL, NULL, &tv);
return Qnil;
}
#endif

#ifdef HAVE_RB_THREAD_WAKEUP
static VALUE thread_spec_rb_thread_wakeup(VALUE self, VALUE thr) {
return rb_thread_wakeup(thr);
@@ -305,11 +215,6 @@ void Init_thread_spec() {
rb_define_method(cls, "rb_thread_alone", thread_spec_rb_thread_alone, 0);
#endif

#ifdef HAVE_RB_THREAD_BLOCKING_REGION
rb_define_method(cls, "rb_thread_blocking_region", thread_spec_rb_thread_blocking_region, 0);
rb_define_method(cls, "rb_thread_blocking_region_with_ubf_io", thread_spec_rb_thread_blocking_region_with_ubf_io, 0);
#endif

#ifdef HAVE_RB_THREAD_CALL_WITHOUT_GVL
rb_define_method(cls, "rb_thread_call_without_gvl", thread_spec_rb_thread_call_without_gvl, 0);
rb_define_method(cls, "rb_thread_call_without_gvl_with_ubf_io", thread_spec_rb_thread_call_without_gvl_with_ubf_io, 0);
@@ -332,11 +237,6 @@ void Init_thread_spec() {
rb_define_method(cls, "rb_thread_local_aset", thread_spec_rb_thread_local_aset, 3);
#endif

#ifdef HAVE_RB_THREAD_SELECT
rb_define_method(cls, "rb_thread_select_fd", thread_spec_rb_thread_select_fd, 2);
rb_define_method(cls, "rb_thread_select", thread_spec_rb_thread_select, 1);
#endif

#ifdef HAVE_RB_THREAD_WAKEUP
rb_define_method(cls, "rb_thread_wakeup", thread_spec_rb_thread_wakeup, 1);
#endif
67 changes: 0 additions & 67 deletions spec/ruby/optional/capi/thread_spec.rb
Original file line number Diff line number Diff line change
@@ -20,31 +20,6 @@ def call_capi_rb_thread_wakeup
Thread.capi_thread_specs = @t
end

describe "rb_thread_select" do
it "returns true if an fd is ready to read" do
read, write = IO.pipe

@t.rb_thread_select_fd(read.to_i, 0).should == false
write << "1"
@t.rb_thread_select_fd(read.to_i, 0).should == true
end

it "does not block all threads" do
t = Thread.new do
sleep 0.25
ScratchPad.record :inner
end
Thread.pass while t.status and t.status != "sleep"

@t.rb_thread_select(500_000)

t.alive?.should be_false
ScratchPad.recorded.should == :inner

t.join
end
end

describe "rb_thread_wait_for" do
it "sleeps the current thread for the give ammount of time" do
start = Time.now
@@ -110,46 +85,4 @@ def call_capi_rb_thread_wakeup
lambda { thr.join }.should raise_error(NotImplementedError)
end
end

end

describe :rb_thread_blocking_region, :shared => true do
before :each do
@t = CApiThreadSpecs.new
ScratchPad.clear
end

it "runs a C function with the global lock unlocked" do
thr = Thread.new do
@t.send(@method)
end

# Wait until it's blocking...
sleep 1

# Wake it up, causing the unblock function to be run.
thr.wakeup

# Make sure it stopped
thr.join(1).should_not be_nil

# And we got a proper value
thr.value.should be_true
end
end

describe "C-API Thread function" do
describe "rb_thread_blocking_region" do
extended_on :rubinius do
it_behaves_like :rb_thread_blocking_region, :rb_thread_blocking_region_with_ubf_io
it_behaves_like :rb_thread_blocking_region, :rb_thread_blocking_region
end

it_behaves_like :rb_thread_blocking_region, :rb_thread_blocking_region_with_ubf_io
it_behaves_like :rb_thread_blocking_region, :rb_thread_blocking_region

it_behaves_like :rb_thread_blocking_region, :rb_thread_call_without_gvl
it_behaves_like :rb_thread_blocking_region, :rb_thread_call_without_gvl2
end
end

Loading