mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00
* 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
This commit is contained in:
parent
5bfe949dd5
commit
1a89cc308d
9 changed files with 368 additions and 149 deletions
11
ChangeLog
11
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.
|
* 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>
|
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):
|
* vm_evalbody.ci, insns.def, vm.c, tool/insns2vm.rb (rb_num_t):
|
||||||
|
|
31
configure.in
31
configure.in
|
@ -1137,28 +1137,25 @@ if test "$dln_a_out_works" = yes; then
|
||||||
STATIC=-Bstatic
|
STATIC=-Bstatic
|
||||||
fi
|
fi
|
||||||
DLEXT=so
|
DLEXT=so
|
||||||
AC_DEFINE(DLEXT, ".so")
|
|
||||||
CCDLFLAGS=
|
CCDLFLAGS=
|
||||||
else
|
else
|
||||||
case "$target_os" in
|
case "$target_os" in
|
||||||
hpux*) DLEXT=sl
|
hpux*) DLEXT=sl;;
|
||||||
AC_DEFINE(DLEXT, ".sl");;
|
nextstep*) DLEXT=bundle;;
|
||||||
nextstep*) DLEXT=bundle
|
openstep*) DLEXT=bundle;;
|
||||||
AC_DEFINE(DLEXT, ".bundle");;
|
rhapsody*) DLEXT=bundle;;
|
||||||
openstep*) DLEXT=bundle
|
darwin*) DLEXT=bundle;;
|
||||||
AC_DEFINE(DLEXT, ".bundle");;
|
os2-emx*) DLEXT=dll;;
|
||||||
rhapsody*) DLEXT=bundle
|
cygwin*|mingw*) DLEXT=so;;
|
||||||
AC_DEFINE(DLEXT, ".bundle");;
|
*) DLEXT=so;;
|
||||||
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");;
|
|
||||||
esac
|
esac
|
||||||
fi
|
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
|
AC_SUBST(STRIP)dnl
|
||||||
if test "$with_dln_a_out" = yes; then
|
if test "$with_dln_a_out" = yes; then
|
||||||
|
|
|
@ -257,6 +257,10 @@ void rb_ia64_flushrs(void);
|
||||||
#define ENV_IGNORECASE
|
#define ENV_IGNORECASE
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifndef DLEXT_MAXLEN
|
||||||
|
#define DLEXT_MAXLEN 4
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifndef RUBY_PLATFORM
|
#ifndef RUBY_PLATFORM
|
||||||
#define RUBY_PLATFORM "unknown-unknown"
|
#define RUBY_PLATFORM "unknown-unknown"
|
||||||
#endif
|
#endif
|
||||||
|
|
234
eval_load.c
234
eval_load.c
|
@ -7,7 +7,6 @@
|
||||||
extern VALUE ruby_top_self;
|
extern VALUE ruby_top_self;
|
||||||
|
|
||||||
VALUE ruby_dln_librefs;
|
VALUE ruby_dln_librefs;
|
||||||
static st_table *loading_tbl;
|
|
||||||
|
|
||||||
#define IS_SOEXT(e) (strcmp(e, ".so") == 0 || strcmp(e, ".o") == 0)
|
#define IS_SOEXT(e) (strcmp(e, ".so") == 0 || strcmp(e, ".o") == 0)
|
||||||
#ifdef DLEXT2
|
#ifdef DLEXT2
|
||||||
|
@ -16,48 +15,6 @@ static st_table *loading_tbl;
|
||||||
#define IS_DLEXT(e) (strcmp(e, DLEXT) == 0)
|
#define IS_DLEXT(e) (strcmp(e, DLEXT) == 0)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static VALUE
|
|
||||||
get_loaded_features(void)
|
|
||||||
{
|
|
||||||
return GET_VM()->loaded_features;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
rb_feature_p(const char *feature, const char *ext, int rb)
|
|
||||||
{
|
|
||||||
VALUE v;
|
|
||||||
char *f, *e;
|
|
||||||
long i, len, elen;
|
|
||||||
|
|
||||||
if (ext) {
|
|
||||||
len = ext - feature;
|
|
||||||
elen = strlen(ext);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
len = strlen(feature);
|
|
||||||
elen = 0;
|
|
||||||
}
|
|
||||||
for (i = 0; i < RARRAY_LEN(get_loaded_features()); ++i) {
|
|
||||||
v = RARRAY_PTR(get_loaded_features())[i];
|
|
||||||
f = StringValuePtr(v);
|
|
||||||
if (strncmp(f, feature, len) != 0)
|
|
||||||
continue;
|
|
||||||
if (!*(e = f + len)) {
|
|
||||||
if (ext)
|
|
||||||
continue;
|
|
||||||
return 'u';
|
|
||||||
}
|
|
||||||
if (*e != '.')
|
|
||||||
continue;
|
|
||||||
if ((!rb || !ext) && (IS_SOEXT(e) || IS_DLEXT(e))) {
|
|
||||||
return 's';
|
|
||||||
}
|
|
||||||
if ((rb || !ext) && (strcmp(e, ".rb") == 0)) {
|
|
||||||
return 'r';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const char *const loadable_ext[] = {
|
static const char *const loadable_ext[] = {
|
||||||
".rb", DLEXT,
|
".rb", DLEXT,
|
||||||
|
@ -67,35 +24,93 @@ static const char *const loadable_ext[] = {
|
||||||
0
|
0
|
||||||
};
|
};
|
||||||
|
|
||||||
static int search_required _((VALUE, VALUE *));
|
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, features;
|
||||||
|
const char *f, *e;
|
||||||
|
long i, len, elen;
|
||||||
|
st_table *loading_tbl;
|
||||||
|
|
||||||
|
if (ext) {
|
||||||
|
len = ext - feature;
|
||||||
|
elen = strlen(ext);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
len = strlen(feature);
|
||||||
|
elen = 0;
|
||||||
|
}
|
||||||
|
features = get_loaded_features();
|
||||||
|
for (i = 0; i < RARRAY_LEN(features); ++i) {
|
||||||
|
v = RARRAY_PTR(features)[i];
|
||||||
|
f = StringValuePtr(v);
|
||||||
|
if (RSTRING_LEN(v) < len || strncmp(f, feature, len) != 0)
|
||||||
|
continue;
|
||||||
|
if (!*(e = f + len)) {
|
||||||
|
if (ext) continue;
|
||||||
|
return 'u';
|
||||||
|
}
|
||||||
|
if (*e != '.') continue;
|
||||||
|
if ((!rb || !ext) && (IS_SOEXT(e) || IS_DLEXT(e))) {
|
||||||
|
return 's';
|
||||||
|
}
|
||||||
|
if ((rb || !ext) && (strcmp(e, ".rb") == 0)) {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
rb_provided(const char *feature)
|
rb_provided(const char *feature)
|
||||||
{
|
{
|
||||||
int i;
|
const char *ext = strrchr(feature, '.');
|
||||||
char *buf;
|
|
||||||
VALUE fname;
|
|
||||||
|
|
||||||
if (rb_feature_p(feature, 0, Qfalse))
|
if (ext && !strchr(ext, '/')) {
|
||||||
return Qtrue;
|
if (strcmp(".rb", ext) == 0) {
|
||||||
if (loading_tbl) {
|
if (rb_feature_p(feature, ext, Qtrue)) return Qtrue;
|
||||||
if (st_lookup(loading_tbl, (st_data_t) feature, 0))
|
return Qfalse;
|
||||||
return Qtrue;
|
}
|
||||||
buf = ALLOCA_N(char, strlen(feature) + 8);
|
else if (IS_SOEXT(ext) || IS_DLEXT(ext)) {
|
||||||
strcpy(buf, feature);
|
if (rb_feature_p(feature, ext, Qfalse)) return Qtrue;
|
||||||
for (i = 0; loadable_ext[i]; i++) {
|
return Qfalse;
|
||||||
strcpy(buf + strlen(feature), loadable_ext[i]);
|
|
||||||
if (st_lookup(loading_tbl, (st_data_t) buf, 0))
|
|
||||||
return Qtrue;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (search_required(rb_str_new2(feature), &fname)) {
|
if (rb_feature_p(feature, feature + strlen(feature), Qtrue))
|
||||||
feature = RSTRING_PTR(fname);
|
return Qtrue;
|
||||||
if (rb_feature_p(feature, 0, Qfalse))
|
|
||||||
return Qtrue;
|
|
||||||
if (loading_tbl && st_lookup(loading_tbl, (st_data_t) feature, 0))
|
|
||||||
return Qtrue;
|
|
||||||
}
|
|
||||||
return Qfalse;
|
return Qfalse;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -237,21 +252,43 @@ rb_f_load(argc, argv)
|
||||||
return Qtrue;
|
return Qtrue;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static char *
|
||||||
load_wait(char *ftptr)
|
load_lock(const char *ftptr)
|
||||||
{
|
{
|
||||||
st_data_t th;
|
st_data_t data;
|
||||||
if (!loading_tbl) {
|
st_table *loading_tbl = get_loading_table();
|
||||||
return Qfalse;
|
|
||||||
}
|
|
||||||
if (!st_lookup(loading_tbl, (st_data_t) ftptr, &th)) {
|
|
||||||
return Qfalse;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* TODO: write wait routine */
|
if (!loading_tbl || !st_lookup(loading_tbl, (st_data_t)ftptr, &data)) {
|
||||||
return Qtrue;
|
/* 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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:
|
* call-seq:
|
||||||
* require(string) => true or false
|
* require(string) => true or false
|
||||||
|
@ -346,16 +383,16 @@ search_required(VALUE fname, VALUE *path)
|
||||||
type = rb_find_file_ext(&tmp, loadable_ext);
|
type = rb_find_file_ext(&tmp, loadable_ext);
|
||||||
tmp = rb_file_expand_path(tmp, Qnil);
|
tmp = rb_file_expand_path(tmp, Qnil);
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case 0:
|
case 0:
|
||||||
ftptr = RSTRING_PTR(tmp);
|
ftptr = RSTRING_PTR(tmp);
|
||||||
if (ft)
|
if (ft)
|
||||||
break;
|
break;
|
||||||
return rb_feature_p(ftptr, 0, Qfalse);
|
return rb_feature_p(ftptr, 0, Qfalse);
|
||||||
|
|
||||||
default:
|
default:
|
||||||
if (ft)
|
if (ft)
|
||||||
break;
|
break;
|
||||||
case 1:
|
case 1:
|
||||||
ext = strrchr(ftptr = RSTRING_PTR(tmp), '.');
|
ext = strrchr(ftptr = RSTRING_PTR(tmp), '.');
|
||||||
if (rb_feature_p(ftptr, ext, !--type))
|
if (rb_feature_p(ftptr, ext, !--type))
|
||||||
break;
|
break;
|
||||||
|
@ -375,8 +412,8 @@ VALUE
|
||||||
rb_require_safe(VALUE fname, int safe)
|
rb_require_safe(VALUE fname, int safe)
|
||||||
{
|
{
|
||||||
VALUE result = Qnil;
|
VALUE result = Qnil;
|
||||||
volatile VALUE errinfo = GET_THREAD()->errinfo;
|
|
||||||
rb_thread_t *th = GET_THREAD();
|
rb_thread_t *th = GET_THREAD();
|
||||||
|
volatile VALUE errinfo = th->errinfo;
|
||||||
int state;
|
int state;
|
||||||
struct {
|
struct {
|
||||||
NODE *node;
|
NODE *node;
|
||||||
|
@ -397,25 +434,17 @@ rb_require_safe(VALUE fname, int safe)
|
||||||
*(volatile VALUE *)&fname = rb_str_new4(fname);
|
*(volatile VALUE *)&fname = rb_str_new4(fname);
|
||||||
found = search_required(fname, &path);
|
found = search_required(fname, &path);
|
||||||
if (found) {
|
if (found) {
|
||||||
if (!path || load_wait(RSTRING_PTR(path))) {
|
if (!path || !(ftptr = load_lock(RSTRING_PTR(path)))) {
|
||||||
result = Qfalse;
|
result = Qfalse;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
rb_set_safe_level_force(0);
|
rb_set_safe_level_force(0);
|
||||||
switch (found) {
|
switch (found) {
|
||||||
case 'r':
|
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);
|
|
||||||
rb_load(path, 0);
|
rb_load(path, 0);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 's':
|
case 's':
|
||||||
ruby_current_node = 0;
|
ruby_current_node = 0;
|
||||||
ruby_sourcefile = rb_source_filename(RSTRING_PTR(path));
|
ruby_sourcefile = rb_source_filename(RSTRING_PTR(path));
|
||||||
ruby_sourceline = 0;
|
ruby_sourceline = 0;
|
||||||
|
@ -430,12 +459,8 @@ rb_require_safe(VALUE fname, int safe)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
POP_TAG();
|
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;
|
ruby_current_node = saved.node;
|
||||||
rb_set_safe_level_force(saved.safe);
|
rb_set_safe_level_force(saved.safe);
|
||||||
if (state) {
|
if (state) {
|
||||||
|
@ -459,6 +484,23 @@ rb_require(const char *fname)
|
||||||
return rb_require_safe(fn, rb_safe_level());
|
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:
|
* call-seq:
|
||||||
* mod.autoload(name, filename) => nil
|
* mod.autoload(name, filename) => nil
|
||||||
|
|
12
ext/extmk.rb
12
ext/extmk.rb
|
@ -452,14 +452,14 @@ unless $extlist.empty?
|
||||||
src = %{\
|
src = %{\
|
||||||
#include "ruby.h"
|
#include "ruby.h"
|
||||||
|
|
||||||
#define init(func, name) { \
|
#define init(func, name) { \\
|
||||||
void func _((void)); \
|
extern void func _((void)); \\
|
||||||
ruby_sourcefile = src = rb_source_filename(name); \
|
ruby_init_ext(name, func); \\
|
||||||
func(); \
|
|
||||||
rb_provide(src); \
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
if !modified?(extinit.c, MTIMES) || IO.read(extinit.c) != src
|
||||||
open(extinit.c, "w") {|fe| fe.print src}
|
open(extinit.c, "w") {|fe| fe.print src}
|
||||||
|
|
10
intern.h
10
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,
|
VALUE rb_thread_blocking_region(rb_blocking_function_t *func, void *data,
|
||||||
rb_unblock_function_t *ubf);
|
rb_unblock_function_t *ubf);
|
||||||
#define RB_UBF_DFL ((rb_unblock_function_t *)-1)
|
#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 */
|
/* time.c */
|
||||||
VALUE rb_time_new(time_t, time_t);
|
VALUE rb_time_new(time_t, time_t);
|
||||||
/* variable.c */
|
/* variable.c */
|
||||||
|
|
213
thread.c
213
thread.c
|
@ -53,6 +53,9 @@
|
||||||
#define THREAD_DEBUG 0
|
#define THREAD_DEBUG 0
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
VALUE rb_cMutex;
|
||||||
|
VALUE rb_cBarrier;
|
||||||
|
|
||||||
static void sleep_timeval(rb_thread_t *th, struct timeval time);
|
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_wait_for_interrupt(rb_thread_t *th, double sleepsec);
|
||||||
static void sleep_forever(rb_thread_t *th);
|
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.
|
* Returns +true+ if this lock is currently held by some thread.
|
||||||
*/
|
*/
|
||||||
static VALUE
|
VALUE
|
||||||
mutex_locked_p(VALUE self)
|
rb_mutex_locked_p(VALUE self)
|
||||||
{
|
{
|
||||||
mutex_t *mutex;
|
mutex_t *mutex;
|
||||||
GetMutexVal(self, 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
|
* Attempts to obtain the lock and returns immediately. Returns +true+ if the
|
||||||
* lock was granted.
|
* lock was granted.
|
||||||
*/
|
*/
|
||||||
static VALUE
|
VALUE
|
||||||
mutex_try_lock(VALUE self)
|
rb_mutex_try_lock(VALUE self)
|
||||||
{
|
{
|
||||||
mutex_t *mutex;
|
mutex_t *mutex;
|
||||||
GetMutexVal(self, 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.
|
* Attempts to grab the lock and waits if it isn't available.
|
||||||
* Raises +ThreadError+ if +mutex+ was locked by the current thread.
|
* Raises +ThreadError+ if +mutex+ was locked by the current thread.
|
||||||
*/
|
*/
|
||||||
static VALUE
|
VALUE
|
||||||
mutex_lock(VALUE self)
|
rb_mutex_lock(VALUE self)
|
||||||
{
|
{
|
||||||
mutex_t *mutex;
|
mutex_t *mutex;
|
||||||
GetMutexVal(self, mutex);
|
GetMutexVal(self, mutex);
|
||||||
|
@ -2242,8 +2245,8 @@ mutex_lock(VALUE self)
|
||||||
* Releases the lock.
|
* Releases the lock.
|
||||||
* Raises +ThreadError+ if +mutex+ wasn't locked by the current thread.
|
* Raises +ThreadError+ if +mutex+ wasn't locked by the current thread.
|
||||||
*/
|
*/
|
||||||
static VALUE
|
VALUE
|
||||||
mutex_unlock(VALUE self)
|
rb_mutex_unlock(VALUE self)
|
||||||
{
|
{
|
||||||
mutex_t *mutex;
|
mutex_t *mutex;
|
||||||
GetMutexVal(self, mutex);
|
GetMutexVal(self, mutex);
|
||||||
|
@ -2257,6 +2260,28 @@ mutex_unlock(VALUE self)
|
||||||
return 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:
|
* call-seq:
|
||||||
* mutex.sleep(timeout = nil) => self
|
* mutex.sleep(timeout = nil) => self
|
||||||
|
@ -2268,22 +2293,153 @@ mutex_unlock(VALUE self)
|
||||||
static VALUE
|
static VALUE
|
||||||
mutex_sleep(int argc, VALUE *argv, VALUE self)
|
mutex_sleep(int argc, VALUE *argv, VALUE self)
|
||||||
{
|
{
|
||||||
int beg, end;
|
VALUE timeout;
|
||||||
mutex_unlock(self);
|
|
||||||
|
|
||||||
beg = time(0);
|
rb_scan_args(argc, argv, "01", &timeout);
|
||||||
if (argc == 0) {
|
return rb_mutex_sleep(self, timeout);
|
||||||
rb_thread_sleep_forever();
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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 {
|
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);
|
return self;
|
||||||
end = time(0) - beg;
|
}
|
||||||
return INT2FIX(end);
|
|
||||||
|
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)
|
Init_Thread(void)
|
||||||
{
|
{
|
||||||
VALUE cThGroup;
|
VALUE cThGroup;
|
||||||
VALUE cMutex;
|
|
||||||
|
|
||||||
rb_define_singleton_method(rb_cThread, "new", thread_s_new, -2);
|
rb_define_singleton_method(rb_cThread, "new", thread_s_new, -2);
|
||||||
rb_define_singleton_method(rb_cThread, "start", 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);
|
rb_define_const(cThGroup, "Default", th->thgroup);
|
||||||
}
|
}
|
||||||
|
|
||||||
cMutex = rb_define_class("Mutex", rb_cObject);
|
rb_cMutex = rb_define_class("Mutex", rb_cObject);
|
||||||
rb_define_alloc_func(cMutex, mutex_alloc);
|
rb_define_alloc_func(rb_cMutex, mutex_alloc);
|
||||||
rb_define_method(cMutex, "initialize", mutex_initialize, 0);
|
rb_define_method(rb_cMutex, "initialize", mutex_initialize, 0);
|
||||||
rb_define_method(cMutex, "locked?", mutex_locked_p, 0);
|
rb_define_method(rb_cMutex, "locked?", rb_mutex_locked_p, 0);
|
||||||
rb_define_method(cMutex, "try_lock", mutex_try_lock, 0);
|
rb_define_method(rb_cMutex, "try_lock", rb_mutex_try_lock, 0);
|
||||||
rb_define_method(cMutex, "lock", mutex_lock, 0);
|
rb_define_method(rb_cMutex, "lock", rb_mutex_lock, 0);
|
||||||
rb_define_method(cMutex, "unlock", mutex_unlock, 0);
|
rb_define_method(rb_cMutex, "unlock", rb_mutex_unlock, 0);
|
||||||
rb_define_method(cMutex, "sleep", mutex_sleep, -1);
|
rb_define_method(rb_cMutex, "sleep", mutex_sleep, -1);
|
||||||
yarvcore_eval(Qnil, rb_str_new2(
|
yarvcore_eval(Qnil, rb_str_new2(
|
||||||
"class Mutex;"
|
"class Mutex;"
|
||||||
" def synchronize; self.lock; yield; ensure; self.unlock; end;"
|
" 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__");
|
recursive_key = rb_intern("__recursive_key__");
|
||||||
rb_eThreadError = rb_define_class("ThreadError", rb_eStandardError);
|
rb_eThreadError = rb_define_class("ThreadError", rb_eStandardError);
|
||||||
|
|
|
@ -190,6 +190,7 @@ vm_mark(void *ptr)
|
||||||
MARK_UNLESS_NULL(vm->mark_object_ary);
|
MARK_UNLESS_NULL(vm->mark_object_ary);
|
||||||
MARK_UNLESS_NULL(vm->last_status);
|
MARK_UNLESS_NULL(vm->last_status);
|
||||||
MARK_UNLESS_NULL(vm->loaded_features);
|
MARK_UNLESS_NULL(vm->loaded_features);
|
||||||
|
if (vm->loading_table) rb_mark_tbl(vm->loading_table);
|
||||||
|
|
||||||
mark_event_hooks(vm->event_hooks);
|
mark_event_hooks(vm->event_hooks);
|
||||||
}
|
}
|
||||||
|
|
|
@ -353,6 +353,7 @@ typedef struct rb_vm_struct {
|
||||||
|
|
||||||
/* load */
|
/* load */
|
||||||
VALUE loaded_features;
|
VALUE loaded_features;
|
||||||
|
struct st_table *loading_table;
|
||||||
|
|
||||||
/* signal */
|
/* signal */
|
||||||
rb_atomic_t signal_buff[RUBY_NSIG];
|
rb_atomic_t signal_buff[RUBY_NSIG];
|
||||||
|
|
Loading…
Add table
Reference in a new issue