* 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.


git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@43688 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
tarui 2013-11-15 17:15:31 +00:00
Родитель 9e96b1edea
Коммит 28a1c4f33e
6 изменённых файлов: 166 добавлений и 2 удалений

Просмотреть файл

@ -1,3 +1,36 @@
Sat Nov 16 02:13:56 2013 Masaya Tarui <tarui@ruby-lang.org>
* 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@gmail.com>
* eval_jump.c (rb_exec_end_proc): fix double free or corruption error

97
cont.c
Просмотреть файл

@ -107,6 +107,8 @@ typedef struct rb_context_struct {
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)
#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)
#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)
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)
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)
}
}
/* 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, VALUE contval)
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);

10
eval.c
Просмотреть файл

@ -840,7 +840,12 @@ rb_ensure(VALUE (*b_proc)(ANYARGS), VALUE data1, VALUE (*e_proc)(ANYARGS), VALUE
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), VALUE data1, VALUE (*e_proc)(ANYARGS), VALUE
/* 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);

10
hash.c
Просмотреть файл

@ -200,6 +200,13 @@ hash_foreach_iter(st_data_t key, st_data_t value, st_data_t argp, int error)
return ST_CHECK;
}
static VALUE
hash_foreach_ensure_rollback(VALUE hash)
{
RHASH_ITER_LEV(hash)++;
return 0;
}
static VALUE
hash_foreach_ensure(VALUE hash)
{
@ -3765,4 +3772,7 @@ Init_Hash(void)
* 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);
}

Просмотреть файл

@ -342,6 +342,7 @@ VALUE rb_insns_name_array(void);
/* 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);

Просмотреть файл

@ -387,6 +387,9 @@ typedef struct rb_vm_struct {
/* 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{
} 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 {
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;