Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/actions/setup/directories/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ runs:
path: ${{ inputs.srcdir }}
fetch-depth: ${{ inputs.fetch-depth }}

- uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
- uses: actions/cache@a7833574556fa59680c1b7cb190c1735db73ebf0 # v5.0.0
with:
path: ${{ inputs.srcdir }}/.downloaded-cache
key: ${{ runner.os }}-${{ runner.arch }}-downloaded-cache
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/windows.yml
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ jobs:

- name: Restore vcpkg artifact
id: restore-vcpkg
uses: actions/cache/restore@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
uses: actions/cache/restore@a7833574556fa59680c1b7cb190c1735db73ebf0 # v5.0.0
with:
path: src\vcpkg_installed
key: windows-${{ matrix.os }}-vcpkg-${{ hashFiles('src/vcpkg.json') }}
Expand All @@ -100,7 +100,7 @@ jobs:
if: ${{ ! steps.restore-vcpkg.outputs.cache-hit }}

- name: Save vcpkg artifact
uses: actions/cache/save@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
uses: actions/cache/save@a7833574556fa59680c1b7cb190c1735db73ebf0 # v5.0.0
with:
path: src\vcpkg_installed
key: windows-${{ matrix.os }}-vcpkg-${{ hashFiles('src/vcpkg.json') }}
Expand Down
2 changes: 1 addition & 1 deletion NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -275,7 +275,7 @@ The following bundled gems are added.

The following bundled gems are updated.

* minitest 5.26.2
* minitest 5.27.0
* power_assert 3.0.1
* rake 13.3.1
* test-unit 3.7.3
Expand Down
13 changes: 8 additions & 5 deletions ext/-test-/scheduler/scheduler.c
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#include "ruby/ruby.h"
#include "ruby/thread.h"
#include "ruby/io.h"
#include "ruby/fiber/scheduler.h"

/*
Expand All @@ -24,6 +25,7 @@
*/

struct blocking_state {
int notify_descriptor;
volatile int interrupted;
};

Expand All @@ -39,14 +41,14 @@ blocking_operation(void *argument)
{
struct blocking_state *blocking_state = (struct blocking_state *)argument;

while (true) {
struct timeval tv = {1, 0}; // 1 second timeout.
write(blocking_state->notify_descriptor, "x", 1);

while (!blocking_state->interrupted) {
struct timeval tv = {1, 0}; // 1 second timeout.
int result = select(0, NULL, NULL, NULL, &tv);

if (result == -1 && errno == EINTR) {
blocking_state->interrupted = 1;
return NULL;
}

// Otherwise, timeout -> loop again.
Expand All @@ -56,9 +58,10 @@ blocking_operation(void *argument)
}

static VALUE
scheduler_blocking_loop(VALUE self)
scheduler_blocking_loop(VALUE self, VALUE notify)
{
struct blocking_state blocking_state = {
.notify_descriptor = rb_io_descriptor(notify),
.interrupted = 0,
};

Expand All @@ -84,5 +87,5 @@ Init_scheduler(void)
VALUE mBug = rb_define_module("Bug");
VALUE mScheduler = rb_define_module_under(mBug, "Scheduler");

rb_define_module_function(mScheduler, "blocking_loop", scheduler_blocking_loop, 0);
rb_define_module_function(mScheduler, "blocking_loop", scheduler_blocking_loop, 1);
}
2 changes: 1 addition & 1 deletion gems/bundled_gems
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
# - revision: revision in repository-url to test
# if `revision` is not given, "v"+`version` or `version` will be used.

minitest 5.26.2 https://github.com/minitest/minitest
minitest 5.27.0 https://github.com/minitest/minitest
power_assert 3.0.1 https://github.com/ruby/power_assert
rake 13.3.1 https://github.com/ruby/rake
test-unit 3.7.3 https://github.com/test-unit/test-unit
Expand Down
1 change: 1 addition & 0 deletions internal/vm.h
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ const char *rb_type_str(enum ruby_value_type type);
VALUE rb_check_funcall_default(VALUE, ID, int, const VALUE *, VALUE);
VALUE rb_check_funcall_basic_kw(VALUE, ID, VALUE, int, const VALUE*, int);
VALUE rb_yield_1(VALUE val);
VALUE rb_ec_yield(struct rb_execution_context_struct *ec, VALUE val);
VALUE rb_yield_force_blockarg(VALUE values);
VALUE rb_lambda_call(VALUE obj, ID mid, int argc, const VALUE *argv,
rb_block_call_func_t bl_proc, int min_argc, int max_argc,
Expand Down
9 changes: 3 additions & 6 deletions test/-ext-/scheduler/test_interrupt_with_scheduler.rb
Original file line number Diff line number Diff line change
Expand Up @@ -26,23 +26,20 @@ def test_without_handle_interrupt_signal_works
# Yield to the scheduler:
sleep(0)

output.puts "ready"
Bug::Scheduler.blocking_loop
Bug::Scheduler.blocking_loop(output)
end
end

output.close
assert_equal "ready\n", input.gets
assert_equal "x", input.read(1)

sleep 0.1 # Ensure the child is in the blocking loop
# $stderr.puts "Sending interrupt"
Process.kill(:INT, pid)

reaper = Thread.new do
Process.waitpid2(pid)
end

unless reaper.join(1)
unless reaper.join(10)
Process.kill(:KILL, pid)
end

Expand Down
14 changes: 9 additions & 5 deletions test/test_timeout.rb
Original file line number Diff line number Diff line change
Expand Up @@ -427,9 +427,9 @@ def test_timeout_in_trap_handler

rd, wr = IO.pipe

signal = Signal.list["USR1"] ? :USR1 : :TERM
signal = :TERM

trap(signal) do
original_handler = trap(signal) do
begin
Timeout.timeout(0.1) do
sleep 1
Expand All @@ -444,9 +444,13 @@ def test_timeout_in_trap_handler
end
end

Process.kill signal, Process.pid
begin
Process.kill signal, Process.pid

assert_equal "OK", rd.read
rd.close
assert_equal "OK", rd.read
rd.close
ensure
trap(signal, original_handler)
end
end
end
35 changes: 11 additions & 24 deletions thread_pthread.c
Original file line number Diff line number Diff line change
Expand Up @@ -972,33 +972,16 @@ thread_sched_wakeup_next_thread(struct rb_thread_sched *sched, rb_thread_t *th,
}
}

// running -> waiting
//
// to_dead: false
// th will run dedicated task.
// run another ready thread.
// to_dead: true
// th will be dead.
// run another ready thread.
// running -> dead (locked)
static void
thread_sched_to_waiting_common0(struct rb_thread_sched *sched, rb_thread_t *th, bool to_dead)
thread_sched_to_dead_common(struct rb_thread_sched *sched, rb_thread_t *th)
{
RB_INTERNAL_THREAD_HOOK(RUBY_INTERNAL_THREAD_EVENT_SUSPENDED, th);

if (!to_dead) native_thread_dedicated_inc(th->vm, th->ractor, th->nt);
RUBY_DEBUG_LOG("th:%u DNT:%d", rb_th_serial(th), th->nt->dedicated);

RUBY_DEBUG_LOG("%sth:%u", to_dead ? "to_dead " : "", rb_th_serial(th));
RB_INTERNAL_THREAD_HOOK(RUBY_INTERNAL_THREAD_EVENT_SUSPENDED, th);

bool can_switch = to_dead ? !th_has_dedicated_nt(th) : false;
thread_sched_wakeup_next_thread(sched, th, can_switch);
}
thread_sched_wakeup_next_thread(sched, th, !th_has_dedicated_nt(th));

// running -> dead (locked)
static void
thread_sched_to_dead_common(struct rb_thread_sched *sched, rb_thread_t *th)
{
RUBY_DEBUG_LOG("dedicated:%d", th->nt->dedicated);
thread_sched_to_waiting_common0(sched, th, true);
RB_INTERNAL_THREAD_HOOK(RUBY_INTERNAL_THREAD_EVENT_EXITED, th);
}

Expand All @@ -1019,8 +1002,12 @@ thread_sched_to_dead(struct rb_thread_sched *sched, rb_thread_t *th)
static void
thread_sched_to_waiting_common(struct rb_thread_sched *sched, rb_thread_t *th)
{
RUBY_DEBUG_LOG("dedicated:%d", th->nt->dedicated);
thread_sched_to_waiting_common0(sched, th, false);
RUBY_DEBUG_LOG("th:%u DNT:%d", rb_th_serial(th), th->nt->dedicated);

RB_INTERNAL_THREAD_HOOK(RUBY_INTERNAL_THREAD_EVENT_SUSPENDED, th);

native_thread_dedicated_inc(th->vm, th->ractor, th->nt);
thread_sched_wakeup_next_thread(sched, th, false);
}

// running -> waiting
Expand Down
84 changes: 24 additions & 60 deletions thread_sync.c
Original file line number Diff line number Diff line change
Expand Up @@ -650,7 +650,6 @@ rb_mutex_sleep(VALUE self, VALUE timeout)
return rb_mut_sleep(GET_EC(), self, timeout);
}


VALUE
rb_mutex_synchronize(VALUE self, VALUE (*func)(VALUE arg), VALUE arg)
{
Expand All @@ -660,6 +659,12 @@ rb_mutex_synchronize(VALUE self, VALUE (*func)(VALUE arg), VALUE arg)
return rb_ec_ensure(args.ec, func, arg, do_mutex_unlock_safe, (VALUE)&args);
}

static VALUE
do_ec_yield(VALUE _ec)
{
return rb_ec_yield((rb_execution_context_t *)_ec, Qundef);
}

VALUE
rb_mut_synchronize(rb_execution_context_t *ec, VALUE self)
{
Expand All @@ -669,7 +674,7 @@ rb_mut_synchronize(rb_execution_context_t *ec, VALUE self)
.ec = ec,
};
do_mutex_lock(&args, 1);
return rb_ec_ensure(args.ec, rb_yield, Qundef, do_mutex_unlock_safe, (VALUE)&args);
return rb_ec_ensure(args.ec, do_ec_yield, (VALUE)ec, do_mutex_unlock_safe, (VALUE)&args);
}

void
Expand Down Expand Up @@ -1560,21 +1565,8 @@ condvar_alloc(VALUE klass)
return obj;
}

/*
* Document-method: ConditionVariable::new
*
* Creates a new condition variable instance.
*/

static VALUE
rb_condvar_initialize(VALUE self)
{
struct rb_condvar *cv = condvar_ptr(self);
ccan_list_head_init(&cv->waitq);
return self;
}

struct sleep_call {
rb_execution_context_t *ec;
VALUE mutex;
VALUE timeout;
};
Expand All @@ -1585,65 +1577,44 @@ static VALUE
do_sleep(VALUE args)
{
struct sleep_call *p = (struct sleep_call *)args;
return rb_funcallv(p->mutex, id_sleep, 1, &p->timeout);
if (CLASS_OF(p->mutex) == rb_cMutex) {
return rb_mut_sleep(p->ec, p->mutex, p->timeout);
}
else {
return rb_funcallv(p->mutex, id_sleep, 1, &p->timeout);
}
}

/*
* Document-method: Thread::ConditionVariable#wait
* call-seq: wait(mutex, timeout=nil)
*
* Releases the lock held in +mutex+ and waits; reacquires the lock on wakeup.
*
* If +timeout+ is given, this method returns after +timeout+ seconds passed,
* even if no other thread doesn't signal.
*
* This method may wake up spuriously due to underlying implementation details.
*
* Returns the slept result on +mutex+.
*/

static VALUE
rb_condvar_wait(int argc, VALUE *argv, VALUE self)
rb_condvar_wait(rb_execution_context_t *ec, VALUE self, VALUE mutex, VALUE timeout)
{
rb_execution_context_t *ec = GET_EC();

struct rb_condvar *cv = condvar_ptr(self);
struct sleep_call args;

rb_scan_args(argc, argv, "11", &args.mutex, &args.timeout);
struct sleep_call args = {
.ec = ec,
.mutex = mutex,
.timeout = timeout,
};

struct sync_waiter sync_waiter = {
.self = args.mutex,
.self = mutex,
.th = ec->thread_ptr,
.fiber = nonblocking_fiber(ec->fiber_ptr)
};

ccan_list_add_tail(&cv->waitq, &sync_waiter.node);
return rb_ensure(do_sleep, (VALUE)&args, delete_from_waitq, (VALUE)&sync_waiter);
return rb_ec_ensure(ec, do_sleep, (VALUE)&args, delete_from_waitq, (VALUE)&sync_waiter);
}

/*
* Document-method: Thread::ConditionVariable#signal
*
* Wakes up the first thread in line waiting for this lock.
*/

static VALUE
rb_condvar_signal(VALUE self)
rb_condvar_signal(rb_execution_context_t *ec, VALUE self)
{
struct rb_condvar *cv = condvar_ptr(self);
wakeup_one(&cv->waitq);
return self;
}

/*
* Document-method: Thread::ConditionVariable#broadcast
*
* Wakes up all threads waiting for this lock.
*/

static VALUE
rb_condvar_broadcast(VALUE self)
rb_condvar_broadcast(rb_execution_context_t *ec, VALUE self)
{
struct rb_condvar *cv = condvar_ptr(self);
wakeup_all(&cv->waitq);
Expand Down Expand Up @@ -1726,13 +1697,6 @@ Init_thread_sync(void)

id_sleep = rb_intern("sleep");

rb_define_method(rb_cConditionVariable, "initialize", rb_condvar_initialize, 0);
rb_undef_method(rb_cConditionVariable, "initialize_copy");
rb_define_method(rb_cConditionVariable, "marshal_dump", undumpable, 0);
rb_define_method(rb_cConditionVariable, "wait", rb_condvar_wait, -1);
rb_define_method(rb_cConditionVariable, "signal", rb_condvar_signal, 0);
rb_define_method(rb_cConditionVariable, "broadcast", rb_condvar_broadcast, 0);

rb_provide("thread.rb");
}

Expand Down
Loading