Define Thread::ConditionVariable in thread_sync.rb

It's more consistent with Mutex, but also the `#wait` method
benefits from receiving the execution context instead of having
to call `GET_EC`.
This commit is contained in:
Jean Boussier 2025-12-12 08:55:45 +01:00
parent cf97a14c78
commit 7e7a1db579
Notes: git 2025-12-12 09:08:06 +00:00
2 changed files with 61 additions and 58 deletions

View File

@ -1560,21 +1560,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;
};
@ -1585,65 +1572,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);
@ -1726,13 +1692,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");
}

View File

@ -148,4 +148,48 @@ class Thread
Primitive.rb_mut_sleep(timeout)
end
end
class ConditionVariable
# Document-method: ConditionVariable::new
#
# Creates a new condition variable instance.
def initialize
end
undef_method :initialize_copy
# :nodoc:
def marshal_dump
raise TypeError, "can't dump #{self.class}"
end
# Document-method: Thread::ConditionVariable#signal
#
# Wakes up the first thread in line waiting for this lock.
def signal
Primitive.rb_condvar_signal
end
# Document-method: Thread::ConditionVariable#broadcast
#
# Wakes up all threads waiting for this lock.
def broadcast
Primitive.rb_condvar_broadcast
end
# 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+.
def wait(mutex, timeout=nil)
Primitive.rb_condvar_wait(mutex, timeout)
end
end
end