ruby-changes:31609
From: tarui <ko1@a...>
Date: Sat, 16 Nov 2013 02:15:36 +0900 (JST)
Subject: [ruby-changes:31609] tarui:r43688 (trunk): * cont.c : Introdule ensure rollback mechanism. Please see below.
tarui 2013-11-16 02:15:31 +0900 (Sat, 16 Nov 2013) New Revision: 43688 http://svn.ruby-lang.org/cgi-bin/viewvc.cgi?view=rev&revision=43688 Log: * cont.c : Introdule ensure rollback mechanism. Please see below. * internal.h (ruby_register_rollback_func_for_ensure): catch up above change. Add rollback mechanism API. * vm_core.h (typedef struct rb_vm_struct): catch up above change. Introdule ensure-rollback relation table. * vm_core.h (typedef struct rb_thread_struct): catch up above change. Introdule ensure stack. * eval.c (rb_ensure): catch up above change. Introdule ensure stack. * hash.c : New function for rollback ensure, and register it to ensure-rollback relation table. [ruby-dev:47803] [Bug #9105] Ensure Rollback Mechanism: A rollback's function is a function to rollback a state before ensure's function execution. When the jump of callcc is across the scope of rb_ensure, ensure's functions and rollback's functions are executed appropriately for keeping consistency. Current API is unstable, and only internal use. ruby_register_rollback_func_for_ensure(ensure_func,rollback_func) This API create relation ensure's function to rollback's function. By registered rollback's function, it is executed When jumpping into corresponding rb_ensure scope. Modified files: trunk/ChangeLog trunk/cont.c trunk/eval.c trunk/hash.c trunk/internal.h trunk/vm_core.h Index: ChangeLog =================================================================== --- ChangeLog (revision 43687) +++ ChangeLog (revision 43688) @@ -1,3 +1,36 @@ https://github.com/ruby/ruby/blob/trunk/ChangeLog#L1 +Sat Nov 16 02:13:56 2013 Masaya Tarui <tarui@r...> + + * cont.c : Introdule ensure rollback mechanism. Please see below. + + * internal.h (ruby_register_rollback_func_for_ensure): catch up above change. + Add rollback mechanism API. + + * vm_core.h (typedef struct rb_vm_struct): catch up above change. + Introdule ensure-rollback relation table. + + * vm_core.h (typedef struct rb_thread_struct): catch up above change. + Introdule ensure stack. + + * eval.c (rb_ensure): catch up above change. + Introdule ensure stack. + + * hash.c : New function for rollback ensure, and register it to + ensure-rollback relation table. [ruby-dev:47803] [Bug #9105] + + Ensure Rollback Mechanism: + A rollback's function is a function to rollback a state before ensure's + function execution. + When the jump of callcc is across the scope of rb_ensure, + ensure's functions and rollback's functions are executed appropriately + for keeping consistency. + + Current API is unstable, and only internal use. + + ruby_register_rollback_func_for_ensure(ensure_func,rollback_func) + This API create relation ensure's function to rollback's function. + By registered rollback's function, it is executed When jumpping into + corresponding rb_ensure scope. + Sat Nov 16 00:18:36 2013 Masaki Matsushita <glass.saga@g...> * eval_jump.c (rb_exec_end_proc): fix double free or corruption error Index: vm_core.h =================================================================== --- vm_core.h (revision 43687) +++ vm_core.h (revision 43688) @@ -387,6 +387,9 @@ typedef struct rb_vm_struct { https://github.com/ruby/ruby/blob/trunk/vm_core.h#L387 /* hook */ rb_hook_list_t event_hooks; + /* relation table of ensure - rollback for callcc */ + struct st_table *ensure_rollback_table; + /* postponed_job */ struct rb_postponed_job_struct *postponed_job_buffer; int postponed_job_index; @@ -507,6 +510,17 @@ typedef struct rb_thread_list_struct{ https://github.com/ruby/ruby/blob/trunk/vm_core.h#L510 } rb_thread_list_t; +typedef struct rb_ensure_entry { + VALUE marker; + VALUE (*e_proc)(ANYARGS); + VALUE data2; +} rb_ensure_entry_t; + +typedef struct rb_ensure_list { + struct rb_ensure_list *next; + struct rb_ensure_entry entry; +} rb_ensure_list_t; + typedef struct rb_thread_struct { VALUE self; rb_vm_t *vm; @@ -626,6 +640,9 @@ typedef struct rb_thread_struct { https://github.com/ruby/ruby/blob/trunk/vm_core.h#L640 VALUE root_fiber; rb_jmpbuf_t root_jmpbuf; + /* ensure & callcc */ + rb_ensure_list_t *ensure_list; + /* misc */ int method_missing_reason; int abort_on_exception; Index: eval.c =================================================================== --- eval.c (revision 43687) +++ eval.c (revision 43688) @@ -840,7 +840,12 @@ rb_ensure(VALUE (*b_proc)(ANYARGS), VALU https://github.com/ruby/ruby/blob/trunk/eval.c#L840 volatile VALUE result = Qnil; volatile VALUE errinfo; rb_thread_t *const th = GET_THREAD(); - + 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 = th->ensure_list; + th->ensure_list = &ensure_list; PUSH_TAG(); if ((state = EXEC_TAG()) == 0) { result = (*b_proc) (data1); @@ -849,7 +854,8 @@ rb_ensure(VALUE (*b_proc)(ANYARGS), VALU https://github.com/ruby/ruby/blob/trunk/eval.c#L854 /* TODO: fix me */ /* retval = prot_tag ? prot_tag->retval : Qnil; */ /* save retval */ errinfo = th->errinfo; - (*e_proc) (data2); + th->ensure_list=ensure_list.next; + (*ensure_list.entry.e_proc)(ensure_list.entry.data2); th->errinfo = errinfo; if (state) JUMP_TAG(state); Index: hash.c =================================================================== --- hash.c (revision 43687) +++ hash.c (revision 43688) @@ -201,6 +201,13 @@ hash_foreach_iter(st_data_t key, st_data https://github.com/ruby/ruby/blob/trunk/hash.c#L201 } static VALUE +hash_foreach_ensure_rollback(VALUE hash) +{ + RHASH_ITER_LEV(hash)++; + return 0; +} + +static VALUE hash_foreach_ensure(VALUE hash) { if (--RHASH_ITER_LEV(hash) == 0) { @@ -3765,4 +3772,7 @@ Init_Hash(void) https://github.com/ruby/ruby/blob/trunk/hash.c#L3772 * See ENV (the class) for more details. */ rb_define_global_const("ENV", envtbl); + + /* for callcc */ + ruby_register_rollback_func_for_ensure(hash_foreach_ensure, hash_foreach_ensure_rollback); } Index: cont.c =================================================================== --- cont.c (revision 43687) +++ cont.c (revision 43688) @@ -107,6 +107,8 @@ typedef struct rb_context_struct { https://github.com/ruby/ruby/blob/trunk/cont.c#L107 rb_thread_t saved_thread; rb_jmpbuf_t jmpbuf; size_t machine_stack_size; + rb_ensure_entry_t *ensure_array; + rb_ensure_list_t *ensure_list; } rb_context_t; enum fiber_status { @@ -223,6 +225,7 @@ cont_free(void *ptr) https://github.com/ruby/ruby/blob/trunk/cont.c#L225 #if FIBER_USE_NATIVE if (cont->type == CONTINUATION_CONTEXT) { /* cont */ + ruby_xfree(cont->ensure_array); RUBY_FREE_UNLESS_NULL(cont->machine_stack); } else { @@ -253,6 +256,7 @@ cont_free(void *ptr) https://github.com/ruby/ruby/blob/trunk/cont.c#L256 #endif } #else /* not FIBER_USE_NATIVE */ + ruby_xfree(cont->ensure_array); RUBY_FREE_UNLESS_NULL(cont->machine_stack); #endif #ifdef __ia64 @@ -485,6 +489,22 @@ cont_capture(volatile int *stat) https://github.com/ruby/ruby/blob/trunk/cont.c#L489 cont_save_machine_stack(th, cont); + /* backup ensure_list to array for search in another context */ + { + rb_ensure_list_t *p; + int size = 0; + rb_ensure_entry_t *entry; + for (p=th->ensure_list; p; p=p->next) + size++; + entry = cont->ensure_array = ALLOC_N(rb_ensure_entry_t,size+1); + for (p=th->ensure_list; p; p=p->next) { + if (!p->entry.marker) + p->entry.marker = rb_ary_tmp_new(0); /* dummy object */ + *entry++ = p->entry; + } + entry->marker = 0; + } + if (ruby_setjmp(cont->jmpbuf)) { volatile VALUE value; @@ -546,6 +566,8 @@ cont_restore_thread(rb_context_t *cont) https://github.com/ruby/ruby/blob/trunk/cont.c#L566 th->first_proc = sth->first_proc; th->root_lep = sth->root_lep; th->root_svar = sth->root_svar; + th->ensure_list = sth->ensure_list; + } #if FIBER_USE_NATIVE @@ -917,6 +939,80 @@ make_passing_arg(int argc, VALUE *argv) https://github.com/ruby/ruby/blob/trunk/cont.c#L939 } } +/* CAUTION!! : Currently, error in rollback_func is not supported */ +/* same as rb_protect if set rollback_func to NULL */ +void +ruby_register_rollback_func_for_ensure(VALUE (*ensure_func)(ANYARGS), VALUE (*rollback_func)(ANYARGS)) +{ + 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); +} + +static inline VALUE +lookup_rollback_func(VALUE (*ensure_func)(ANYARGS)) +{ + st_table *table = GET_VM()->ensure_rollback_table; + st_data_t val; + if (table && st_lookup(table, (st_data_t)ensure_func, &val)) + return (VALUE) val; + return Qundef; +} + + +static inline void +rollback_ensure_stack(VALUE self,rb_ensure_list_t *current,rb_ensure_entry_t *target) +{ + rb_ensure_list_t *p; + rb_ensure_entry_t *entry; + size_t i; + size_t cur_size; + size_t target_size; + size_t base_point; + VALUE (*func)(ANYARGS); + + cur_size = 0; + for (p=current; p; p=p->next) + cur_size++; + target_size = 0; + for (entry=target; entry->marker; entry++) + target_size++; + + /* search common stack point */ + p = current; + base_point = cur_size; + while (base_point) { + if (target_size >= base_point && + p->entry.marker == target[target_size - base_point].marker) + break; + base_point --; + p = p->next; + } + + /* rollback function check */ + for (i=0; i < target_size - base_point; i++) { + if (!lookup_rollback_func(target[i].e_proc)) { + rb_raise(rb_eRuntimeError, "continuation called from out of critical rb_ensure scope"); + } + } + /* pop ensure stack */ + while (cur_size > base_point) { + /* escape from ensure block */ + (*current->entry.e_proc)(current->entry.data2); + current = current->next; + cur_size--; + } + /* push ensure stack */ + while (i--) { + func = (VALUE (*)(ANYARGS)) lookup_rollback_func(target[i].e_proc); + if ((VALUE)func != Qundef) { + (*func)(target[i].data2); + } + } +} + /* * call-seq: * cont.call(args, ...) @@ -954,6 +1050,7 @@ rb_cont_call(int argc, VALUE *argv, VALU https://github.com/ruby/ruby/blob/trunk/cont.c#L1050 rb_raise(rb_eRuntimeError, "continuation called across fiber"); } } + rollback_ensure_stack(contval, th->ensure_list, cont->ensure_array); cont->argc = argc; cont->value = make_passing_arg(argc, argv); Index: internal.h =================================================================== --- internal.h (revision 43687) +++ internal.h (revision 43688) @@ -342,6 +342,7 @@ VALUE rb_insns_name_array(void); https://github.com/ruby/ruby/blob/trunk/internal.h#L342 /* cont.c */ VALUE rb_obj_is_fiber(VALUE); void rb_fiber_reset_root_local_storage(VALUE); +void ruby_register_rollback_func_for_ensure(VALUE (*ensure_func)(ANYARGS), VALUE (*rollback_func)(ANYARGS)); /* debug.c */ PRINTF_ARGS(void ruby_debug_printf(const char*, ...), 1, 2); -- ML: ruby-changes@q... Info: http://www.atdot.net/~ko1/quickml/