summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--ChangeLog11
-rw-r--r--configure.in31
-rw-r--r--defines.h4
-rw-r--r--eval_load.c184
-rw-r--r--ext/extmk.rb12
-rw-r--r--intern.h10
-rw-r--r--thread.c213
-rw-r--r--yarvcore.c1
-rw-r--r--yarvcore.h1
9 files changed, 343 insertions, 124 deletions
diff --git a/ChangeLog b/ChangeLog
index 8ec7429ea..2e409a842 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,7 +1,16 @@
-Thu May 3 22:05:40 2007 Nobuyoshi Nakada <nobu@ruby-lang.org>
+Thu May 3 22:20:08 2007 Nobuyoshi Nakada <nobu@ruby-lang.org>
+
+ * 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 <nobu@ruby-lang.org>
* vm_evalbody.ci, insns.def, vm.c, tool/insns2vm.rb (rb_num_t):
diff --git a/configure.in b/configure.in
index 1ed7542ff..223264926 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 9e26a2522..c8c135055 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 3210422bc..f7375381e 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 15d2abb60..f40bfa6ec 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 c4cd7a7d4..e230ac5ce 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 7c563a060..8e76b83b9 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("<preload>"), INT2FIX(1));
+ "end;"), rb_str_new2("<preload>"), 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 47d1a4133..cb66e6789 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 0532ebc71..0ff3e9b01 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];