前田です。

At Wed, 12 Nov 2003 12:03:28 +0900,
Hidetoshi NAGAI <nagai / ai.kyutech.ac.jp> wrote:
> From: Tietew <tietew-ml-ruby-dev / tietew.net>
> Subject: [ruby-dev:21903] Re: pthread trouble on tcltklib
> Date: Wed, 12 Nov 2003 11:20:18 +0900
> Message-ID: <20031112111119.D6F8.TIETEW-ML-RUBY-DEV / tietew.net>
> > Windows の例ですが,Ruby が走るネイティブスレッドを一つ決め,そ
> > の上で Ruby を走らせています。そのスレッド以外で Ruby に制御は絶
> > 対に渡しません。解決不可能な問題の原因になるので。
> 
> Windows での実装状況を知らないものでお尋ねします.
> tcltklib では Ruby から Tcl/Tk の機能を呼び出すのと同様に
> Tcl/Tk から Ruby の機能を呼び出すようになっているのですが,
> この Tcl/Tk からの呼び出しが,Tcl/Tk 上の複数スレッドから
> 行われた場合にはどのようになるのでしょうか.

Windowsではないですけど、mod_rubyではすべてのネイティブスレッドか
らのRuby機能の呼び出しを、キューを使ってRuby実行用のネイティブス
レッドに転送するようにしてます。
mod_rubyの現在の実装では同時に一つのスレッドの要求しか処理できな
いのですが、Ruby実行用のネイティブスレッド内で複数のRubyスレッド
を動かせば、原理的には同時に複数の要求を処理できると思います。

肝の部分だけ抜き出すとこんな感じです。

typedef VALUE (*ruby_protect_func_t)(VALUE);

typedef struct ruby_request {
    ruby_interp_func_t func; 
    void *arg;
    void *result;
    int state;
    int done;
    apr_thread_cond_t *done_cond;
    struct ruby_request *next;
} ruby_request_t;

static ruby_request_t *ruby_request_queue = NULL;

static void *ruby_thread_start(apr_thread_t *t, void *data)
{
    server_rec *s = (server_rec *) data;
    ruby_request_t *req;

    ruby_init_interpreter(s);

    while (1) {
	apr_thread_mutex_lock(ruby_request_queue_mutex);
	while (ruby_request_queue == NULL)
	    apr_thread_cond_wait(ruby_request_queue_cond,
				 ruby_request_queue_mutex);
	req = ruby_request_queue;
	if (req->func == SHUTDOWN_RUBY_THREAD)
	    break;
	
	req->result =
	    (void *) rb_protect((ruby_protect_func_t) req->func,
				(VALUE) req->arg,
				&req->state);
	ruby_request_queue = ruby_request_queue->next;
	req->done = 1;
	apr_thread_cond_signal(req->done_cond);
	apr_thread_mutex_unlock(ruby_request_queue_mutex);
    }

    ruby_finalize_interpreter();
    req->done = 1;
    apr_thread_cond_signal(req->done_cond);
    apr_thread_mutex_unlock(ruby_request_queue_mutex);
    return NULL;
}

apr_status_t ruby_call_interpreter(pool *p, ruby_interp_func_t func,
				   void *arg, void **result, int *state)
{
    apr_status_t status;
    ruby_request_t *req;

    req = apr_palloc(p, sizeof(ruby_request_t));
    req->func = func;
    req->arg = arg;
    req->result = NULL;
    req->state = 0;
    req->done = 0;
    status = apr_thread_cond_create(&req->done_cond, p);
    if (status != APR_SUCCESS)
	return status;
    req->next = NULL;
    apr_thread_mutex_lock(ruby_request_queue_mutex);
    if (ruby_request_queue)
	ruby_request_queue->next = req;
    else
	ruby_request_queue = req;
    apr_thread_cond_signal(ruby_request_queue_cond);
    while (!req->done)
	apr_thread_cond_wait(req->done_cond, ruby_request_queue_mutex);
    apr_thread_mutex_unlock(ruby_request_queue_mutex);
    if (result)
	*result = req->result;
    if (state)
	*state = req->state;
    return APR_SUCCESS;
}

-- 
前田 修吾