mirror of
				https://github.com/ruby/ruby.git
				synced 2022-11-09 12:17:21 -05:00 
			
		
		
		
	* ext/tcltklib/tcltklib.c: To fix 'pthread-enabled Tcl/Tk' problem,
TclTkIp#_eval calls Tcl_Eval() on the mainloop thread only (queueing a handler to the EventQueue). git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@4921 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
		
							parent
							
								
									91669219ef
								
							
						
					
					
						commit
						3d337f3606
					
				
					 3 changed files with 234 additions and 67 deletions
				
			
		| 
						 | 
				
			
			@ -1,3 +1,11 @@
 | 
			
		|||
Sat Nov  8 06:19:38 2003  Hidetoshi NAGAI  <nagai@ai.kyutech.ac.jp>
 | 
			
		||||
 | 
			
		||||
	* ext/tcltklib/tcltklib.c: To fix 'pthread-enabled Tcl/Tk' problem, 
 | 
			
		||||
	  TclTkIp#_eval calls Tcl_Eval() on the mainloop thread only 
 | 
			
		||||
	  (queueing a handler to the EventQueue). 
 | 
			
		||||
 | 
			
		||||
	* ext/tcltklib/README.1st: edit the description of '--with-pthread-ext'
 | 
			
		||||
 | 
			
		||||
Fri Nov  7 23:23:04 2003  Tanaka Akira  <akr@m17n.org>
 | 
			
		||||
 | 
			
		||||
	* lib/pathname.rb (Pathname#+): if self or the argument is `.', return
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -31,10 +31,11 @@ directry of Ruby sources, please try something like as the followings.
 | 
			
		|||
 | 
			
		||||
 | 
			
		||||
 *** ATTENTION ***
 | 
			
		||||
If your Tcl/Tk libraries are compiled with "pthread support", Ruby/Tk
 | 
			
		||||
may cause "Hang-up" or "Segmentation Fault" frequently. To avoid this 
 | 
			
		||||
trouble, please try to use the '--with-pthread-ext' option of the 
 | 
			
		||||
'configure' command and re-compile Ruby sources.
 | 
			
		||||
When your Tcl/Tk libraries are compiled with "pthread support", 
 | 
			
		||||
Ruby/Tk may cause "Hang-up" or "Segmentation Fault" frequently. 
 | 
			
		||||
If you have such a trouble, please try to use the '--with-pthread-ext' 
 | 
			
		||||
option of the 'configure' command and re-compile Ruby sources. 
 | 
			
		||||
It may help you to avoid this trouble, 
 | 
			
		||||
 | 
			
		||||
==========================================================
 | 
			
		||||
                Hidetoshi NAGAI (nagai@ai.kyutech.ac.jp)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -79,6 +79,16 @@ struct invoke_queue {
 | 
			
		|||
    VALUE *result;
 | 
			
		||||
    VALUE thread;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct eval_queue {
 | 
			
		||||
    Tcl_Event ev;
 | 
			
		||||
    VALUE str;
 | 
			
		||||
    VALUE obj;
 | 
			
		||||
    int done;
 | 
			
		||||
    int safe_level;
 | 
			
		||||
    VALUE *result;
 | 
			
		||||
    VALUE thread;
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
static VALUE eventloop_thread;
 | 
			
		||||
static VALUE watchdog_thread;
 | 
			
		||||
| 
						 | 
				
			
			@ -451,7 +461,9 @@ lib_eventloop_core(check_root, check_var)
 | 
			
		|||
		    }
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (Tcl_DoOneEvent(TCL_ALL_EVENTS | TCL_DONT_WAIT)) {
 | 
			
		||||
		found_event = Tcl_DoOneEvent(TCL_ALL_EVENTS | TCL_DONT_WAIT);
 | 
			
		||||
 | 
			
		||||
		if (found_event) {
 | 
			
		||||
		    tick_counter++;
 | 
			
		||||
		} else {
 | 
			
		||||
		    tick_counter += no_event_tick;
 | 
			
		||||
| 
						 | 
				
			
			@ -687,6 +699,7 @@ lib_do_one_event_core(argc, argv, self, is_ip)
 | 
			
		|||
{
 | 
			
		||||
    VALUE vflags;
 | 
			
		||||
    int flags;
 | 
			
		||||
    int found_event;
 | 
			
		||||
 | 
			
		||||
    if (rb_scan_args(argc, argv, "01", &vflags) == 0) {
 | 
			
		||||
	flags = TCL_ALL_EVENTS | TCL_DONT_WAIT;
 | 
			
		||||
| 
						 | 
				
			
			@ -708,7 +721,9 @@ lib_do_one_event_core(argc, argv, self, is_ip)
 | 
			
		|||
	}
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (Tcl_DoOneEvent(flags)) {
 | 
			
		||||
    found_event = Tcl_DoOneEvent(TCL_ALL_EVENTS | TCL_DONT_WAIT);
 | 
			
		||||
 | 
			
		||||
    if (found_event) {
 | 
			
		||||
	return Qtrue;
 | 
			
		||||
    } else {
 | 
			
		||||
	return Qfalse;
 | 
			
		||||
| 
						 | 
				
			
			@ -736,7 +751,7 @@ ip_do_one_event(argc, argv, self)
 | 
			
		|||
 | 
			
		||||
/* Tcl command `ruby' */
 | 
			
		||||
static VALUE
 | 
			
		||||
ip_eval_rescue(failed, einfo)
 | 
			
		||||
ip_ruby_eval_rescue(failed, einfo)
 | 
			
		||||
    VALUE *failed;
 | 
			
		||||
    VALUE einfo;
 | 
			
		||||
{
 | 
			
		||||
| 
						 | 
				
			
			@ -744,55 +759,28 @@ ip_eval_rescue(failed, einfo)
 | 
			
		|||
    return Qnil;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* restart Tk */
 | 
			
		||||
struct eval_body_arg {
 | 
			
		||||
    char  *string;
 | 
			
		||||
    VALUE failed;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static VALUE
 | 
			
		||||
lib_restart(self)
 | 
			
		||||
    VALUE self;
 | 
			
		||||
ip_ruby_eval_body(arg)
 | 
			
		||||
     struct eval_body_arg *arg;
 | 
			
		||||
{
 | 
			
		||||
    struct tcltkip *ptr = get_ip(self);
 | 
			
		||||
 | 
			
		||||
    rb_secure(4);
 | 
			
		||||
 | 
			
		||||
    /* destroy the root wdiget */
 | 
			
		||||
    ptr->return_value = Tcl_Eval(ptr->ip, "destroy .");
 | 
			
		||||
    /* ignore ERROR */
 | 
			
		||||
    DUMP2("(TCL_Eval result) %d", ptr->return_value);
 | 
			
		||||
 | 
			
		||||
    /* execute Tk_Init of Tk_SafeInit */
 | 
			
		||||
#if TCL_MAJOR_VERSION >= 8
 | 
			
		||||
    if (Tcl_IsSafe(ptr->ip)) {
 | 
			
		||||
	DUMP1("Tk_SafeInit");
 | 
			
		||||
	if (Tk_SafeInit(ptr->ip) == TCL_ERROR) {
 | 
			
		||||
	    rb_raise(rb_eRuntimeError, "%s", ptr->ip->result);
 | 
			
		||||
	}
 | 
			
		||||
    } else {
 | 
			
		||||
	DUMP1("Tk_Init");
 | 
			
		||||
	if (Tk_Init(ptr->ip) == TCL_ERROR) {
 | 
			
		||||
	    rb_raise(rb_eRuntimeError, "%s", ptr->ip->result);
 | 
			
		||||
	}
 | 
			
		||||
    }
 | 
			
		||||
#else
 | 
			
		||||
    DUMP1("Tk_Init");
 | 
			
		||||
    if (Tk_Init(ptr->ip) == TCL_ERROR) {
 | 
			
		||||
	rb_raise(rb_eRuntimeError, "%s", ptr->ip->result);
 | 
			
		||||
    }
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
    return Qnil;
 | 
			
		||||
    rb_trap_immediate = 0;
 | 
			
		||||
    return rb_rescue2(rb_eval_string, (VALUE)arg->string, 
 | 
			
		||||
		      ip_ruby_eval_rescue, (VALUE)&(arg->failed),
 | 
			
		||||
		      rb_eStandardError, rb_eScriptError, rb_eSystemExit, 
 | 
			
		||||
		      (VALUE)0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static VALUE
 | 
			
		||||
ip_restart(self)
 | 
			
		||||
    VALUE self;
 | 
			
		||||
ip_ruby_eval_ensure(trapflag)
 | 
			
		||||
     VALUE trapflag;
 | 
			
		||||
{
 | 
			
		||||
    struct tcltkip *ptr = get_ip(self);
 | 
			
		||||
 | 
			
		||||
    rb_secure(4);
 | 
			
		||||
    if (Tcl_GetMaster(ptr->ip) != (Tcl_Interp*)NULL) {
 | 
			
		||||
	/* slave IP */
 | 
			
		||||
	return Qnil;
 | 
			
		||||
    }
 | 
			
		||||
    return lib_restart(self);
 | 
			
		||||
    rb_trap_immediate = NUM2INT(trapflag);
 | 
			
		||||
    return Qnil;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int
 | 
			
		||||
| 
						 | 
				
			
			@ -811,9 +799,8 @@ ip_ruby(clientData, interp, argc, argv)
 | 
			
		|||
#endif
 | 
			
		||||
{
 | 
			
		||||
    VALUE res;
 | 
			
		||||
    int old_trapflg;
 | 
			
		||||
    VALUE failed = 0;
 | 
			
		||||
    char *arg;
 | 
			
		||||
    int old_trapflag;
 | 
			
		||||
     struct eval_body_arg arg;
 | 
			
		||||
    int  dummy;
 | 
			
		||||
 | 
			
		||||
    /* ruby command has 1 arg. */
 | 
			
		||||
| 
						 | 
				
			
			@ -823,34 +810,31 @@ ip_ruby(clientData, interp, argc, argv)
 | 
			
		|||
 | 
			
		||||
    /* get C string from Tcl object */
 | 
			
		||||
#if TCL_MAJOR_VERSION >= 8
 | 
			
		||||
    arg = Tcl_GetStringFromObj(argv[1], &dummy);
 | 
			
		||||
    arg.string = Tcl_GetStringFromObj(argv[1], &dummy);
 | 
			
		||||
#else
 | 
			
		||||
    arg = argv[1];
 | 
			
		||||
    arg.string = argv[1];
 | 
			
		||||
#endif
 | 
			
		||||
    arg.failed = 0;
 | 
			
		||||
 | 
			
		||||
    /* evaluate the argument string by ruby */
 | 
			
		||||
    DUMP2("rb_eval_string(%s)", arg);
 | 
			
		||||
    old_trapflg = rb_trap_immediate;
 | 
			
		||||
    rb_trap_immediate = 0;
 | 
			
		||||
    res = rb_rescue2(rb_eval_string, (VALUE)arg, 
 | 
			
		||||
                     ip_eval_rescue, (VALUE)&failed,
 | 
			
		||||
                     rb_eStandardError, rb_eScriptError, rb_eSystemExit, 
 | 
			
		||||
		     (VALUE)0);
 | 
			
		||||
    rb_trap_immediate = old_trapflg;
 | 
			
		||||
    old_trapflag = rb_trap_immediate;
 | 
			
		||||
    res = rb_ensure(ip_ruby_eval_body, (VALUE)&arg, 
 | 
			
		||||
		    ip_ruby_eval_ensure, INT2FIX(old_trapflag));
 | 
			
		||||
 | 
			
		||||
    /* status check */
 | 
			
		||||
    if (failed) {
 | 
			
		||||
        VALUE eclass = CLASS_OF(failed);
 | 
			
		||||
    if (arg.failed) {
 | 
			
		||||
        VALUE eclass = CLASS_OF(arg.failed);
 | 
			
		||||
	DUMP1("(rb_eval_string result) failed");
 | 
			
		||||
	Tcl_ResetResult(interp);
 | 
			
		||||
	Tcl_AppendResult(interp, StringValuePtr(failed), (char*)NULL);
 | 
			
		||||
	Tcl_AppendResult(interp, StringValuePtr(arg.failed), (char*)NULL);
 | 
			
		||||
        if (eclass == eTkCallbackBreak) {
 | 
			
		||||
	    return TCL_BREAK;
 | 
			
		||||
	} else if (eclass == eTkCallbackContinue) {
 | 
			
		||||
	    return TCL_CONTINUE;
 | 
			
		||||
	} else if (eclass == rb_eSystemExit) {
 | 
			
		||||
	    Tcl_Eval(interp, "destroy .");
 | 
			
		||||
	    rb_raise(rb_eSystemExit, StringValuePtr(failed));
 | 
			
		||||
	    rb_raise(rb_eSystemExit, StringValuePtr(arg.failed));
 | 
			
		||||
	} else {
 | 
			
		||||
	    return TCL_ERROR;
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -1743,9 +1727,10 @@ ip_is_deleted_p(self)
 | 
			
		|||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/* eval string in tcl by Tcl_Eval() */
 | 
			
		||||
static VALUE
 | 
			
		||||
ip_eval(self, str)
 | 
			
		||||
ip_eval_real(self, str)
 | 
			
		||||
    VALUE self;
 | 
			
		||||
    VALUE str;
 | 
			
		||||
{
 | 
			
		||||
| 
						 | 
				
			
			@ -1769,6 +1754,179 @@ ip_eval(self, str)
 | 
			
		|||
    return(rb_tainted_str_new2(ptr->ip->result));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static VALUE
 | 
			
		||||
evq_safelevel_handler(arg, evq)
 | 
			
		||||
    VALUE arg;
 | 
			
		||||
    VALUE evq;
 | 
			
		||||
{
 | 
			
		||||
    struct eval_queue *q;
 | 
			
		||||
 | 
			
		||||
    Data_Get_Struct(evq, struct eval_queue, q);
 | 
			
		||||
    DUMP2("(safe-level handler) $SAFE = %d", q->safe_level);
 | 
			
		||||
    rb_set_safe_level(q->safe_level);
 | 
			
		||||
    return ip_eval_real(q->obj, q->str);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int eval_queue_handler _((Tcl_Event *, int));
 | 
			
		||||
int
 | 
			
		||||
eval_queue_handler(evPtr, flags)
 | 
			
		||||
    Tcl_Event *evPtr;
 | 
			
		||||
    int flags;
 | 
			
		||||
{
 | 
			
		||||
    struct eval_queue *q = (struct eval_queue *)evPtr;
 | 
			
		||||
 | 
			
		||||
    DUMP2("do_eval_queue_handler : evPtr = %lx", evPtr);
 | 
			
		||||
    DUMP2("eval queue_thread : %lx", rb_thread_current());
 | 
			
		||||
    DUMP2("added by thread : %lx", q->thread);
 | 
			
		||||
 | 
			
		||||
    if (q->done) {
 | 
			
		||||
	DUMP1("processed by another event-loop");
 | 
			
		||||
	return 0;
 | 
			
		||||
    } else {
 | 
			
		||||
	DUMP1("process it on current event-loop");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* process it */
 | 
			
		||||
    q->done = 1;
 | 
			
		||||
 | 
			
		||||
    /* check safe-level */
 | 
			
		||||
    if (rb_safe_level() != q->safe_level) {
 | 
			
		||||
	*(q->result) 
 | 
			
		||||
	    = rb_funcall(rb_proc_new(evq_safelevel_handler, 
 | 
			
		||||
				     Data_Wrap_Struct(rb_cData,0,0,q)), 
 | 
			
		||||
			 rb_intern("call"), 0);
 | 
			
		||||
    } else {
 | 
			
		||||
    DUMP2("call eval_real (for caller thread:%lx)", q->thread);
 | 
			
		||||
    DUMP2("call eval_real (current thread:%lx)", rb_thread_current());
 | 
			
		||||
	*(q->result) = ip_eval_real(q->obj, q->str);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* back to caller */
 | 
			
		||||
    DUMP2("back to caller (caller thread:%lx)", q->thread);
 | 
			
		||||
    DUMP2("               (current thread:%lx)", rb_thread_current());
 | 
			
		||||
    rb_thread_run(q->thread);
 | 
			
		||||
    DUMP1("finish back to caller");
 | 
			
		||||
 | 
			
		||||
    /* end of handler : remove it */
 | 
			
		||||
    return 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static VALUE
 | 
			
		||||
ip_eval(self, str)
 | 
			
		||||
    VALUE self;
 | 
			
		||||
    VALUE str;
 | 
			
		||||
{
 | 
			
		||||
    struct eval_queue *tmp;
 | 
			
		||||
    VALUE current = rb_thread_current();
 | 
			
		||||
    VALUE result;
 | 
			
		||||
    VALUE *alloc_result;
 | 
			
		||||
    Tcl_QueuePosition position;
 | 
			
		||||
 | 
			
		||||
    if (eventloop_thread == 0 || current == eventloop_thread) {
 | 
			
		||||
	if (eventloop_thread) {
 | 
			
		||||
	    DUMP2("eval from current eventloop %lx", current);
 | 
			
		||||
	} else {
 | 
			
		||||
	    DUMP2("eval from thread:%lx but no eventloop", current);
 | 
			
		||||
	}
 | 
			
		||||
	result = ip_eval_real(self, str);
 | 
			
		||||
	if (rb_obj_is_kind_of(result, rb_eException)) {
 | 
			
		||||
	    rb_exc_raise(result);
 | 
			
		||||
	}
 | 
			
		||||
	return result;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    DUMP2("eval from thread %lx (NOT current eventloop)", current);
 | 
			
		||||
 | 
			
		||||
    /* allocate memory (protected from Tcl_ServiceEvent) */
 | 
			
		||||
    alloc_result = ALLOC(VALUE);
 | 
			
		||||
 | 
			
		||||
    /* allocate memory (freed by Tcl_ServiceEvent) */
 | 
			
		||||
    tmp = (struct eval_queue *)Tcl_Alloc(sizeof(struct eval_queue));
 | 
			
		||||
 | 
			
		||||
    /* construct event data */
 | 
			
		||||
    tmp->done = 0;
 | 
			
		||||
    tmp->obj = self;
 | 
			
		||||
    tmp->str = str;
 | 
			
		||||
    tmp->result = alloc_result;
 | 
			
		||||
    tmp->thread = current;
 | 
			
		||||
    tmp->safe_level = rb_safe_level();
 | 
			
		||||
    tmp->ev.proc = eval_queue_handler;
 | 
			
		||||
    position = TCL_QUEUE_TAIL;
 | 
			
		||||
 | 
			
		||||
    /* add the handler to Tcl event queue */
 | 
			
		||||
    DUMP1("add handler");
 | 
			
		||||
    Tcl_QueueEvent(&(tmp->ev), position);
 | 
			
		||||
 | 
			
		||||
    /* wait for the handler to be processed */
 | 
			
		||||
    DUMP2("wait for handler (current thread:%lx)", current);
 | 
			
		||||
    rb_thread_stop();
 | 
			
		||||
    DUMP2("back from handler (current thread:%lx)", current);
 | 
			
		||||
 | 
			
		||||
    /* get result & free allocated memory */
 | 
			
		||||
    result = *alloc_result;
 | 
			
		||||
    free(alloc_result);
 | 
			
		||||
    if (rb_obj_is_kind_of(result, rb_eException)) {
 | 
			
		||||
	rb_exc_raise(result);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return result;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/* restart Tk */
 | 
			
		||||
static VALUE
 | 
			
		||||
lib_restart(self)
 | 
			
		||||
    VALUE self;
 | 
			
		||||
{
 | 
			
		||||
    struct tcltkip *ptr = get_ip(self);
 | 
			
		||||
 | 
			
		||||
    rb_secure(4);
 | 
			
		||||
 | 
			
		||||
    /* destroy the root wdiget */
 | 
			
		||||
    /* ptr->return_value = Tcl_Eval(ptr->ip, "destroy ."); */
 | 
			
		||||
    ptr->return_value = FIX2INT(ip_eval(self, "destroy ."));
 | 
			
		||||
    /* ignore ERROR */
 | 
			
		||||
    DUMP2("(TCL_Eval result) %d", ptr->return_value);
 | 
			
		||||
    Tcl_ResetResult(ptr->ip);
 | 
			
		||||
 | 
			
		||||
    /* execute Tk_Init of Tk_SafeInit */
 | 
			
		||||
#if TCL_MAJOR_VERSION >= 8
 | 
			
		||||
    if (Tcl_IsSafe(ptr->ip)) {
 | 
			
		||||
	DUMP1("Tk_SafeInit");
 | 
			
		||||
	if (Tk_SafeInit(ptr->ip) == TCL_ERROR) {
 | 
			
		||||
	    rb_raise(rb_eRuntimeError, "%s", ptr->ip->result);
 | 
			
		||||
	}
 | 
			
		||||
    } else {
 | 
			
		||||
	DUMP1("Tk_Init");
 | 
			
		||||
	if (Tk_Init(ptr->ip) == TCL_ERROR) {
 | 
			
		||||
	    rb_raise(rb_eRuntimeError, "%s", ptr->ip->result);
 | 
			
		||||
	}
 | 
			
		||||
    }
 | 
			
		||||
#else
 | 
			
		||||
    DUMP1("Tk_Init");
 | 
			
		||||
    if (Tk_Init(ptr->ip) == TCL_ERROR) {
 | 
			
		||||
	rb_raise(rb_eRuntimeError, "%s", ptr->ip->result);
 | 
			
		||||
    }
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
    return Qnil;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static VALUE
 | 
			
		||||
ip_restart(self)
 | 
			
		||||
    VALUE self;
 | 
			
		||||
{
 | 
			
		||||
    struct tcltkip *ptr = get_ip(self);
 | 
			
		||||
 | 
			
		||||
    rb_secure(4);
 | 
			
		||||
    if (Tcl_GetMaster(ptr->ip) != (Tcl_Interp*)NULL) {
 | 
			
		||||
	/* slave IP */
 | 
			
		||||
	return Qnil;
 | 
			
		||||
    }
 | 
			
		||||
    return lib_restart(self);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static VALUE
 | 
			
		||||
ip_toUTF8(self, str, encodename)
 | 
			
		||||
    VALUE self;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue