From 1a89cc308d823033715131c2709a009ee971eb28 Mon Sep 17 00:00:00 2001 From: nobu Date: Thu, 3 May 2007 13:19:11 +0000 Subject: * configure.in, defines.h, eval_load.c (rb_feature_p, rb_provided, search_required, rb_require_safe), ext/extmk.rb: Fix a bug where a statically linked extension cannot be autoloaded. [ruby-dev:30023] / [ruby-dev:30239] * thread.c: added an internal class, Barrier. * yarvcore.h (struct rb_vm_struct): moved loading_table from global. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@12246 b2dd03c8-39d4-4d8f-98ff-823fe69b080e --- ChangeLog | 11 ++- configure.in | 31 ++++----- defines.h | 4 ++ eval_load.c | 184 +++++++++++++++++++++++++++++++-------------------- ext/extmk.rb | 12 ++-- intern.h | 10 +++ thread.c | 213 +++++++++++++++++++++++++++++++++++++++++++++++++++-------- yarvcore.c | 1 + yarvcore.h | 1 + 9 files changed, 343 insertions(+), 124 deletions(-) diff --git a/ChangeLog b/ChangeLog index 8ec7429ea2..2e409a8423 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,7 +1,16 @@ -Thu May 3 22:05:40 2007 Nobuyoshi Nakada +Thu May 3 22:20:08 2007 Nobuyoshi Nakada + + * configure.in, defines.h, eval_load.c (rb_feature_p, rb_provided, + search_required, rb_require_safe), ext/extmk.rb: Fix + a bug where a statically linked extension cannot be autoloaded. + [ruby-dev:30023] / [ruby-dev:30239] + + * thread.c: added an internal class, Barrier. * thread.c: copied rdocs from fastthread. + * yarvcore.h (struct rb_vm_struct): moved loading_table from global. + Thu May 3 18:10:12 2007 Nobuyoshi Nakada * vm_evalbody.ci, insns.def, vm.c, tool/insns2vm.rb (rb_num_t): diff --git a/configure.in b/configure.in index 1ed7542ffb..2232649263 100644 --- a/configure.in +++ b/configure.in @@ -1137,28 +1137,25 @@ if test "$dln_a_out_works" = yes; then STATIC=-Bstatic fi DLEXT=so - AC_DEFINE(DLEXT, ".so") CCDLFLAGS= else case "$target_os" in - hpux*) DLEXT=sl - AC_DEFINE(DLEXT, ".sl");; - nextstep*) DLEXT=bundle - AC_DEFINE(DLEXT, ".bundle");; - openstep*) DLEXT=bundle - AC_DEFINE(DLEXT, ".bundle");; - rhapsody*) DLEXT=bundle - AC_DEFINE(DLEXT, ".bundle");; - darwin*) DLEXT=bundle - AC_DEFINE(DLEXT, ".bundle");; - os2-emx*) DLEXT=dll - AC_DEFINE(DLEXT, ".dll");; - cygwin*|mingw*) DLEXT=so - AC_DEFINE(DLEXT, ".so");; - *) DLEXT=so - AC_DEFINE(DLEXT, ".so");; + hpux*) DLEXT=sl;; + nextstep*) DLEXT=bundle;; + openstep*) DLEXT=bundle;; + rhapsody*) DLEXT=bundle;; + darwin*) DLEXT=bundle;; + os2-emx*) DLEXT=dll;; + cygwin*|mingw*) DLEXT=so;; + *) DLEXT=so;; esac fi +len=2 # .rb +n=`expr "$DLEXT" : '.*'`; test "$n" -gt "$len" && len=$n +n=`expr "$DLEXT2" : '.*'`; test "$n" -gt "$len" && len=$n +AC_DEFINE_UNQUOTED(DLEXT_MAXLEN, `expr $len + 1`) +test ".$DLEXT" = "." || AC_DEFINE_UNQUOTED(DLEXT, ".$DLEXT") +test ".$DLEXT2" = "." || AC_DEFINE_UNQUOTED(DLEXT2, ".$DLEXT2") AC_SUBST(STRIP)dnl if test "$with_dln_a_out" = yes; then diff --git a/defines.h b/defines.h index 9e26a2522b..c8c1350554 100644 --- a/defines.h +++ b/defines.h @@ -257,6 +257,10 @@ void rb_ia64_flushrs(void); #define ENV_IGNORECASE #endif +#ifndef DLEXT_MAXLEN +#define DLEXT_MAXLEN 4 +#endif + #ifndef RUBY_PLATFORM #define RUBY_PLATFORM "unknown-unknown" #endif diff --git a/eval_load.c b/eval_load.c index 3210422bc4..f7375381ec 100644 --- a/eval_load.c +++ b/eval_load.c @@ -7,7 +7,6 @@ extern VALUE ruby_top_self; VALUE ruby_dln_librefs; -static st_table *loading_tbl; #define IS_SOEXT(e) (strcmp(e, ".so") == 0 || strcmp(e, ".o") == 0) #ifdef DLEXT2 @@ -16,18 +15,34 @@ static st_table *loading_tbl; #define IS_DLEXT(e) (strcmp(e, DLEXT) == 0) #endif + +static const char *const loadable_ext[] = { + ".rb", DLEXT, +#ifdef DLEXT2 + DLEXT2, +#endif + 0 +}; + static VALUE get_loaded_features(void) { return GET_VM()->loaded_features; } +static st_table * +get_loading_table(void) +{ + return GET_VM()->loading_table; +} + static int rb_feature_p(const char *feature, const char *ext, int rb) { - VALUE v; - char *f, *e; + VALUE v, features; + const char *f, *e; long i, len, elen; + st_table *loading_tbl; if (ext) { len = ext - feature; @@ -37,18 +52,17 @@ rb_feature_p(const char *feature, const char *ext, int rb) len = strlen(feature); elen = 0; } - for (i = 0; i < RARRAY_LEN(get_loaded_features()); ++i) { - v = RARRAY_PTR(get_loaded_features())[i]; + features = get_loaded_features(); + for (i = 0; i < RARRAY_LEN(features); ++i) { + v = RARRAY_PTR(features)[i]; f = StringValuePtr(v); - if (strncmp(f, feature, len) != 0) + if (RSTRING_LEN(v) < len || strncmp(f, feature, len) != 0) continue; if (!*(e = f + len)) { - if (ext) - continue; + if (ext) continue; return 'u'; } - if (*e != '.') - continue; + if (*e != '.') continue; if ((!rb || !ext) && (IS_SOEXT(e) || IS_DLEXT(e))) { return 's'; } @@ -56,46 +70,47 @@ rb_feature_p(const char *feature, const char *ext, int rb) return 'r'; } } + loading_tbl = get_loading_table(); + if (loading_tbl) { + if (st_lookup(loading_tbl, (st_data_t)feature, 0)) { + if (!ext) return 'u'; + return strcmp(ext, ".rb") ? 's' : 'r'; + } + else { + char *buf; + + if (ext && *ext) return 0; + buf = ALLOCA_N(char, len + DLEXT_MAXLEN + 1); + MEMCPY(buf, feature, char, len); + for (i = 0; (e = loadable_ext[i]) != 0; i++) { + strncpy(buf + len, e, DLEXT_MAXLEN + 1); + if (st_lookup(loading_tbl, (st_data_t)buf, 0)) { + return i ? 's' : 'r'; + } + } + } + } return 0; } -static const char *const loadable_ext[] = { - ".rb", DLEXT, -#ifdef DLEXT2 - DLEXT2, -#endif - 0 -}; - -static int search_required _((VALUE, VALUE *)); - int rb_provided(const char *feature) { - int i; - char *buf; - VALUE fname; + const char *ext = strrchr(feature, '.'); - if (rb_feature_p(feature, 0, Qfalse)) - return Qtrue; - if (loading_tbl) { - if (st_lookup(loading_tbl, (st_data_t) feature, 0)) - return Qtrue; - buf = ALLOCA_N(char, strlen(feature) + 8); - strcpy(buf, feature); - for (i = 0; loadable_ext[i]; i++) { - strcpy(buf + strlen(feature), loadable_ext[i]); - if (st_lookup(loading_tbl, (st_data_t) buf, 0)) - return Qtrue; + if (ext && !strchr(ext, '/')) { + if (strcmp(".rb", ext) == 0) { + if (rb_feature_p(feature, ext, Qtrue)) return Qtrue; + return Qfalse; + } + else if (IS_SOEXT(ext) || IS_DLEXT(ext)) { + if (rb_feature_p(feature, ext, Qfalse)) return Qtrue; + return Qfalse; } } - if (search_required(rb_str_new2(feature), &fname)) { - feature = RSTRING_PTR(fname); - if (rb_feature_p(feature, 0, Qfalse)) - return Qtrue; - if (loading_tbl && st_lookup(loading_tbl, (st_data_t) feature, 0)) - return Qtrue; - } + if (rb_feature_p(feature, feature + strlen(feature), Qtrue)) + return Qtrue; + return Qfalse; } @@ -237,21 +252,43 @@ rb_f_load(argc, argv) return Qtrue; } -static int -load_wait(char *ftptr) +static char * +load_lock(const char *ftptr) { - st_data_t th; - if (!loading_tbl) { - return Qfalse; - } - if (!st_lookup(loading_tbl, (st_data_t) ftptr, &th)) { - return Qfalse; + st_data_t data; + st_table *loading_tbl = get_loading_table(); + + if (!loading_tbl || !st_lookup(loading_tbl, (st_data_t)ftptr, &data)) { + /* loading ruby library should be serialized. */ + if (!loading_tbl) { + GET_VM()->loading_table = loading_tbl = st_init_strtable(); + } + /* partial state */ + ftptr = ruby_strdup(ftptr); + data = (st_data_t)rb_barrier_new(); + st_insert(loading_tbl, (st_data_t)ftptr, data); + return (char *)ftptr; } + rb_barrier_wait((VALUE)data); + return 0; +} - /* TODO: write wait routine */ - return Qtrue; +static void +load_unlock(const char *ftptr) +{ + if (ftptr) { + st_data_t key = (st_data_t)ftptr; + st_data_t data; + st_table *loading_tbl = get_loading_table(); + + if (st_delete(loading_tbl, &key, &data)) { + free((char *)key); + rb_barrier_release((VALUE)data); + } + } } + /* * call-seq: * require(string) => true or false @@ -346,16 +383,16 @@ search_required(VALUE fname, VALUE *path) type = rb_find_file_ext(&tmp, loadable_ext); tmp = rb_file_expand_path(tmp, Qnil); switch (type) { - case 0: + case 0: ftptr = RSTRING_PTR(tmp); if (ft) break; return rb_feature_p(ftptr, 0, Qfalse); - default: + default: if (ft) break; - case 1: + case 1: ext = strrchr(ftptr = RSTRING_PTR(tmp), '.'); if (rb_feature_p(ftptr, ext, !--type)) break; @@ -375,8 +412,8 @@ VALUE rb_require_safe(VALUE fname, int safe) { VALUE result = Qnil; - volatile VALUE errinfo = GET_THREAD()->errinfo; rb_thread_t *th = GET_THREAD(); + volatile VALUE errinfo = th->errinfo; int state; struct { NODE *node; @@ -397,25 +434,17 @@ rb_require_safe(VALUE fname, int safe) *(volatile VALUE *)&fname = rb_str_new4(fname); found = search_required(fname, &path); if (found) { - if (!path || load_wait(RSTRING_PTR(path))) { + if (!path || !(ftptr = load_lock(RSTRING_PTR(path)))) { result = Qfalse; } else { rb_set_safe_level_force(0); switch (found) { - case 'r': - /* loading ruby library should be serialized. */ - if (!loading_tbl) { - loading_tbl = st_init_strtable(); - } - /* partial state */ - ftptr = ruby_strdup(RSTRING_PTR(path)); - st_insert(loading_tbl, (st_data_t) ftptr, - (st_data_t) GET_THREAD()->self); + case 'r': rb_load(path, 0); break; - case 's': + case 's': ruby_current_node = 0; ruby_sourcefile = rb_source_filename(RSTRING_PTR(path)); ruby_sourceline = 0; @@ -430,12 +459,8 @@ rb_require_safe(VALUE fname, int safe) } } POP_TAG(); + load_unlock(ftptr); - if (ftptr) { - if (st_delete(loading_tbl, (st_data_t *) & ftptr, 0)) { /* loading done */ - free(ftptr); - } - } ruby_current_node = saved.node; rb_set_safe_level_force(saved.safe); if (state) { @@ -459,6 +484,23 @@ rb_require(const char *fname) return rb_require_safe(fn, rb_safe_level()); } +void +ruby_init_ext(const char *name, void (*init)(void)) +{ + rb_control_frame_t *frame = GET_THREAD()->cfp; + + ruby_current_node = 0; + ruby_sourcefile = rb_source_filename(name); + ruby_sourceline = 0; + frame->method_id = 0; + SCOPE_SET(NOEX_PUBLIC); + if (load_lock(name)) { + (*init)(); + rb_provide(name); + load_unlock(name); + } +} + /* * call-seq: * mod.autoload(name, filename) => nil diff --git a/ext/extmk.rb b/ext/extmk.rb index 15d2abb608..f40bfa6ecf 100644 --- a/ext/extmk.rb +++ b/ext/extmk.rb @@ -452,14 +452,14 @@ unless $extlist.empty? src = %{\ #include "ruby.h" -#define init(func, name) { \ - void func _((void)); \ - ruby_sourcefile = src = rb_source_filename(name); \ - func(); \ - rb_provide(src); \ +#define init(func, name) { \\ + extern void func _((void)); \\ + ruby_init_ext(name, func); \\ } -void Init_ext _((void))\n{\n char *src;#$extinit} +void ruby_init_ext _((const char *name, void (*init)(void))); + +void Init_ext _((void))\n{\n#$extinit} } if !modified?(extinit.c, MTIMES) || IO.read(extinit.c) != src open(extinit.c, "w") {|fe| fe.print src} diff --git a/intern.h b/intern.h index c4cd7a7d48..e230ac5ce6 100644 --- a/intern.h +++ b/intern.h @@ -538,6 +538,16 @@ typedef VALUE rb_blocking_function_t(rb_thread_t *th, void *); VALUE rb_thread_blocking_region(rb_blocking_function_t *func, void *data, rb_unblock_function_t *ubf); #define RB_UBF_DFL ((rb_unblock_function_t *)-1) +VALUE rb_mutex_new(void); +VALUE rb_mutex_locked_p(VALUE mutex); +VALUE rb_mutex_try_lock(VALUE mutex); +VALUE rb_mutex_lock(VALUE mutex); +VALUE rb_mutex_unlock(VALUE mutex); +VALUE rb_mutex_sleep(VALUE self, VALUE timeout); +VALUE rb_mutex_synchronize(VALUE self); +VALUE rb_barrier_new(void); +VALUE rb_barrier_wait(VALUE self); +VALUE rb_barrier_release(VALUE self); /* time.c */ VALUE rb_time_new(time_t, time_t); /* variable.c */ diff --git a/thread.c b/thread.c index 7c563a060c..8e76b83b9f 100644 --- a/thread.c +++ b/thread.c @@ -53,6 +53,9 @@ #define THREAD_DEBUG 0 #endif +VALUE rb_cMutex; +VALUE rb_cBarrier; + static void sleep_timeval(rb_thread_t *th, struct timeval time); static void sleep_wait_for_interrupt(rb_thread_t *th, double sleepsec); static void sleep_forever(rb_thread_t *th); @@ -2173,8 +2176,8 @@ rb_mutex_new(void) * * Returns +true+ if this lock is currently held by some thread. */ -static VALUE -mutex_locked_p(VALUE self) +VALUE +rb_mutex_locked_p(VALUE self) { mutex_t *mutex; GetMutexVal(self, mutex); @@ -2188,8 +2191,8 @@ mutex_locked_p(VALUE self) * Attempts to obtain the lock and returns immediately. Returns +true+ if the * lock was granted. */ -static VALUE -mutex_try_lock(VALUE self) +VALUE +rb_mutex_try_lock(VALUE self) { mutex_t *mutex; GetMutexVal(self, mutex); @@ -2214,8 +2217,8 @@ mutex_try_lock(VALUE self) * Attempts to grab the lock and waits if it isn't available. * Raises +ThreadError+ if +mutex+ was locked by the current thread. */ -static VALUE -mutex_lock(VALUE self) +VALUE +rb_mutex_lock(VALUE self) { mutex_t *mutex; GetMutexVal(self, mutex); @@ -2242,8 +2245,8 @@ mutex_lock(VALUE self) * Releases the lock. * Raises +ThreadError+ if +mutex+ wasn't locked by the current thread. */ -static VALUE -mutex_unlock(VALUE self) +VALUE +rb_mutex_unlock(VALUE self) { mutex_t *mutex; GetMutexVal(self, mutex); @@ -2257,6 +2260,28 @@ mutex_unlock(VALUE self) return self; } +VALUE +rb_mutex_sleep(VALUE self, VALUE timeout) +{ + time_t beg, end; + struct timeval t; + + if (!NIL_P(timeout)) { + t = rb_time_interval(timeout); + } + rb_mutex_unlock(self); + beg = time(0); + if (NIL_P(timeout)) { + rb_thread_sleep_forever(); + } + else { + rb_thread_wait_for(t); + } + rb_mutex_lock(self); + end = time(0) - beg; + return INT2FIX(end); +} + /* * call-seq: * mutex.sleep(timeout = nil) => self @@ -2268,22 +2293,153 @@ mutex_unlock(VALUE self) static VALUE mutex_sleep(int argc, VALUE *argv, VALUE self) { - int beg, end; - mutex_unlock(self); + VALUE timeout; - beg = time(0); - if (argc == 0) { - rb_thread_sleep_forever(); + rb_scan_args(argc, argv, "01", &timeout); + return rb_mutex_sleep(self, timeout); +} + +/* + * call-seq: + * mutex.synchronize { ... } => result of the block + * + * Obtains a lock, runs the block, and releases the lock when the block + * completes. See the example under +Mutex+. + */ + +VALUE +rb_thread_synchronize(VALUE mutex, VALUE (*func)(VALUE arg), VALUE arg) +{ + rb_mutex_lock(mutex); + return rb_ensure(func, arg, rb_mutex_unlock, mutex); +} + +/* + * Document-class: Barrier + */ +typedef struct rb_thread_list_struct rb_thread_list_t; + +struct rb_thread_list_struct { + rb_thread_t *th; + rb_thread_list_t *next; +}; + +static void +thlist_mark(void *ptr) +{ + rb_thread_list_t *q = ptr; + + for (; q; q = q->next) { + rb_gc_mark(q->th->self); } - else if (argc == 1) { - rb_thread_wait_for(rb_time_interval(argv[0])); +} + +static void +thlist_free(void *ptr) +{ + rb_thread_list_t *q = ptr, *next; + + for (; q; q = next) { + next = q->next; + ruby_xfree(q); + } +} + +static int +thlist_signal(rb_thread_list_t **list, unsigned int maxth) +{ + int woken = 0; + rb_thread_list_t *q; + + while (q = *list) { + rb_thread_t *th = q->th; + + *list = q->next; + ruby_xfree(q); + if (th->status != THREAD_KILLED) { + rb_thread_ready(th); + if (++woken >= maxth && maxth) break; + } + } + return woken; +} + +typedef struct { + rb_thread_t *owner; + rb_thread_list_t *waiting, **tail; +} rb_barrier_t; + +static void +barrier_mark(void *ptr) +{ + rb_barrier_t *b = ptr; + + if (b->owner) rb_gc_mark(b->owner->self); + thlist_mark(b->waiting); +} + +static void +barrier_free(void *ptr) +{ + rb_barrier_t *b = ptr; + + b->owner = 0; + thlist_free(b->waiting); + b->waiting = 0; + ruby_xfree(ptr); +} + +static VALUE +barrier_alloc(VALUE klass) +{ + VALUE volatile obj; + rb_barrier_t *barrier; + + obj = Data_Make_Struct(klass, rb_barrier_t, + barrier_mark, barrier_free, barrier); + barrier->owner = GET_THREAD(); + barrier->waiting = 0; + barrier->tail = &barrier->waiting; + return obj; +} + +VALUE +rb_barrier_new(void) +{ + return barrier_alloc(rb_cBarrier); +} + +VALUE +rb_barrier_wait(VALUE self) +{ + rb_barrier_t *barrier; + rb_thread_list_t *q; + + Data_Get_Struct(self, rb_barrier_t, barrier); + if (!barrier->owner || barrier->owner->status == THREAD_KILLED) { + barrier->owner = 0; + thlist_signal(&barrier->waiting, 0); } else { - rb_raise(rb_eArgError, "wrong number of arguments"); + *barrier->tail = q = ALLOC(rb_thread_list_t); + q->th = GET_THREAD(); + q->next = 0; + barrier->tail = &q->next; + rb_thread_sleep_forever(); } - mutex_lock(self); - end = time(0) - beg; - return INT2FIX(end); + return self; +} + +VALUE +rb_barrier_release(VALUE self) +{ + rb_barrier_t *barrier; + unsigned int n; + + Data_Get_Struct(self, rb_barrier_t, barrier); + barrier->owner = 0; + n = thlist_signal(&barrier->waiting, 0); + return n ? UINT2NUM(n) : Qfalse; } @@ -2770,7 +2926,6 @@ void Init_Thread(void) { VALUE cThGroup; - VALUE cMutex; rb_define_singleton_method(rb_cThread, "new", thread_s_new, -2); rb_define_singleton_method(rb_cThread, "start", thread_s_new, -2); @@ -2828,18 +2983,18 @@ Init_Thread(void) rb_define_const(cThGroup, "Default", th->thgroup); } - cMutex = rb_define_class("Mutex", rb_cObject); - rb_define_alloc_func(cMutex, mutex_alloc); - rb_define_method(cMutex, "initialize", mutex_initialize, 0); - rb_define_method(cMutex, "locked?", mutex_locked_p, 0); - rb_define_method(cMutex, "try_lock", mutex_try_lock, 0); - rb_define_method(cMutex, "lock", mutex_lock, 0); - rb_define_method(cMutex, "unlock", mutex_unlock, 0); - rb_define_method(cMutex, "sleep", mutex_sleep, -1); + rb_cMutex = rb_define_class("Mutex", rb_cObject); + rb_define_alloc_func(rb_cMutex, mutex_alloc); + rb_define_method(rb_cMutex, "initialize", mutex_initialize, 0); + rb_define_method(rb_cMutex, "locked?", rb_mutex_locked_p, 0); + rb_define_method(rb_cMutex, "try_lock", rb_mutex_try_lock, 0); + rb_define_method(rb_cMutex, "lock", rb_mutex_lock, 0); + rb_define_method(rb_cMutex, "unlock", rb_mutex_unlock, 0); + rb_define_method(rb_cMutex, "sleep", mutex_sleep, -1); yarvcore_eval(Qnil, rb_str_new2( "class Mutex;" " def synchronize; self.lock; yield; ensure; self.unlock; end;" - "end;") , rb_str_new2(""), INT2FIX(1)); + "end;"), rb_str_new2(""), INT2FIX(1)); recursive_key = rb_intern("__recursive_key__"); rb_eThreadError = rb_define_class("ThreadError", rb_eStandardError); diff --git a/yarvcore.c b/yarvcore.c index 47d1a41330..cb66e67892 100644 --- a/yarvcore.c +++ b/yarvcore.c @@ -190,6 +190,7 @@ vm_mark(void *ptr) MARK_UNLESS_NULL(vm->mark_object_ary); MARK_UNLESS_NULL(vm->last_status); MARK_UNLESS_NULL(vm->loaded_features); + if (vm->loading_table) rb_mark_tbl(vm->loading_table); mark_event_hooks(vm->event_hooks); } diff --git a/yarvcore.h b/yarvcore.h index 0532ebc71a..0ff3e9b011 100644 --- a/yarvcore.h +++ b/yarvcore.h @@ -353,6 +353,7 @@ typedef struct rb_vm_struct { /* load */ VALUE loaded_features; + struct st_table *loading_table; /* signal */ rb_atomic_t signal_buff[RUBY_NSIG]; -- cgit v1.2.3