* eval.c (rb_add_threadswitch_hook): wrapper for unofficial APIs

in Mac OS X port.  the use of them is strongly discouraged.

	* eval.c (rb_remove_threadswitch_hook): ditto.

git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/branches/ruby_1_8@29326 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
nobu 2010-09-23 18:00:46 +00:00
parent 3a412f2805
commit f5cbe886b6
9 changed files with 281 additions and 3 deletions

View file

@ -1,3 +1,10 @@
Fri Sep 24 03:00:26 2010 Nobuyoshi Nakada <nobu@ruby-lang.org>
* eval.c (rb_add_threadswitch_hook): wrapper for unofficial APIs
in Mac OS X port. the use of them is strongly discouraged.
* eval.c (rb_remove_threadswitch_hook): ditto.
Fri Sep 3 16:42:59 2010 Akinori MUSHA <knu@iDaemons.org>
* parse.y (method_call): Add support for Ruby 1.9 style method

View file

@ -145,6 +145,20 @@ if test $frame_address = yes; then
AC_DEFINE(USE_BUILTIN_FRAME_ADDRESS)
fi
threadswitch_hook=no
AC_ARG_ENABLE(macosx-unofficial-threadswitch-hook,
AS_HELP_STRING([--enable-macosx-unofficial-threadswitch-hook],
[enable Mac OS X unofficial thread switch hook, the use of this option is discouraged.]),
[threadswitch_hook=$enableval])
AS_CASE($threadswitch_hook:$target_os,
[yes:darwin*], [
AC_MSG_WARN([enabled Mac OS X unofficial thread switch hook])
AC_DEFINE(RUBY_ENABLE_MACOSX_UNOFFICIAL_THREADSWITCH)
],
[yes:*], [
AC_MSG_ERROR([--enable-macosx-unofficial-threadswitch-hook is valid only for Mac OS X.])
])
AC_ARG_PROGRAM
dnl Checks for programs.

64
eval.c
View file

@ -2663,6 +2663,70 @@ rb_remove_event_hook(func)
return -1;
}
#if defined __APPLE__ && defined __MACH__ && defined RUBY_ENABLE_MACOSX_UNOFFICIAL_THREADSWITCH
typedef struct threadswitch_hook {
rb_threadswitch_hook_func_t func;
struct threadswitch_hook *next;
} rb_threadswitch_hook_t;
static rb_threadswitch_hook_t *threadswitch_hooks;
static void
call_threadswitch_hook(event, node, thread, mid, klass)
rb_event_t event;
NODE *node;
VALUE thread;
ID mid;
VALUE klass;
{
rb_threadswitch_hook_t *hook = threadswitch_hooks;
rb_threadswitch_event_t thevent = event >> RUBY_THREADSWITCH_SHIFT;
for (; hook; hook = hook->next) {
(*hook->func)(thevent, thread);
}
}
void *
rb_add_threadswitch_hook(func)
rb_threadswitch_hook_func_t func;
{
rb_threadswitch_hook_t *hook;
int new_hook = !threadswitch_hooks;
rb_warn("rb_add_threadswitch_hook is not an official API; use rb_add_event_hook");
hook = ALLOC(rb_threadswitch_hook_t);
hook->func = func;
hook->next = threadswitch_hooks;
threadswitch_hooks = hook;
if (new_hook) {
rb_add_event_hook(call_threadswitch_hook, RUBY_EVENT_THREAD_ALL);
}
return hook;
}
void
rb_remove_threadswitch_hook(handle)
void *handle;
{
rb_threadswitch_hook_t **hook_p, *hook;
for (hook_p = &threadswitch_hooks; *hook_p; hook_p = &hook->next) {
hook = *hook_p;
if (hook == (rb_threadswitch_hook_t*)handle) {
*hook_p = hook->next;
xfree(hook);
if (!threadswitch_hooks) {
rb_remove_event_hook(call_threadswitch_hook);
}
break;
}
}
}
#endif
/*
* call-seq:
* set_trace_func(proc) => proc

View file

@ -0,0 +1,2 @@
require 'mkmf'
create_makefile("-test-/threadswitch/event_hook")

View file

@ -0,0 +1,148 @@
#include <ruby.h>
#include <node.h>
/* copied from eval.c */
static const char *
get_event_name(rb_event_t event)
{
switch (event) {
case RUBY_EVENT_LINE:
return "line";
case RUBY_EVENT_CLASS:
return "class";
case RUBY_EVENT_END:
return "end";
case RUBY_EVENT_CALL:
return "call";
case RUBY_EVENT_RETURN:
return "return";
case RUBY_EVENT_C_CALL:
return "c-call";
case RUBY_EVENT_C_RETURN:
return "c-return";
case RUBY_EVENT_RAISE:
return "raise";
case RUBY_EVENT_THREAD_INIT:
return "thread-init";
case RUBY_EVENT_THREAD_FREE:
return "thread-free";
case RUBY_EVENT_THREAD_SAVE:
return "thread-save";
case RUBY_EVENT_THREAD_RESTORE:
return "thread-restore";
default:
return "unknown";
}
}
static VALUE event_callback;
static void
event_hook(event, node, obj, mid, klass)
rb_event_t event;
NODE *node;
VALUE obj;
ID mid;
VALUE klass;
{
VALUE block = rb_thread_local_aref(rb_curr_thread->thread, event_callback);
if (!NIL_P(block)) {
VALUE args = rb_ary_new();
rb_ary_push(args, rb_str_new2(get_event_name(event)));
rb_ary_push(args, obj);
rb_ary_push(args, ID2SYM(mid));
rb_ary_push(args, klass);
rb_proc_call(block, args);
}
}
static VALUE
add_event_hook(obj)
VALUE obj;
{
rb_add_event_hook(event_hook, RUBY_EVENT_ALL);
return obj;
}
#ifdef RUBY_ENABLE_MACOSX_UNOFFICIAL_THREADSWITCH
#define get_threadswitch_event_name(thevent) get_event_name((thevent) << RUBY_THREADSWITCH_SHIFT)
static void
threadswitch_event_hook(event, thread)
rb_threadswitch_event_t event;
VALUE thread;
{
VALUE block = rb_thread_local_aref(rb_curr_thread->thread, event_callback);
if (!NIL_P(block)) {
VALUE args = rb_ary_new();
rb_ary_push(args, rb_str_new2(get_threadswitch_event_name(event)));
rb_ary_push(args, thread);
rb_proc_call(block, args);
}
}
static VALUE rb_cThreadSwitchHook;
static VALUE
threadswitch_add_event_hook(klass)
VALUE klass;
{
void *handle = rb_add_threadswitch_hook(threadswitch_event_hook);
return Data_Wrap_Struct(klass, 0, rb_remove_threadswitch_hook, handle);
}
static VALUE
threadswitch_remove_event_hook(obj)
VALUE obj;
{
void *handle = DATA_PTR(obj);
DATA_PTR(obj) = 0;
if (handle) {
rb_remove_threadswitch_hook(handle);
}
return obj;
}
static VALUE
restore_hook(arg)
VALUE arg;
{
VALUE *save = (VALUE *)arg;
threadswitch_remove_event_hook(save[0]);
rb_thread_local_aset(rb_curr_thread->thread, event_callback, save[1]);
return Qnil;
}
static VALUE
threadswitch_hook(klass)
VALUE klass;
{
VALUE save[2];
save[1] = rb_thread_local_aref(rb_curr_thread->thread, event_callback);
rb_thread_local_aset(rb_curr_thread->thread, event_callback, rb_block_proc());
save[0] = threadswitch_add_event_hook(klass);
return rb_ensure(rb_yield, save[0], restore_hook, (VALUE)save);
}
static void
Init_threadswitch_hook(mEventHook)
VALUE mEventHook;
{
rb_cThreadSwitchHook = rb_define_class_under(mEventHook, "ThreadSwitch", rb_cObject);
rb_define_singleton_method(rb_cThreadSwitchHook, "add", threadswitch_add_event_hook, 0);
rb_define_singleton_method(rb_cThreadSwitchHook, "hook", threadswitch_hook, 0);
rb_define_method(rb_cThreadSwitchHook, "remove", threadswitch_remove_event_hook, 0);
}
#else
#define Init_threadswitch_hook(mEventHook) (void)(mEventHook)
#endif
void
Init_event_hook()
{
VALUE mEventHook = rb_define_module("EventHook");
event_callback = rb_intern("rb_event_callback");
rb_define_module_function(mEventHook, "hook", add_event_hook, 0);
Init_threadswitch_hook(mEventHook);
}

15
node.h
View file

@ -384,6 +384,21 @@ 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 RUBY_ENABLE_MACOSX_UNOFFICIAL_THREADSWITCH
typedef rb_event_t rb_threadswitch_event_t;
#define RUBY_THREADSWITCH_SHIFT 8
#define RUBY_THREADSWITCH_INIT (RUBY_EVENT_THREAD_INIT>>RUBY_THREADSWITCH_SHIFT)
#define RUBY_THREADSWITCH_FREE (RUBY_EVENT_THREAD_FREE>>RUBY_THREADSWITCH_SHIFT)
#define RUBY_THREADSWITCH_SAVE (RUBY_EVENT_THREAD_SAVE>>RUBY_THREADSWITCH_SHIFT)
#define RUBY_THREADSWITCH_RESTORE (RUBY_EVENT_THREAD_RESTORE>>RUBY_THREADSWITCH_SHIFT)
typedef void (*rb_threadswitch_hook_func_t) _((rb_threadswitch_event_t,VALUE));
DEPRECATED(void *rb_add_threadswitch_hook _((rb_threadswitch_hook_func_t func)));
DEPRECATED(void rb_remove_threadswitch_hook _((void *handle)));
#endif
#if defined(HAVE_GETCONTEXT) && defined(HAVE_SETCONTEXT)
#include <ucontext.h>
#define USE_CONTEXT

View file

@ -0,0 +1,16 @@
require 'test/unit'
require '-test-/threadswitch/event_hook'
require 'ruby/envutil'
class Test_ThreadSwitch < Test::Unit::TestCase
def test_threadswitch_init
threads = []
warning = EnvUtil.verbose_warning {
EventHook::ThreadSwitch.hook {|name, thread|
threads << thread if name == "thread-init"
}
}
assert_match(/not an official API/, warning)
assert_operator(threads, :include?, Thread.current)
end
end

View file

@ -25,6 +25,18 @@ module EnvUtil
end
end
module_function :rubybin
def verbose_warning
class << (stderr = "")
alias write <<
end
stderr, $stderr, verbose, $VERBOSE = $stderr, stderr, $VERBOSE, true
yield stderr
ensure
stderr, $stderr, $VERBOSE = $stderr, stderr, verbose
return stderr
end
module_function :verbose_warning
end
begin

View file

@ -1,7 +1,7 @@
#define RUBY_VERSION "1.8.8"
#define RUBY_RELEASE_DATE "2010-09-03"
#define RUBY_RELEASE_DATE "2010-09-24"
#define RUBY_VERSION_CODE 188
#define RUBY_RELEASE_CODE 20100903
#define RUBY_RELEASE_CODE 20100924
#define RUBY_PATCHLEVEL -1
#define RUBY_VERSION_MAJOR 1
@ -9,7 +9,7 @@
#define RUBY_VERSION_TEENY 8
#define RUBY_RELEASE_YEAR 2010
#define RUBY_RELEASE_MONTH 9
#define RUBY_RELEASE_DAY 3
#define RUBY_RELEASE_DAY 24
#define NO_STRING_LITERAL_CONCATENATION 1
#ifdef RUBY_EXTERN