diff --git a/cont.c b/cont.c index c91cae6..532fc3b 100644 --- a/cont.c +++ b/cont.c @@ -116,6 +116,8 @@ static const rb_data_type_t cont_data_type, fiber_data_type; static VALUE rb_cContinuation; static VALUE rb_cFiber; static VALUE rb_eFiberError; +static ID id_default_vm_stacksize; +static ID id_vm_stacksize; #define GetContPtr(obj, ptr) \ TypedData_Get_Struct((obj), rb_context_t, &cont_data_type, (ptr)) @@ -1016,7 +1018,7 @@ fiber_t_alloc(VALUE fibval) } static VALUE -fiber_init(VALUE fibval, VALUE proc) +fiber_init(VALUE fibval, VALUE proc, VALUE vm_stacksize) { rb_fiber_t *fib = fiber_t_alloc(fibval); rb_context_t *cont = &fib->cont; @@ -1030,7 +1032,7 @@ fiber_init(VALUE fibval, VALUE proc) fiber_link_join(fib); - th->stack_size = FIBER_VM_STACK_SIZE; + th->stack_size = NUM2ULONG(vm_stacksize); th->stack = ALLOC_N(VALUE, th->stack_size); th->cfp = (void *)(th->stack + th->stack_size); @@ -1060,16 +1062,32 @@ fiber_init(VALUE fibval, VALUE proc) } /* :nodoc: */ +static VALUE rb_fiber_s_get_default_vm_stacksize(VALUE); + static VALUE -rb_fiber_init(VALUE fibval) +rb_fiber_init(int argc, VALUE *argv, VALUE fibval) { - return fiber_init(fibval, rb_block_proc()); + VALUE opt; + VALUE stacksize; + + rb_scan_args(argc, argv, "0:", &opt); + if (NIL_P(opt) || NIL_P(rb_hash_aref(opt, ID2SYM(id_vm_stacksize)))) + stacksize = rb_fiber_s_get_default_vm_stacksize(rb_obj_class(fibval)); + else + stacksize = rb_to_int(rb_hash_aref(opt, ID2SYM(id_vm_stacksize))); + if (NUM2ULONG(stacksize) < (unsigned long)FIBER_VM_STACK_SIZE) + rb_raise(rb_eArgError, + "Fiber vm stacksize must equal or bigger than %d", + FIBER_VM_STACK_SIZE); + + return fiber_init(fibval, rb_block_proc(), stacksize); } VALUE rb_fiber_new(VALUE (*func)(ANYARGS), VALUE obj) { - return fiber_init(fiber_alloc(rb_cFiber), rb_proc_new(func, obj)); + return fiber_init(fiber_alloc(rb_cFiber), rb_proc_new(func, obj), + rb_fiber_s_get_default_vm_stacksize(rb_cFiber)); } static VALUE @@ -1396,6 +1414,20 @@ rb_fiber_m_transfer(int argc, VALUE *argv, VALUE fib) /* * call-seq: + * fiber.vm_stacksize() -> integer + * + * Returns VM stack size of the fiber. + */ +static VALUE +rb_fiber_vm_stacksize(VALUE fibval) +{ + rb_fiber_t *fib; + GetFiberPtr(fibval, fib); + return ULONG2NUM(fib->cont.saved_thread.stack_size); +} + +/* + * call-seq: * Fiber.yield(args, ...) -> obj * * Yields control back to the context that resumed the fiber, passing @@ -1424,7 +1456,39 @@ rb_fiber_s_current(VALUE klass) return rb_fiber_current(); } +/* + * call-seq: + * Fiber.default_vm_stacksize() -> integer + * + * Returns the default VM stack size. Note that this is a maximum number of + * object in VM stack, not a size in byte. + */ +static VALUE +rb_fiber_s_get_default_vm_stacksize(VALUE klass) +{ + return rb_ivar_get(klass, id_default_vm_stacksize); +} +/* + * call-seq: + * Fiber.default_vm_stacksize = num -> integer + * + * Sets the default VM stack size. + * Returns +num+. + * Raises TypeError if +num+ is not Numeric and + * raises ArgumentError if num is less than FIBER_VM_STACK_SIZE. + */ +static VALUE +rb_fiber_s_set_default_vm_stacksize(VALUE klass, VALUE num) +{ + num = rb_to_int(num); + if (NUM2ULONG(num) < (unsigned long)FIBER_VM_STACK_SIZE) + rb_raise(rb_eArgError, + "Fiber default_vm_stacksize must equal or bigger than %d", + FIBER_VM_STACK_SIZE); + rb_ivar_set(klass, id_default_vm_stacksize, num); + return num; +} /* * Document-class: FiberError @@ -1455,12 +1519,22 @@ Init_Cont(void) SET_MACHINE_STACK_END(&th->machine_stack_end); #endif + id_default_vm_stacksize = rb_intern("default_vm_stacksize"); + id_vm_stacksize = rb_intern("vm_stacksize"); + rb_cFiber = rb_define_class("Fiber", rb_cObject); rb_define_alloc_func(rb_cFiber, fiber_alloc); rb_eFiberError = rb_define_class("FiberError", rb_eStandardError); + rb_ivar_set(rb_cFiber, id_default_vm_stacksize, + INT2NUM(FIBER_VM_STACK_SIZE)); + rb_define_singleton_method(rb_cFiber, "default_vm_stacksize", + rb_fiber_s_get_default_vm_stacksize, 0); + rb_define_singleton_method(rb_cFiber, "default_vm_stacksize=", + rb_fiber_s_set_default_vm_stacksize, 1); rb_define_singleton_method(rb_cFiber, "yield", rb_fiber_s_yield, -1); - rb_define_method(rb_cFiber, "initialize", rb_fiber_init, 0); + rb_define_method(rb_cFiber, "initialize", rb_fiber_init, -1); rb_define_method(rb_cFiber, "resume", rb_fiber_m_resume, -1); + rb_define_method(rb_cFiber, "vm_stacksize", rb_fiber_vm_stacksize, 0); } #if defined __GNUC__ && __GNUC__ >= 4 diff --git a/test/ruby/test_fiber.rb b/test/ruby/test_fiber.rb index ed7ecc7..0248f3c 100644 --- a/test/ruby/test_fiber.rb +++ b/test/ruby/test_fiber.rb @@ -220,5 +220,37 @@ class TestFiber < Test::Unit::TestCase end assert_equal("Can't call on top of Fiber or Thread", error.message, bug5083) end + + def test_default_vm_stacksize + begin + default_vm_stacksize = Fiber.default_vm_stacksize + assert_equal(4*1024, default_vm_stacksize) + Fiber.default_vm_stacksize = 8 * 1024 + assert_equal(8*1024, Fiber.default_vm_stacksize) + assert_raise(TypeError) do + Fiber.default_vm_stacksize = Object.new + end + assert_raise(ArgumentError) do + Fiber.default_vm_stacksize = 0 + end + ensure + if default_vm_stacksize + Fiber.default_vm_stacksize = default_vm_stacksize + end + end + end + + def test_vm_stacksize + fib = Fiber.new { } + assert_equal(Fiber.default_vm_stacksize, fib.vm_stacksize) + fib = Fiber.new(:vm_stacksize => 8*1024) {} + assert_equal(8*1024, fib.vm_stacksize) + assert_raise(TypeError) do + Fiber.new(:vm_stacksize => Object.new) {} + end + assert_raise(ArgumentError) do + Fiber.new(:vm_stacksize => 2 * 1024) {} + end + end end