From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on dcvr.yhbt.net X-Spam-Level: X-Spam-ASN: X-Spam-Status: No, score=-4.0 required=3.0 tests=ALL_TRUSTED,AWL,BAYES_00 shortcircuit=no autolearn=ham autolearn_force=no version=3.4.2 Received: from localhost (dcvr.yhbt.net [127.0.0.1]) by dcvr.yhbt.net (Postfix) with ESMTP id B4E78211B3 for ; Thu, 29 Nov 2018 21:26:02 +0000 (UTC) From: Eric Wong To: spew@80x24.org Subject: [PATCH] configure: support --disable-callcc Date: Thu, 29 Nov 2018 21:26:02 +0000 Message-Id: <20181129212602.12362-1-e@80x24.org> List-Id: "callcc" increases stack overhead of every rb_ensure call and increases the memory use of every rb_execution_context_t. So allow non-"callcc" users to build a leaner and meaner Ruby. [Feature #10548] --- configure.ac | 6 +++++ cont.c | 43 +++++++++++++++++++++++++++------ eval.c | 9 ++++++- ext/continuation/continuation.c | 8 ++++++ include/ruby/ruby.h | 4 +++ test/lib/envutil.rb | 8 ++++++ test/ruby/test_array.rb | 4 +-- test/ruby/test_beginendblock.rb | 1 + test/ruby/test_continuation.rb | 3 +-- test/ruby/test_enum.rb | 2 +- test/ruby/test_fiber.rb | 15 ++++++------ test/ruby/test_hash.rb | 5 +++- test/ruby/test_marshal.rb | 2 +- test/ruby/test_settracefunc.rb | 20 ++++++++------- test/test_extlibs.rb | 2 +- vm_core.h | 8 ++++++ 16 files changed, 107 insertions(+), 33 deletions(-) diff --git a/configure.ac b/configure.ac index ed88ec15ff1..2406d2e7563 100644 --- a/configure.ac +++ b/configure.ac @@ -2333,6 +2333,12 @@ AS_IF([test "${rb_cv_fiber_coroutine:-no}" != no], [ AC_SUBST(X_FIBER_COROUTINE_H, [$COROUTINE_H]) ]) +AC_ARG_ENABLE(callcc, + AS_HELP_STRING([--disable-callcc], [disable callcc support]), + [rb_cv_callcc=$enableval],[rb_cv_callcc=yes]) +AS_CASE(["$rb_cv_callcc"], + [no], [AC_DEFINE_UNQUOTED(RUBY_SUPPORT_CALLCC, 0)]) + AS_IF([test x"$enable_pthread" = xyes], [ for pthread_lib in thr pthread pthreads c c_r root; do AC_CHECK_LIB($pthread_lib, pthread_create, diff --git a/cont.c b/cont.c index 142713ad842..a3c1c89d284 100644 --- a/cont.c +++ b/cont.c @@ -129,7 +129,9 @@ typedef struct rb_context_struct { } machine; rb_execution_context_t saved_ec; rb_jmpbuf_t jmpbuf; +#if RUBY_SUPPORT_CALLCC rb_ensure_entry_t *ensure_array; +#endif /* Pointer to MJIT info about the continuation. */ struct mjit_cont *mjit_cont; } rb_context_t; @@ -301,11 +303,15 @@ ec_switch(rb_thread_t *th, rb_fiber_t *fib) VM_ASSERT(ec->fiber_ptr->cont.self == 0 || ec->vm_stack != NULL); } -static const rb_data_type_t cont_data_type, fiber_data_type; -static VALUE rb_cContinuation; +static const rb_data_type_t fiber_data_type; static VALUE rb_cFiber; static VALUE rb_eFiberError; +#if RUBY_SUPPORT_CALLCC +static const rb_data_type_t cont_data_type; +static VALUE rb_cContinuation; +NOINLINE(static VALUE cont_capture(volatile int *volatile stat)); + static rb_context_t * cont_ptr(VALUE obj) { @@ -315,6 +321,7 @@ cont_ptr(VALUE obj) return cont; } +#endif /* RUBY_SUPPORT_CALLCC */ static rb_fiber_t * fiber_ptr(VALUE obj) @@ -327,8 +334,6 @@ fiber_ptr(VALUE obj) return fib; } -NOINLINE(static VALUE cont_capture(volatile int *volatile stat)); - #define THREAD_MUST_BE_RUNNING(th) do { \ if (!(th)->ec->tag) rb_raise(rb_eThreadError, "not running thread"); \ } while (0) @@ -403,7 +408,9 @@ cont_free(void *ptr) #if FIBER_USE_NATIVE if (cont->type == CONTINUATION_CONTEXT) { /* cont */ +#if RUBY_SUPPORT_CALLCC ruby_xfree(cont->ensure_array); +#endif RUBY_FREE_UNLESS_NULL(cont->machine.stack); } else { @@ -437,7 +444,9 @@ cont_free(void *ptr) #endif } #else /* not FIBER_USE_NATIVE */ +#if RUBY_SUPPORT_CALLCC ruby_xfree(cont->ensure_array); +#endif RUBY_FREE_UNLESS_NULL(cont->machine.stack); #endif #ifdef __ia64 @@ -556,6 +565,7 @@ rb_obj_is_fiber(VALUE obj) } } +#if FIBER_USE_NATIVE == 0 || RUBY_SUPPORT_CALLCC != 0 static void cont_save_machine_stack(rb_thread_t *th, rb_context_t *cont) { @@ -599,12 +609,15 @@ cont_save_machine_stack(rb_thread_t *th, rb_context_t *cont) MEMCPY(cont->machine.register_stack, cont->machine.register_stack_src, VALUE, size); #endif } +#endif /* FIBER_USE_NATIVE == 0 || RUBY_SUPPORT_CALLCC != 0 */ +#if RUBY_SUPPORT_CALLCC static const rb_data_type_t cont_data_type = { "continuation", {cont_mark, cont_free, cont_memsize,}, 0, 0, RUBY_TYPED_FREE_IMMEDIATELY }; +#endif /* RUBY_SUPPORT_CALLCC */ static inline void cont_save_thread(rb_context_t *cont, rb_thread_t *th) @@ -640,6 +653,7 @@ cont_init(rb_context_t *cont, rb_thread_t *th) } } +#if RUBY_SUPPORT_CALLCC static rb_context_t * cont_new(VALUE klass) { @@ -653,6 +667,7 @@ cont_new(VALUE klass) cont_init(cont, th); return cont; } +#endif /* RUBY_SUPPORT_CALLCC */ #if 0 void @@ -685,6 +700,7 @@ COMPILER_WARNING_PUSH #ifdef __clang__ COMPILER_WARNING_IGNORED(-Wduplicate-decl-specifier) #endif +#if RUBY_SUPPORT_CALLCC static VALUE cont_capture(volatile int *volatile stat) { @@ -747,6 +763,7 @@ cont_capture(volatile int *volatile stat) return contval; } } +#endif /* RUBY_SUPPORT_CALLCC */ COMPILER_WARNING_POP static inline void @@ -756,6 +773,7 @@ fiber_restore_thread(rb_thread_t *th, rb_fiber_t *fib) VM_ASSERT(th->ec->fiber_ptr == fib); } +#if RUBY_SUPPORT_CALLCC static inline void cont_restore_thread(rb_context_t *cont) { @@ -811,6 +829,7 @@ cont_restore_thread(rb_context_t *cont) fiber_restore_thread(th, (rb_fiber_t*)cont); } } +#endif /* RUBY_SUPPORT_CALLCC */ #if FIBER_USE_NATIVE #if defined(FIBER_USE_COROUTINE) @@ -1005,6 +1024,7 @@ fiber_setcontext(rb_fiber_t *newfib, rb_fiber_t *oldfib) } #endif /* FIBER_USE_NATIVE */ +#if RUBY_SUPPORT_CALLCC NOINLINE(NORETURN(static void cont_restore_1(rb_context_t *))); static void @@ -1073,7 +1093,7 @@ register_stack_extend(rb_context_t *cont, VALUE *vp, VALUE *curr_bsp) } #undef C #undef E -#endif +#endif /* __ia64 */ static void cont_restore_0(rb_context_t *cont, VALUE *addr_in_prev_frame) @@ -1126,6 +1146,7 @@ cont_restore_0(rb_context_t *cont, VALUE *addr_in_prev_frame) #ifdef __ia64 #define cont_restore_0(cont, vp) register_stack_extend((cont), (vp), (VALUE*)rb_ia64_bsp()) #endif +#endif /* RUBY_SUPPORT_CALLCC */ /* * Document-class: Continuation @@ -1210,6 +1231,7 @@ cont_restore_0(rb_context_t *cont, VALUE *addr_in_prev_frame) * unwinding a call stack. */ +#if RUBY_SUPPORT_CALLCC static VALUE rb_callcc(VALUE self) { @@ -1223,6 +1245,7 @@ rb_callcc(VALUE self) return rb_yield(val); } } +#endif /* RUBY_SUPPORT_CALLCC */ static VALUE make_passing_arg(int argc, const VALUE *argv) @@ -1242,13 +1265,16 @@ make_passing_arg(int argc, const VALUE *argv) void ruby_register_rollback_func_for_ensure(VALUE (*ensure_func)(ANYARGS), VALUE (*rollback_func)(ANYARGS)) { +#if RUBY_SUPPORT_CALLCC st_table **table_p = &GET_VM()->ensure_rollback_table; if (UNLIKELY(*table_p == NULL)) { *table_p = st_init_numtable(); } st_insert(*table_p, (st_data_t)ensure_func, (st_data_t)rollback_func); +#endif /* RUBY_SUPPORT_CALLCC */ } +#if RUBY_SUPPORT_CALLCC static inline VALUE lookup_rollback_func(VALUE (*ensure_func)(ANYARGS)) { @@ -1310,6 +1336,7 @@ rollback_ensure_stack(VALUE self,rb_ensure_list_t *current,rb_ensure_entry_t *ta } } } +#endif /* RUBY_SUPPORT_CALLCC */ /* * call-seq: @@ -1326,7 +1353,7 @@ rollback_ensure_stack(VALUE self,rb_ensure_list_t *current,rb_ensure_entry_t *ta * callcc {|cont| cont.call 1 } #=> 1 * callcc {|cont| cont.call 1, 2, 3 } #=> [1, 2, 3] */ - +#if RUBY_SUPPORT_CALLCC static VALUE rb_cont_call(int argc, VALUE *argv, VALUE contval) { @@ -1352,6 +1379,7 @@ rb_cont_call(int argc, VALUE *argv, VALUE contval) cont_restore_0(cont, &contval); return Qnil; /* unreachable */ } +#endif /* RUBY_SUPPORT_CALLCC */ /*********/ /* fiber */ @@ -2116,7 +2144,7 @@ Init_Cont(void) } RUBY_SYMBOL_EXPORT_BEGIN - +#if RUBY_SUPPORT_CALLCC void ruby_Init_Continuation_body(void) { @@ -2127,6 +2155,7 @@ ruby_Init_Continuation_body(void) rb_define_method(rb_cContinuation, "[]", rb_cont_call, -1); rb_define_global_function("callcc", rb_callcc, 0); } +#endif /* RUBY_SUPPORT_CALLCC */ void ruby_Init_Fiber_as_Coroutine(void) diff --git a/eval.c b/eval.c index ecb79ccdcb6..88927c0e6e3 100644 --- a/eval.c +++ b/eval.c @@ -1041,12 +1041,16 @@ rb_ensure(VALUE (*b_proc)(ANYARGS), VALUE data1, VALUE (*e_proc)(ANYARGS), VALUE volatile VALUE result = Qnil; VALUE errinfo; rb_execution_context_t * volatile ec = GET_EC(); + +#if RUBY_SUPPORT_CALLCC rb_ensure_list_t ensure_list; ensure_list.entry.marker = 0; ensure_list.entry.e_proc = e_proc; ensure_list.entry.data2 = data2; ensure_list.next = ec->ensure_list; ec->ensure_list = &ensure_list; +#endif + EC_PUSH_TAG(ec); if ((state = EC_EXEC_TAG()) == TAG_NONE) { result = (*b_proc) (data1); @@ -1056,8 +1060,11 @@ rb_ensure(VALUE (*b_proc)(ANYARGS), VALUE data1, VALUE (*e_proc)(ANYARGS), VALUE if (!NIL_P(errinfo) && !RB_TYPE_P(errinfo, T_OBJECT)) { ec->errinfo = Qnil; } +#if RUBY_SUPPORT_CALLCC ec->ensure_list=ensure_list.next; - (*ensure_list.entry.e_proc)(ensure_list.entry.data2); +#endif + (*e_proc)(data2); + ec->errinfo = errinfo; if (state) EC_JUMP_TAG(ec, state); diff --git a/ext/continuation/continuation.c b/ext/continuation/continuation.c index c678371cca5..02485e89a59 100644 --- a/ext/continuation/continuation.c +++ b/ext/continuation/continuation.c @@ -1,7 +1,11 @@ #include "ruby/ruby.h" +#if RUBY_SUPPORT_CALLCC void ruby_Init_Continuation_body(void); +#else +NORETURN(void Init_continuation(void)); +#endif void Init_continuation(void) @@ -9,5 +13,9 @@ Init_continuation(void) #ifndef RUBY_EXPORT rb_warn("callcc is obsolete; use Fiber instead"); #endif +#if RUBY_SUPPORT_CALLCC ruby_Init_Continuation_body(); +#else + rb_raise(rb_eNotImpError, "callcc disabled in this build of Ruby"); +#endif } diff --git a/include/ruby/ruby.h b/include/ruby/ruby.h index 58e28d2c9f8..f07e87cce5e 100644 --- a/include/ruby/ruby.h +++ b/include/ruby/ruby.h @@ -1017,6 +1017,10 @@ struct RString { #define USE_TRANSIENT_HEAP 1 #endif +#ifndef RUBY_SUPPORT_CALLCC +#define RUBY_SUPPORT_CALLCC 1 +#endif + enum ruby_rarray_flags { RARRAY_EMBED_LEN_MAX = 3, RARRAY_EMBED_FLAG = RUBY_FL_USER1, diff --git a/test/lib/envutil.rb b/test/lib/envutil.rb index 5d3bce99ec3..c18a936e584 100644 --- a/test/lib/envutil.rb +++ b/test/lib/envutil.rb @@ -202,6 +202,14 @@ def suppress_warning end module_function :suppress_warning + def support_callcc? + suppress_warning { require 'continuation' } + true + rescue NotImplementedError + false + end + module_function :support_callcc? + def under_gc_stress(stress = true) stress, GC.stress = GC.stress, stress yield diff --git a/test/ruby/test_array.rb b/test/ruby/test_array.rb index aaf3eca58c5..4dc771647f5 100644 --- a/test/ruby/test_array.rb +++ b/test/ruby/test_array.rb @@ -3068,8 +3068,6 @@ def test_sum private def need_continuation - unless respond_to?(:callcc, true) - EnvUtil.suppress_warning {require 'continuation'} - end + skip 'callcc disabled' unless EnvUtil.support_callcc? end end diff --git a/test/ruby/test_beginendblock.rb b/test/ruby/test_beginendblock.rb index 9238a533547..99ccf07486d 100644 --- a/test/ruby/test_beginendblock.rb +++ b/test/ruby/test_beginendblock.rb @@ -126,6 +126,7 @@ def test_rescue_at_exit end def test_callcc_at_exit + skip 'callcc disabled' unless EnvUtil.support_callcc? bug9110 = '[ruby-core:58329][Bug #9110]' assert_ruby_status([], "#{<<~"begin;"}\n#{<<~'end;'}", bug9110) begin; diff --git a/test/ruby/test_continuation.rb b/test/ruby/test_continuation.rb index 8c62d208402..6e73a913c89 100644 --- a/test/ruby/test_continuation.rb +++ b/test/ruby/test_continuation.rb @@ -1,6 +1,5 @@ # frozen_string_literal: false require 'test/unit' -EnvUtil.suppress_warning {require 'continuation'} require 'fiber' class TestContinuation < Test::Unit::TestCase @@ -142,4 +141,4 @@ def test_tracing_with_thread_set_trace_func tracing_with_thread_set_trace_func assert_equal 3, @memo end -end +end if EnvUtil.support_callcc? diff --git a/test/ruby/test_enum.rb b/test/ruby/test_enum.rb index c56e280e061..c354dc9bb35 100644 --- a/test/ruby/test_enum.rb +++ b/test/ruby/test_enum.rb @@ -1,6 +1,5 @@ # frozen_string_literal: false require 'test/unit' -EnvUtil.suppress_warning {require 'continuation'} require 'stringio' class TestEnumerable < Test::Unit::TestCase @@ -677,6 +676,7 @@ def test_cycle end def test_callcc + skip 'callcc disabled' unless EnvUtil.support_callcc? assert_raise(RuntimeError) do c = nil @obj.sort_by {|x| callcc {|c2| c ||= c2 }; x } diff --git a/test/ruby/test_fiber.rb b/test/ruby/test_fiber.rb index 1729147d5e7..40f026470ce 100644 --- a/test/ruby/test_fiber.rb +++ b/test/ruby/test_fiber.rb @@ -1,7 +1,6 @@ # frozen_string_literal: false require 'test/unit' require 'fiber' -EnvUtil.suppress_warning {require 'continuation'} require 'tmpdir' class TestFiber < Test::Unit::TestCase @@ -81,12 +80,14 @@ def test_error f.resume f.resume } - assert_raise(RuntimeError){ - Fiber.new{ - @c = callcc{|c| @c = c} - }.resume - @c.call # cross fiber callcc - } + if EnvUtil.support_callcc? + assert_raise(RuntimeError){ + Fiber.new{ + @c = callcc{|c| @c = c} + }.resume + @c.call # cross fiber callcc + } + end assert_raise(RuntimeError){ Fiber.new{ raise diff --git a/test/ruby/test_hash.rb b/test/ruby/test_hash.rb index 7db3c27f87a..be205431001 100644 --- a/test/ruby/test_hash.rb +++ b/test/ruby/test_hash.rb @@ -1,7 +1,6 @@ # -*- coding: us-ascii -*- # frozen_string_literal: false require 'test/unit' -EnvUtil.suppress_warning {require 'continuation'} class TestHash < Test::Unit::TestCase @@ -1228,6 +1227,7 @@ def test_flatten_arity end def test_callcc + skip 'callcc disabled' unless EnvUtil.support_callcc? h = @cls[1=>2] c = nil f = false @@ -1248,6 +1248,7 @@ def test_callcc end def test_callcc_iter_level + skip 'callcc disabled' unless EnvUtil.support_callcc? bug9105 = '[ruby-dev:47803] [Bug #9105]' h = @cls[1=>2, 3=>4] c = nil @@ -1266,6 +1267,7 @@ def test_callcc_iter_level end def test_callcc_escape + skip 'callcc disabled' unless EnvUtil.support_callcc? bug9105 = '[ruby-dev:47803] [Bug #9105]' assert_nothing_raised(RuntimeError, bug9105) do h=@cls[] @@ -1280,6 +1282,7 @@ def test_callcc_escape end def test_callcc_reenter + skip 'callcc disabled' unless EnvUtil.support_callcc? bug9105 = '[ruby-dev:47803] [Bug #9105]' assert_nothing_raised(RuntimeError, bug9105) do h = @cls[1=>2,3=>4] diff --git a/test/ruby/test_marshal.rb b/test/ruby/test_marshal.rb index e269428dda3..c6d26c343ba 100644 --- a/test/ruby/test_marshal.rb +++ b/test/ruby/test_marshal.rb @@ -640,7 +640,7 @@ def marshal_load(v) end def test_continuation - require "continuation" + skip 'callcc disabled' unless EnvUtil.support_callcc? c = Bug9523.new assert_raise_with_message(RuntimeError, /Marshal\.dump reentered at marshal_dump/) do Marshal.dump(c) diff --git a/test/ruby/test_settracefunc.rb b/test/ruby/test_settracefunc.rb index edbc1e99566..9676b83f496 100644 --- a/test/ruby/test_settracefunc.rb +++ b/test/ruby/test_settracefunc.rb @@ -999,15 +999,17 @@ def each end } assert_normal_exit src % %q{obj.zip({}) {}}, bug7774 - assert_normal_exit src % %q{ - require 'continuation' - begin - c = nil - obj.sort_by {|x| callcc {|c2| c ||= c2 }; x } - c.call - rescue RuntimeError - end - }, bug7774 + if EnvUtil.support_callcc? + assert_normal_exit src % %q{ + require 'continuation' + begin + c = nil + obj.sort_by {|x| callcc {|c2| c ||= c2 }; x } + c.call + rescue RuntimeError + end + }, bug7774 + end # TracePoint tp_b = nil diff --git a/test/test_extlibs.rb b/test/test_extlibs.rb index 7dc22ee8a38..ed9818e7e62 100644 --- a/test/test_extlibs.rb +++ b/test/test_extlibs.rb @@ -42,7 +42,7 @@ def windows? @excluded = excluded check_existence "bigdecimal" - check_existence "continuation" + check_existence "continuation" if EnvUtil.support_callcc? check_existence "coverage" check_existence "date" #check_existence "dbm" # depend on libdbm diff --git a/vm_core.h b/vm_core.h index c557562e17c..2e077fb7d8d 100644 --- a/vm_core.h +++ b/vm_core.h @@ -636,7 +636,9 @@ typedef struct rb_vm_struct { rb_hook_list_t global_hooks; /* relation table of ensure - rollback for callcc */ +#if RUBY_SUPPORT_CALLCC struct st_table *ensure_rollback_table; +#endif /* postponed_job */ struct rb_postponed_job_struct *postponed_job_buffer; @@ -808,6 +810,7 @@ typedef struct rb_thread_list_struct{ struct rb_thread_struct *th; } rb_thread_list_t; +#if RUBY_SUPPORT_CALLCC typedef struct rb_ensure_entry { VALUE marker; VALUE (*e_proc)(ANYARGS); @@ -818,6 +821,7 @@ typedef struct rb_ensure_list { struct rb_ensure_list *next; struct rb_ensure_entry entry; } rb_ensure_list_t; +#endif /* RUBY_SUPPORT_CALLCC */ typedef char rb_thread_id_string_t[sizeof(rb_nativethread_id_t) * 2 + 3]; @@ -830,7 +834,9 @@ typedef struct rb_execution_context_struct { rb_control_frame_t *cfp; struct rb_vm_tag *tag; +#if RUBY_SUPPORT_CALLCC struct rb_vm_protect_tag *protect_tag; +#endif /* interrupt flags */ rb_atomic_t interrupt_flag; @@ -848,8 +854,10 @@ typedef struct rb_execution_context_struct { const VALUE *root_lep; VALUE root_svar; +#if RUBY_SUPPORT_CALLCC /* ensure & callcc */ rb_ensure_list_t *ensure_list; +#endif /* trace information */ struct rb_trace_arg_struct *trace_arg; -- EW