Skip to content
Draft
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
29 changes: 23 additions & 6 deletions cont.c
Original file line number Diff line number Diff line change
Expand Up @@ -2368,6 +2368,27 @@ rb_fiber_storage_aset(VALUE class, VALUE key, VALUE value)
}
}

static uint32_t
fiber_quantum_value(VALUE val)
{
if (RB_FLOAT_TYPE_P(val)) {
rb_raise(rb_eTypeError, "quantum must be an Integer");
}

VALUE integer = rb_to_int(val);

if (FIXNUM_P(integer)) {
if (FIX2LONG(integer) <= 0) {
rb_raise(rb_eArgError, "quantum must be positive");
}
}
else if (RBIGNUM_NEGATIVE_P(integer)) {
rb_raise(rb_eArgError, "quantum must be positive");
}

return NUM2UINT(integer);
}

static VALUE
fiber_initialize(VALUE self, VALUE proc, struct fiber_pool * fiber_pool, unsigned int blocking, VALUE storage, VALUE quantum)
{
Expand All @@ -2388,9 +2409,7 @@ fiber_initialize(VALUE self, VALUE proc, struct fiber_pool * fiber_pool, unsigne
fiber->stack.pool = fiber_pool;

if (!UNDEF_P(quantum)) {
uint32_t q = NUM2UINT(quantum);
if (q == 0) rb_raise(rb_eArgError, "quantum must be positive");
fiber->cont.saved_ec.quantum = q;
fiber->cont.saved_ec.quantum = fiber_quantum_value(quantum);
}

return self;
Expand Down Expand Up @@ -3020,9 +3039,7 @@ static VALUE
rb_fiber_quantum_set(VALUE self, VALUE val)
{
rb_fiber_t *fiber = fiber_ptr(self);
uint32_t q = NUM2UINT(val);
if (q == 0) rb_raise(rb_eArgError, "quantum must be positive");
fiber->cont.saved_ec.quantum = q;
fiber->cont.saved_ec.quantum = fiber_quantum_value(val);
return val;
}

Expand Down
20 changes: 19 additions & 1 deletion spec/ruby/core/fiber/quantum_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -23,22 +23,40 @@
f.quantum.should == 25_000
end

it "raises ArgumentError when set to 0" do
it "raises ArgumentError when initialized with non-positive values" do
-> { Fiber.new(quantum: 0) { Fiber.yield } }.should raise_error(ArgumentError)
-> { Fiber.new(quantum: -1) { Fiber.yield } }.should raise_error(ArgumentError)
end

it "raises ArgumentError when set to non-positive values" do
f = Fiber.new { Fiber.yield }
-> { f.quantum = 0 }.should raise_error(ArgumentError)
-> { f.quantum = -1 }.should raise_error(ArgumentError)
end

it "raises TypeError when initialized with a non-integer" do
-> { Fiber.new(quantum: 1.5) { Fiber.yield } }.should raise_error(TypeError)
end

it "raises TypeError when set to a non-numeric" do
f = Fiber.new { Fiber.yield }
-> { f.quantum = :big }.should raise_error(TypeError)
-> { f.quantum = nil }.should raise_error(TypeError)
-> { f.quantum = 1.5 }.should raise_error(TypeError)
end

it "raises TypeError when set to a symbol" do
f = Fiber.new { Fiber.yield }
-> { f.quantum = :large }.should raise_error(TypeError)
end

it "raises RangeError when initialized or set beyond the uint32 range" do
f = Fiber.new { Fiber.yield }

-> { Fiber.new(quantum: 2**32) { Fiber.yield } }.should raise_error(RangeError)
-> { f.quantum = 2**32 }.should raise_error(RangeError)
end

it "controls the runtime value at forced preemption" do
# A fiber with a small quantum accumulates less runtime per slot than
# one with a large quantum. Without a scheduler, we verify the quantum
Expand Down
16 changes: 16 additions & 0 deletions test/ruby/test_fiber.rb
Original file line number Diff line number Diff line change
Expand Up @@ -605,8 +605,24 @@ def test_fiber_quantum_set_via_accessor
def test_fiber_quantum_must_be_positive
f = Fiber.new { Fiber.yield }
assert_raise(ArgumentError) { f.quantum = 0 }
assert_raise(ArgumentError) { f.quantum = -1 }
assert_raise(ArgumentError) { Fiber.new(quantum: 0) { Fiber.yield } }
assert_raise(ArgumentError) { Fiber.new(quantum: -1) { Fiber.yield } }
end

def test_fiber_quantum_must_be_integer
f = Fiber.new { Fiber.yield }
# Non-integer types should raise TypeError.
assert_raise(TypeError) { f.quantum = :large }
assert_raise(TypeError) { f.quantum = 1.5 }
assert_raise(TypeError) { Fiber.new(quantum: :large) { Fiber.yield } }
assert_raise(TypeError) { Fiber.new(quantum: 1.5) { Fiber.yield } }
end

def test_fiber_quantum_must_fit_uint32
f = Fiber.new { Fiber.yield }
assert_raise(RangeError) { f.quantum = 2**32 }
assert_raise(RangeError) { Fiber.new(quantum: 2**32) { Fiber.yield } }
end

def test_fiber_runtime_starts_at_zero
Expand Down
Loading