[ruby-dev:38924] thread switch hook for RubyCocoa
From:
Nobuyoshi Nakada <nobu@...>
Date:
2009-07-27 22:36:21 UTC
List:
ruby-dev #38924
なかだです。
Mac OS X用のruby 1.8には、RubyCocoa向けにApple独自のthread_hook
パッチというものが入っています。これは1.8標準のEVENT_HOOKと事実
上同じ機能であり、フックのタイミングを追加するだけです。
RubyKaigiのときにちょっとknuさんや、高尾さん、MacPortsメンテナの
木村わさんとも話したんですが、標準に追加してはどうでしょうか。
問題となる可能性としては
1. スレッドスイッチがやや遅くなる
フックが登録されていないときにはフラグをチェックするだけなの
で、スレッドスイッチ自体に比べればごくわずか
2. フックを使用する側にも変更が必要になる
RubyCocoa以外に使っているものはないと思いますが、私は確認して
いません
また、RubyCocoaのruby 1.9対応状況もよくわからないのですが、1.9で
もこのTHREAD_SWITCH hookは必要なのでしょうか。
以下、1.8用とRubyCocoa 1.0.0用のパッチです。対応バージョンかどう
かチェックするために、rb_add_threadswitch_hook()の有無の代わりに、
rb_event_allという変数の有無とその値をみるようにしています。
Index: eval.c
===================================================================
--- eval.c (revision 24302)
+++ eval.c (working copy)
@@ -2628,4 +2628,8 @@ rb_obj_is_proc(proc)
}
+static void thread_deliver_event _((rb_event_hook_func_t,rb_event_t));
+
+const rb_event_t rb_event_all = RUBY_EVENT_ALL;
+
void
rb_add_event_hook(func, events)
@@ -2640,4 +2644,7 @@ rb_add_event_hook(func, events)
hook->next = event_hooks;
event_hooks = hook;
+ if (events & RUBY_EVENT_THREAD_INIT) {
+ thread_deliver_event(func, RUBY_EVENT_THREAD_INIT);
+ }
}
@@ -10657,8 +10664,23 @@ rb_gc_abort_threads()
}
+static void
+thread_deliver_event(func, event)
+ rb_event_hook_func_t func;
+ rb_event_t event;
+{
+ rb_thread_t th;
+
+ FOREACH_THREAD(th) {
+ (*func)(event, 0, th->thread, 0, RBASIC(th->thread)->klass);
+ } END_FOREACH(th);
+}
+
static inline void
stack_free(th)
rb_thread_t th;
{
+ EXEC_EVENT_HOOK(RUBY_EVENT_THREAD_FREE, 0,
+ th->thread, 0, RBASIC(th->thread)->klass);
+
if (th->stk_ptr) free(th->stk_ptr);
th->stk_ptr = 0;
@@ -10720,4 +10742,7 @@ rb_thread_save_context(th)
size_t len;
static VALUE tval;
+
+ EXEC_EVENT_HOOK(RUBY_EVENT_THREAD_SAVE, th->node,
+ th->thread, 0, RBASIC(th->thread)->klass);
len = ruby_stack_length(&pos);
@@ -10959,4 +10984,6 @@ rb_thread_restore_context(th, exit)
{
if (!th->stk_ptr) rb_bug("unsaved context");
+ EXEC_EVENT_HOOK(RUBY_EVENT_THREAD_RESTORE, th->node,
+ th->thread, 0, RBASIC(th->thread)->klass);
stack_extend(th, exit);
}
@@ -12359,4 +12386,7 @@ rb_thread_alloc(klass)
th->thread = Data_Wrap_Struct(klass, thread_mark, thread_free, th);
+ EXEC_EVENT_HOOK(RUBY_EVENT_THREAD_INIT, ruby_current_node,
+ th->thread, 0, RBASIC(th->thread)->klass);
+
for (vars = th->dyna_vars; vars; vars = vars->next) {
if (FL_TEST(vars, DVAR_DONT_RECYCLE)) break;
Index: node.h
===================================================================
--- node.h (revision 24302)
+++ node.h (working copy)
@@ -16,4 +16,7 @@
#if defined(__cplusplus)
extern "C" {
+#if 0
+}
+#endif
#endif
@@ -369,5 +372,10 @@ typedef unsigned int rb_event_t;
#define RUBY_EVENT_C_RETURN 0x40
#define RUBY_EVENT_RAISE 0x80
-#define RUBY_EVENT_ALL 0xff
+#define RUBY_EVENT_THREAD_INIT 0x0100
+#define RUBY_EVENT_THREAD_FREE 0x0200
+#define RUBY_EVENT_THREAD_SAVE 0x0400
+#define RUBY_EVENT_THREAD_RESTORE 0x0800
+#define RUBY_EVENT_THREAD_ALL 0x0f00
+#define RUBY_EVENT_ALL 0xfff
typedef void (*rb_event_hook_func_t) _((rb_event_t,NODE*,VALUE,ID,VALUE));
@@ -375,4 +383,5 @@ NODE *rb_copy_node_scope _((NODE *, NODE
void rb_add_event_hook _((rb_event_hook_func_t,rb_event_t));
int rb_remove_event_hook _((rb_event_hook_func_t));
+extern const rb_event_t rb_event_all;
#if defined(HAVE_GETCONTEXT) && defined(HAVE_SETCONTEXT)
@@ -485,4 +494,7 @@ int rb_thread_reset_raised(rb_thread_t t
#if defined(__cplusplus)
+#if 0
+extern "C" {
+#endif
} /* extern "C" { */
#endif
--- framework/src/objc/RBRuntime.m.orig 2009-02-18 06:46:15.000000000 +0900
+++ framework/src/objc/RBRuntime.m 2009-07-28 07:27:20.000000000 +0900
@@ -487,29 +487,13 @@
static int rb_cocoa_thread_debug = 1;
-#ifndef RUBY_THREADSWITCH_INIT
-/* The declarations immediately below come from the patched ruby.h.
- Since they are not available here (determined by the lack of definition for
- RUBY_THREADSWITCH_INIT) we define them manually, so that a patched
- libruby.dylib can still be used at runtime.
-*/
-
-typedef unsigned int rb_threadswitch_event_t;
-
-#define RUBY_THREADSWITCH_INIT 0x01
-#define RUBY_THREADSWITCH_FREE 0x02
-#define RUBY_THREADSWITCH_SAVE 0x04
-#define RUBY_THREADSWITCH_RESTORE 0x08
-
-typedef void (*rb_threadswitch_hook_func_t) _((rb_threadswitch_event_t,VALUE));
+#ifdef RUBY_EVENT_THREAD_ALL
+#define RUBY_EVENT_THREAD_INIT 0x0100
+#define RUBY_EVENT_THREAD_FREE 0x0200
+#define RUBY_EVENT_THREAD_SAVE 0x0400
+#define RUBY_EVENT_THREAD_RESTORE 0x0800
+#define RUBY_EVENT_THREAD_ALL 0x0f00
#endif
-/* The following two functions are marked as weak imports so that RubyCocoa
- will still load without thread switching hooks support in the ruby
- interpreter.
-*/
-extern void *rb_add_threadswitch_hook(rb_threadswitch_hook_func_t func)
- __attribute__ ((weak_import));
-extern void rb_remove_threadswitch_hook(void *handle)
- __attribute__ ((weak_import));
+extern rb_event_t rb_event_all __attribute__ ((weak_import));
/* Cached values for direct call to +[NSThread currentThread] (not clear if
@@ -850,8 +834,8 @@
This function is registered with the ruby core as a threadswitch event hook.
*/
-static void rb_cocoa_thread_schedule_hook(rb_threadswitch_event_t event,
- VALUE thread)
+static void rb_cocoa_thread_schedule_hook(rb_event_t event, NODE *node, VALUE thread,
+ ID methodid, VALUE klass)
{
- void *context;
+ st_data_t context, key = (st_data_t)thread;
NSThread *nsthread;
@@ -864,17 +848,16 @@
}
switch (event) {
- case RUBY_THREADSWITCH_INIT:
- context = rb_cocoa_thread_init_context(nsthread,
+ case RUBY_EVENT_THREAD_INIT:
+ context = (st_data_t)rb_cocoa_thread_init_context(nsthread,
rb_cocoa_did_install_thread_hooks || thread == rb_thread_main()
? thread : Qnil);
- DLOG("Created context %p for thread %p\n", context, (void*)thread);
- st_insert(rb_cocoa_thread_state, (st_data_t)thread, (st_data_t)context);
+ DLOG("Created context %p for thread %p\n", (void*)context, (void*)thread);
+ st_insert(rb_cocoa_thread_state, key, context);
break;
- case RUBY_THREADSWITCH_FREE:
- if (st_delete(rb_cocoa_thread_state, (st_data_t*)&thread,
- (st_data_t *)&context)) {
+ case RUBY_EVENT_THREAD_FREE:
+ if (st_delete(rb_cocoa_thread_state, &key, &context)) {
- DLOG("Freeing context %p for thread %p\n", context, (void*)thread);
+ DLOG("Freeing context %p for thread %p\n", (void*)context, (void*)thread);
rb_cocoa_thread_free_context(nsthread,thread,
(struct rb_cocoa_thread_context*) context);
@@ -882,23 +865,21 @@
break;
- case RUBY_THREADSWITCH_SAVE:
- if (!st_lookup(rb_cocoa_thread_state, (st_data_t)thread,
- (st_data_t *)&context)) {
+ case RUBY_EVENT_THREAD_SAVE:
+ if (!st_lookup(rb_cocoa_thread_state, key, &context)) {
- DLOG("Created context before save %p for thread %p\n", context,
+ DLOG("Created context before save %p for thread %p\n", (void*)context,
(void*)thread);
context = rb_cocoa_thread_init_context(nsthread, thread);
- st_insert(rb_cocoa_thread_state, (st_data_t)thread, (st_data_t)context);
+ st_insert(rb_cocoa_thread_state, key, context);
}
- DLOG("Saving context %p for thread %p\n", context, (void*)thread);
+ DLOG("Saving context %p for thread %p\n", (void*)context, (void*)thread);
rb_cocoa_thread_save_context(nsthread,
(struct rb_cocoa_thread_context*) context);
break;
- case RUBY_THREADSWITCH_RESTORE:
- if (st_lookup(rb_cocoa_thread_state, (st_data_t)thread,
- (st_data_t *)&context)) {
+ case RUBY_EVENT_THREAD_RESTORE:
+ if (st_lookup(rb_cocoa_thread_state, key, &context)) {
- DLOG("Restoring context %p for thread %p\n", context, (void*)thread);
+ DLOG("Restoring context %p for thread %p\n", (void*)context, (void*)thread);
rb_cocoa_thread_restore_context(nsthread,
(struct rb_cocoa_thread_context*) context);
@@ -919,23 +900,11 @@
rb_cocoa_thread_debug = getenv("RUBYCOCOA_THREAD_DEBUG") != NULL;
- if (rb_add_threadswitch_hook == NULL) {
+ if (&rb_event_all == NULL || !(rb_event_all & RUBY_EVENT_THREAD_ALL)) {
if (rb_cocoa_thread_debug) {
- NSLog(@"RBCocoaInstallRubyThreadSchedulerHooks: warning: rb_set_cocoa_thread_hooks not present in ruby core");
+ NSLog(@"RBCocoaInstallRubyThreadSchedulerHooks: warning: RUBY_EVENT_THREAD_ALL not present in ruby core");
}
return;
}
- NSSymbol threadswitch_symbol =
- NSLookupAndBindSymbol("_rb_add_threadswitch_hook");
- NSSymbol ruby_init_symbol =
- NSLookupAndBindSymbol("_ruby_init");
-
- if (NSModuleForSymbol(threadswitch_symbol)
- != NSModuleForSymbol(ruby_init_symbol)) {
- NSLog(@"RBCocoaInstallRubyThreadSchedulerHooks: warning: rb_set_cocoa_thread_hooks is linked from a different library (%s) than ruby_init (%s)",
- NSNameOfModule(NSModuleForSymbol(threadswitch_symbol)), NSNameOfModule(NSModuleForSymbol(ruby_init_symbol)));
- return;
- }
-
rb_cocoa_thread_state = st_init_numtable();
rb_cocoa_main_nsthread = [NSThread currentThread];
@@ -978,9 +947,10 @@
/* Finally, register the hook with the ruby core */
- rb_add_threadswitch_hook(rb_cocoa_thread_schedule_hook);
+ rb_add_event_hook(rb_cocoa_thread_schedule_hook, RUBY_EVENT_THREAD_ALL);
rb_cocoa_did_install_thread_hooks = YES;
DLOG("Thread hooks done, main Ruby thread is %p\n",
(void *)rb_thread_current());
+#endif
}
--
--- 僕の前にBugはない。
--- 僕の後ろにBugはできる。
中田 伸悦