Thread switch hook for RubyCocoa

e$B$J$+$@$G$9!#e(B

Mac OS Xe$BMQ$Ne(Bruby
1.8e$B$K$O!"e(BRubyCocoae$B8~$1$Ke(BApplee$BFH<+$Ne(Bthread_hook
e$B%Q%C%A$H$$$&$b$N$,F~$C$F$$$^$9!#$3$l$Oe(B1.8e$BI8=$Ne(BEVENT_HOOKe$B$H;v<Be(B e$B>eF1$85!G=$G$"$j!"%U%C%/$N%?%$%_%s%0$rDI2C$9$k$@$1$G$9!#e(B RubyKaigie$B$N$H$-$K$A$g$C$He(Bknue$B$5$s$d!"9bHx$5$s!"e(BMacPortse$B%a%s%F%J$Ne(B e$BLZB<$o$5$s$H$bOC$7$?$s$G$9$,!"I8=$KDI2C$7$F$O$I$&$G$7$g$&$+!#e(B

e$BLdBj$H$J$k2DG=@-$H$7$F$Oe(B

  1. e$B%9%l%C%I%9%$%C%A$,$d$dCY$/$J$ke(B
    e$B%U%C%/$,EPO?$5$l$F$$$J$$$H$-$K$O%U%i%0$r%A%’%C%/$9$k$@$1$J$Ne(B
    e$B$G!"%9%l%C%I%9%$%C%A<+BN$KHf$Y$l$P$4$/$o$:$+e(B

  2. e$B%U%C%/$r;HMQ$9$kB&$K$bJQ99$,I,MW$K$J$ke(B
    RubyCocoae$B0J30$K;H$C$F$$$k$b$N$O$J$$$H;W$$$^$9$,!";d$O3NG’$7$Fe(B
    e$B$$$^$;$se(B

e$B$^$?!"e(BRubyCocoae$B$Ne(Bruby
1.9e$BBP1~>u67$b$h$/$o$+$i$J$$$N$G$9$,!"e(B1.9e$B$Ge(B
e$B$b$3$Ne(BTHREAD_SWITCH hooke$B$OI,MW$J$N$G$7$g$&$+!#e(B

e$B0J2<!"e(B1.8e$BMQ$He(BRubyCocoa
1.0.0e$BMQ$N%Q%C%A$G$9!#BP1~%P!<%8%g%s$+$I$&e(B
e$B$+%A%’%C%/$9$k$?$a$K!"e(Brb_add_threadswitch_hook()e$B$NM-L5$NBe$o$j$K!"e(B
rb_event_alle$B$H$$$&JQ?t$NM-L5$H$=$NCM$r$_$k$h$&$K$7$F$$$^$9!#e(B

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
    }

At Tue, 28 Jul 2009 07:36:21 +0900,
Nobuyoshi N. wrote:

で、スレッドスイッチ自体に比べればごくわずか

  1. フックを使用する側にも変更が必要になる
    RubyCocoa以外に使っているものはないと思いますが、私は確認して
    いません

 共に許容範囲だと思います。アプリケーションが使うインターフェース
ではないので、ここのABI非互換性は重大ではないと考えます。

また、RubyCocoaのruby 1.9対応状況もよくわからないのですが、1.9で
もこのTHREAD_SWITCH hookは必要なのでしょうか。

 それが謎なんですよね。下のパッチと共にRubyCocoaの開発チーム問い
合わせる必要がありますね。

以下、1.8用とRubyCocoa 1.0.0用のパッチです。対応バージョンかどう
かチェックするために、rb_add_threadswitch_hook()の有無の代わりに、
rb_event_allという変数の有無とその値をみるようにしています。

 commitしてもらえますか。

e$BLZB<$o!we(BRubyCocoae$B%A!<%`e(B/MacPorts
port:rubye$B%a%s%F%J$G$9!#e(B

e$B%Q%C%A$NFbMF$K$D$$$F$O$3$l$+$i3NG’$7$^$9$N$G!">/$7;~4V$/$@$5$$!#e(B
e$B@h$KEz$($i$l$k$3$H$K$D$$$FJVEz$7$F$*$-$^$9!#e(B

On Tue, 28 Jul 2009 07:36:21 +0900, Nobuyoshi N. wrote:

  1. e$B%9%l%C%I%9%$%C%A$,$d$dCY$/$J$ke(B
    e$B%U%C%/$,EPO?$5$l$F$$$J$$$H$-$K$O%U%i%0$r%A%’%C%/$9$k$@$1$J$Ne(B
    e$B$G!"%9%l%C%I%9%$%C%A<+BN$KHf$Y$l$P$4$/$o$:$+e(B

  2. e$B%U%C%/$r;HMQ$9$kB&$K$bJQ99$,I,MW$K$J$ke(B
    RubyCocoae$B0J30$K;H$C$F$$$k$b$N$O$J$$$H;W$$$^$9$,!";d$O3NG’$7$Fe(B
    e$B$$$^$;$se(B

e$B;d$NCN$C$F$$$kHO0O$G$b!"$[$+$K$O$"$j$^$;$s!#e(B

e$B$^$?!"e(BRubyCocoae$B$Ne(Bruby 1.9e$BBP1~>u67$b$h$/$o$+$i$J$$$N$G$9$,!"e(B1.9e$B$Ge(B
e$B$b$3$Ne(BTHREAD_SWITCH hooke$B$OI,MW$J$N$G$7$g$&$+!#e(B

RubyCocoae$B$Ge(Bruby 1.9e$B$KBP1~$9$kM=Dj$O:#$N$H$3$m$"$j$^$;$s!#e(B
1.9e$B7O$Oe(BMacRubye$B$N<iHwHO0O$H$$$&G’<1$G$9!#e(B
e$B$b$7e(BRubyCocoae$B$re(B1.9e$B$KBP1~$5$;$k$H$-$K$J$C$F$be(Bhooke$B$OITMW$H;W$$$^$9!#e(B

e$BLZB<e(B(e$B$oe(B)e$B$G$9!#e(B

RubyCocoae$B$N$[$&$N%Q%C%A$G$9$,!"e(B#ife$B$He(B#endife$B$N?t$,0lCW$7$^$;$s!#e(B
e$B;d$N$+$s$A$,$$$J$N$+$b$7$l$^$;$s$,!"3NG’$7$F$$$?$@$1$J$$$G$7$g$&$+!)e(B

On Tue, 28 Jul 2009 07:36:21 +0900, Nobuyoshi N. wrote:

e$B$J$+$@$G$9!#e(B

Mac OS Xe$BMQ$Ne(Bruby 1.8e$B$K$O!"e(BRubyCocoae$B8~$1$Ke(BApplee$BFH<+$Ne(Bthread_hook
e$B%Q%C%A$H$$$&$b$N$,F~$C$F$$$^$9!#$3$l$Oe(B1.8e$BI8=$Ne(BEVENT_HOOKe$B$H;v<Be(B e$B>eF1$85!G=$G$"$j!"%U%C%/$N%?%$%_%s%0$rDI2C$9$k$@$1$G$9!#e(B RubyKaigie$B$N$H$-$K$A$g$C$He(Bknue$B$5$s$d!"9bHx$5$s!"e(BMacPortse$B%a%s%F%J$Ne(B e$BLZB<$o$5$s$H$bOC$7$?$s$G$9$,!"I8=$KDI2C$7$F$O$I$&$G$7$g$&$+!#e(B

e$BLZB<$o!we(BRubyCocoae$B$G$9!#e(B

ruby 1.8e$B$Ne(B20090729e$B$Ne(Bsnapshote$B$H!“e(BRubyCocoa
1.0.0e$B$K$J$+$@$5$s$Ne(B
e$B%Q%C%A$r$”$F$?$b$N$GF0:n3NG’$b$7$^$7$?!#LdBj$J$5$=$&$G$9!#e(B
RubyCocoae$B$N$[$&$O!"$$$?$@$$$?%Q%C%A$rD4@0$7$F<h$j9~$_$9$k$3$He(B
e$B$K$7$^$9!#$"$j$,$H$&$4$6$$$^$7$?!*e(B

Rubye$BB&$NJQ99$O!"e(B1.8.8e$B0J9_$GM-8z$K$J$k$H9M$($F$h$$$N$G$7$g$&$+!)e(B

e$B9bHx$G$9!#e(B

On 2009/07/28, at 12:02, Akinori MUSHA wrote:

e$B$^$?!"e(BRubyCocoae$B$Ne(Bruby 1.9e$BBP1~>u67$b$h$/$o$+$i$J$$$N$G$9$,!"e(B1.9e$B$Ge(B
e$B$b$3$Ne(BTHREAD_SWITCH hooke$B$OI,MW$J$N$G$7$g$&$+!#e(B

e$B!!$=$l$,Ff$J$s$G$9$h$M!#2<$N%Q%C%A$H6&$Ke(BRubyCocoae$B$N3+H/%A!<%`Ld$$e(B
e$B9g$o$;$kI,MW$,$"$j$^$9$M!#e(B

e$B;d$+$ie(BRubyCocoae$B$N3+H/%A!<%`$K$*J9$-$7$F$_$h$&$+$H;W$$$^$9!#e(B

  • RubyCocoae$B$Oe(Bruby 1.9e$B$KBP1~$9$k$N$+e(B
  • e$BBP1~$9$k$N$G$"$l$P$3$N%Q%C%A$OI,MW$+e(B

e$B$J$*!“;d$NG’<1$G$Oe(Bruby 1.9e$BBP1~$O$7$J$$$H;W$$$^$9!#e(B
MacRubye$B$,$”$j$^$9$+$i$M!#e(B

e$B0J2<!"e(B1.8e$BMQ$He(BRubyCocoa 1.0.0e$BMQ$N%Q%C%A$G$9!#BP1~%P!<%8%g%s$+$I$&e(B
e$B$+%A%'%C%/$9$k$?$a$K!"e(Brb_add_threadswitch_hook()e$B$NM-L5$NBe$o$j$K!"e(B
rb_event_alle$B$H$$$&JQ?t$NM-L5$H$=$NCM$r$_$k$h$&$K$7$F$$$^$9!#e(B

e$B!!e(Bcommite$B$7$F$b$i$($^$9$+!#e(B

RubyCocoa 1.0.0e$BMQ$N%Q%C%A$b3+H/%A!<%`$NJ}$KJs9p$7$F$*$-$^$9!#e(B

e$B$3$Ne(BMLe$B$rFI$s$G$*$i$l$k$h$&$J5$$,$7$^$9$,e(B…


TAKAO Kouji [email protected]
blog: 高尾宏治日記 on はてな
twitter: takaokouji / projects: ruby, s7-seven

e$B%t%!%s%5%s$G$9!#e(B

e$B:#G/$N#97n$K%j%j!<%9M=Dj$Ne(BMac OS X 10.6(Snow Leopard)e$B$KF~$C$FM_$7$$$N$G!"e(B
1.8.7e$B$K%P%C%/%]!<%H$7$F$b$i$$$?$$$G$9!#e(B
Snow
Leoparde$B$KF~$ke(BRubye$B$O$b$&8GDj$5$l$F$^$9$h!#@H<e@-0J30$OF~$i$J$$$H;W$$$^$9!#e(B

e$B9bHx$G$9!#e(B

On 2009/07/30, at 23:39, kimura wataru wrote:

ruby 1.8e$B$Ne(B20090729e$B$Ne(Bsnapshote$B$H!“e(BRubyCocoa 1.0.0e$B$K$J$+$@$5$s$Ne(B
e$B%Q%C%A$r$”$F$?$b$N$GF0:n3NG’$b$7$^$7$?!#LdBj$J$5$=$&$G$9!#e(B
RubyCocoae$B$N$[$&$O!“$$$?$@$$$?%Q%C%A$rD4@0$7$F<h$j9~$_$9$k$3$He(B
e$B$K$7$^$9!#$”$j$,$H$&$4$6$$$^$7$?!*e(B

e$B$9$P$i$7$$!#e(B

Rubye$BB&$NJQ99$O!"e(B1.8.8e$B0J9_$GM-8z$K$J$k$H9M$($F$h$$$N$G$7$g$&$+!)e(B

e$B:#G/$N#97n$K%j%j!<%9M=Dj$Ne(BMac OS X 10.6(Snow
Leopard)e$B$KF~$C$FM_$7$$$N$G!"e(B
1.8.7e$B$K%P%C%/%]!<%H$7$F$b$i$$$?$$$G$9!#e(B
e$B%P%C%/%]!<%H$7$F$b$i$&$K$O!“e(B
e$B$=$l$J$j$K6/$$M}M3$H$*4j$$$7$^$9$H8@$$B3$1$k:,5$$,$”$l$P$h$$$H;W$C$F$$$^$9!#e(B

e$B$H=q$$$F$_$^$7$?$,!"$3$l$OIT6q9g=$@5$G$O$J$/5!G=DI2C$NMWK>$J$N$G!“e(B
1.8.7e$B$K$OF~$i$J$$$h$&$J5$$,$7$F$-$^$7$?!#e(B
e$B$&!<$s!”$I$&$J$s$G$7$g$&$M!#e(B


TAKAO Kouji [email protected]
blog: 高尾宏治日記 on はてな
twitter: takaokouji / projects: ruby, s7-seven

e$B$J$+$@$G$9!#e(B

At Fri, 31 Jul 2009 10:06:30 +0900,
Vincent I. wrote in [ruby-dev:38970]:

e$B:#G/$N#97n$K%j%j!<%9M=Dj$Ne(BMac OS X 10.6(Snow Leopard)e$B$KF~$C$FM_$7$$$N$G!"e(B
1.8.7e$B$K%P%C%/%]!<%H$7$F$b$i$$$?$$$G$9!#e(B
Snow Leoparde$B$KF~$ke(BRubye$B$O$b$&8GDj$5$l$F$^$9$h!#@H<e@-0J30$OF~$i$J$$$H;W$$$^$9!#e(B

e$B$H$$$&$3$H$O!"$^$H$b$Je(Buniversal binary
supporte$B$r%P%C%/%]!<%H$7$Fe(B
e$B$b$d$C$Q$je(B10.6e$B$K$OF~$i$J$$$H$$$&$3$H$G$9$h$M!#e(B

e$BLZB<$oe(B@MacPorts port:rubye$B%a%s%F%J$G$9!#e(B

10.5e$B$N$H$-$N<B@S$G$9$,!"%Q%C%A%l%Y%k$Oe(B10.5.xe$B$J$I$Ge(B
e$B99?7$5$l$k$3$H$,$"$j$^$7$?!#e(B9e$B7n$K%j%j!<%9$5$l$k!"e(B10.6(10.6.0)e$B$Ke(B
e$B$D$$$F$O$b$&4V$K9g$$$^$;$s$,!“e(B10.6.xe$B$G$OH?1G$5$l$k2DG=@-$b$”$k$H;W$$$^$9!#e(B

e$B%3!<%I$,99?7$5$l$k$N$H!“e(Buniversal
binarye$B$N<B8=<jK!$,JQ99$5$l$k$+$Oe(B
e$BJL$NOC$H$b9M$($i$l$^$9$,!“8=:$Ne(BApplee$BHG$N;EAH$$r0];}$7B3$1$k$N$be(B
e$B<j4V$,$”$k$G$7$g$&$+$i!”@Z$jBX$($k2ACM$O$"$k$H;W$$$^$9!#e(B

MacPortse$BE*$K$b%P%C%/%]!<%H$5$l$k$H$&$l$7$$$G$9!#e(B