1
0
Fork 0
mirror of https://github.com/ruby/ruby.git synced 2022-11-09 12:17:21 -05:00

thread_sync.c: Clarify and document the behavior of timeout == 0

[Feature #18982]

Instead of introducing an `exception: false` argument to have `non_block`
return nil rather than raise, we can clearly document that a timeout of 0
immediately returns.

The code is refactored a bit to avoid doing a time calculation in
such case.
This commit is contained in:
Jean Boussier 2022-10-06 15:53:16 +02:00
parent 7db29de008
commit 60defe0a68
Notes: git 2022-10-17 14:56:26 +00:00
4 changed files with 42 additions and 24 deletions

View file

@ -87,6 +87,13 @@ describe :queue_deq, shared: true do
t.join
end
it "immediately returns nil if no item is available and the timeout is 0" do
q = @object.call
q << 1
q.send(@method, timeout: 0).should == 1
q.send(@method, timeout: 0).should == nil
end
it "raise TypeError if timeout is not a valid numeric" do
q = @object.call
-> { q.send(@method, timeout: "1") }.should raise_error(

View file

@ -73,7 +73,13 @@ describe :sizedqueue_enq, shared: true do
t.join
end
it "returns nil if no item is available in time" do
it "returns nil if no space is avialable and timeout is 0" do
q = @object.call(1)
q.send(@method, 1, timeout: 0).should == q
q.send(@method, 2, timeout: 0).should == nil
end
it "returns nil if no space is available in time" do
q = @object.call(1)
q << 1
t = Thread.new {

View file

@ -1029,13 +1029,19 @@ static VALUE
queue_do_pop(VALUE self, struct rb_queue *q, int should_block, VALUE timeout)
{
check_array(self, q->que);
rb_hrtime_t end = queue_timeout2hrtime(timeout);
if (RARRAY_LEN(q->que) == 0) {
if (!should_block) {
rb_raise(rb_eThreadError, "queue empty");
}
if (RTEST(rb_equal(INT2FIX(0), timeout))) {
return Qnil;
}
}
rb_hrtime_t end = queue_timeout2hrtime(timeout);
while (RARRAY_LEN(q->que) == 0) {
if (!should_block) {
rb_raise(rb_eThreadError, "queue empty");
}
else if (queue_closed_p(self)) {
if (queue_closed_p(self)) {
return queue_closed_result(self, q);
}
else {
@ -1232,16 +1238,22 @@ rb_szqueue_max_set(VALUE self, VALUE vmax)
static VALUE
rb_szqueue_push(rb_execution_context_t *ec, VALUE self, VALUE object, VALUE non_block, VALUE timeout)
{
rb_hrtime_t end = queue_timeout2hrtime(timeout);
bool timed_out = false;
struct rb_szqueue *sq = szqueue_ptr(self);
if (queue_length(self, &sq->q) >= sq->max) {
if (RTEST(non_block)) {
rb_raise(rb_eThreadError, "queue full");
}
if (RTEST(rb_equal(INT2FIX(0), timeout))) {
return Qnil;
}
}
rb_hrtime_t end = queue_timeout2hrtime(timeout);
while (queue_length(self, &sq->q) >= sq->max) {
if (RTEST(non_block)) {
rb_raise(rb_eThreadError, "queue full");
}
else if (queue_closed_p(self)) {
break;
if (queue_closed_p(self)) {
raise_closed_queue_error(self);
}
else {
rb_execution_context_t *ec = GET_EC();
@ -1262,18 +1274,11 @@ rb_szqueue_push(rb_execution_context_t *ec, VALUE self, VALUE object, VALUE non_
};
rb_ensure(queue_sleep, (VALUE)&queue_sleep_arg, szqueue_sleep_done, (VALUE)&queue_waiter);
if (!NIL_P(timeout) && rb_hrtime_now() >= end) {
timed_out = true;
break;
return Qnil;
}
}
}
if (queue_closed_p(self)) {
raise_closed_queue_error(self);
}
if (timed_out) return Qnil;
return queue_do_push(self, &sq->q, object);
}

View file

@ -10,7 +10,7 @@ class Thread
# +ThreadError+ is raised.
#
# If +timeout+ seconds have passed and no data is available +nil+ is
# returned.
# returned. If +timeout+ is +0+ it returns immediately.
def pop(non_block = false, timeout: nil)
if non_block && timeout
raise ArgumentError, "can't set a timeout if non_block is enabled"
@ -32,7 +32,7 @@ class Thread
# suspended, and +ThreadError+ is raised.
#
# If +timeout+ seconds have passed and no data is available +nil+ is
# returned.
# returned. If +timeout+ is +0+ it returns immediately.
def pop(non_block = false, timeout: nil)
if non_block && timeout
raise ArgumentError, "can't set a timeout if non_block is enabled"
@ -54,7 +54,7 @@ class Thread
# thread isn't suspended, and +ThreadError+ is raised.
#
# If +timeout+ seconds have passed and no space is available +nil+ is
# returned.
# returned. If +timeout+ is +0+ it returns immediately.
# Otherwise it returns +self+.
def push(object, non_block = false, timeout: nil)
if non_block && timeout