ruby-changes:6505
From: nobu <ko1@a...>
Date: Fri, 11 Jul 2008 06:48:40 +0900 (JST)
Subject: [ruby-changes:6505] Ruby:r18021 (mvm): * vm_core.h (rb_thread_t): per-thread cwd support. [EXPERIMENTAL]
nobu 2008-07-11 06:45:17 +0900 (Fri, 11 Jul 2008) New Revision: 18021 http://svn.ruby-lang.org/cgi-bin/viewvc.cgi?view=rev&revision=18021 Log: * vm_core.h (rb_thread_t): per-thread cwd support. [EXPERIMENTAL] * io.c (rb_sysopen), dir.c (dir_s_chdir), file.c (ruby_readlink, ruby_absolute_path_p), thread.c (thread_create_core, ruby_thread_getcwd, ruby_getcwd), vm.c (Init_VM): ditto. * string.c (rb_str_wrap): wrap a heap pointer with string. * thread.c (ruby_system_alone): returns if only current VM is running. Modified files: branches/mvm/ChangeLog branches/mvm/common.mk branches/mvm/dir.c branches/mvm/file.c branches/mvm/include/ruby/intern.h branches/mvm/include/ruby/mvm.h branches/mvm/io.c branches/mvm/string.c branches/mvm/thread.c branches/mvm/util.c branches/mvm/vm.c branches/mvm/vm_core.h Index: mvm/include/ruby/mvm.h =================================================================== --- mvm/include/ruby/mvm.h (revision 18020) +++ mvm/include/ruby/mvm.h (revision 18021) @@ -39,4 +39,6 @@ VALUE *ruby_vm_specific_ptr(rb_vm_t *, int); VALUE *rb_vm_specific_ptr(int); +char *ruby_thread_getcwd(rb_thread_t *); + #endif /* RUBY_MVM_H */ Index: mvm/include/ruby/intern.h =================================================================== --- mvm/include/ruby/intern.h (revision 18020) +++ mvm/include/ruby/intern.h (revision 18021) @@ -292,6 +292,7 @@ int rb_thread_fd_writable(int); void rb_thread_fd_close(int); int rb_thread_alone(void); +int ruby_system_alone(void); void rb_thread_polling(void); void rb_thread_sleep(int); void rb_thread_sleep_forever(void); @@ -523,6 +524,7 @@ VALUE rb_vsprintf(const char*, va_list); VALUE rb_str_format(int, const VALUE *, VALUE); /* string.c */ +VALUE rb_str_wrap(char*, long); VALUE rb_str_new(const char*, long); VALUE rb_str_new2(const char*); VALUE rb_str_new3(VALUE); @@ -625,6 +627,7 @@ VALUE rb_barrier_new(void); VALUE rb_barrier_wait(VALUE self); VALUE rb_barrier_release(VALUE self); +char *rb_thread_cwd(void); /* time.c */ VALUE rb_time_new(time_t, long); VALUE rb_time_nano_new(time_t, long); Index: mvm/ChangeLog =================================================================== --- mvm/ChangeLog (revision 18020) +++ mvm/ChangeLog (revision 18021) @@ -1,3 +1,16 @@ +Fri Jul 11 06:45:14 2008 Nobuyoshi Nakada <nobu@r...> + + * vm_core.h (rb_thread_t): per-thread cwd support. [EXPERIMENTAL] + + * io.c (rb_sysopen), dir.c (dir_s_chdir), + file.c (ruby_readlink, ruby_absolute_path_p), + thread.c (thread_create_core, ruby_thread_getcwd, ruby_getcwd), + vm.c (Init_VM): ditto. + + * string.c (rb_str_wrap): wrap a heap pointer with string. + + * thread.c (ruby_system_alone): returns if only current VM is running. + Fri Jul 11 06:44:06 2008 Nobuyoshi Nakada <nobu@r...> * dir.c (fcntl.h): needed for AT_FDCWD. Index: mvm/vm_core.h =================================================================== --- mvm/vm_core.h (revision 18020) +++ mvm/vm_core.h (revision 18021) @@ -405,6 +405,14 @@ #define RUBY_VM_VALUE_CACHE_SIZE 0x1000 #define USE_VALUE_CACHE 0 +#if defined HAVE_DIRFD && defined HAVE_OPENAT +#define USE_OPENAT 1 +#else +#define USE_OPENAT 0 +#endif +#if USE_OPENAT +#include <fcntl.h> +#endif struct rb_unblock_callback { rb_unblock_function_t *func; @@ -474,6 +482,13 @@ VALUE *value_cache_ptr; #endif + struct { +#if USE_OPENAT + int fd; +#endif + char *path; + } cwd; + struct rb_thread_struct *join_list_next; struct rb_thread_struct *join_list_head; Index: mvm/string.c =================================================================== --- mvm/string.c (revision 18020) +++ mvm/string.c (revision 18021) @@ -374,6 +374,27 @@ } VALUE +rb_str_wrap(char *ptr, long len) +{ + VALUE str; + + if (!ptr) { + rb_raise(rb_eArgError, "NULL pointer given"); + } + if (len < 0) { + rb_raise(rb_eArgError, "negative string size (or size too big)"); + } + + str = str_alloc(rb_cString); + RSTRING(str)->as.heap.aux.capa = len; + RSTRING(str)->as.heap.ptr = ptr; + STR_SET_NOEMBED(str); + STR_SET_LEN(str, len); + RSTRING_PTR(str)[len] = '\0'; + return str; +} + +VALUE rb_str_new(const char *ptr, long len) { return str_new(rb_cString, ptr, len); Index: mvm/io.c =================================================================== --- mvm/io.c (revision 18020) +++ mvm/io.c (revision 18021) @@ -3395,7 +3395,12 @@ } struct sysopen_struct { - char *fname; +#if USE_OPENAT + int base; +#else + VALUE fullpath; +#endif + const char *fname; int flag; unsigned int mode; }; @@ -3404,29 +3409,50 @@ sysopen_func(void *ptr) { struct sysopen_struct *data = ptr; +#if USE_OPENAT + return (VALUE)openat(data->base, data->fname, data->flag, data->mode); +#else return (VALUE)open(data->fname, data->flag, data->mode); +#endif } +static void +rb_sysopen_prepare(struct sysopen_struct *data, const char *fname, int flags, unsigned int mode) +{ +#if USE_OPENAT + data->base = GET_THREAD()->cwd.fd; +#else + if (ruby_absolute_path_p(fname)) { + data->base = Qnil; + } + else { + data->base = rb_file_expand_path(rb_str_new2(fname), Qnil); + fname = RSTRING_PTR(data->base); + } +#endif + data->fname = fname; + data->flag = flags; + data->mode = mode; +} + static int -rb_sysopen_internal(char *fname, int flags, unsigned int mode) +rb_sysopen_internal(struct sysopen_struct *data) { - struct sysopen_struct data; - data.fname = fname; - data.flag = flags; - data.mode = mode; - return (int)rb_thread_blocking_region(sysopen_func, &data, RB_UBF_DFL, 0); + return (int)rb_thread_blocking_region(sysopen_func, data, RB_UBF_DFL, 0); } static int rb_sysopen(char *fname, int flags, unsigned int mode) { int fd; + struct sysopen_struct data; - fd = rb_sysopen_internal(fname, flags, mode); + rb_sysopen_prepare(&data, fname, flags, mode); + fd = rb_sysopen_internal(&data); if (fd < 0) { if (errno == EMFILE || errno == ENFILE) { rb_gc(); - fd = rb_sysopen_internal(fname, flags, mode); + fd = rb_sysopen_internal(&data); } if (fd < 0) { rb_sys_fail(fname); Index: mvm/thread.c =================================================================== --- mvm/thread.c (revision 18020) +++ mvm/thread.c (revision 18021) @@ -310,6 +310,12 @@ --system_working; } +int +ruby_system_alone(void) +{ + return system_working <= 1; +} + static void thread_cleanup_func_before_exec(void *th_ptr) { @@ -455,6 +461,10 @@ native_mutex_initialize(&th->interrupt_lock); /* kick thread */ st_insert(th->vm->living_threads, thval, (st_data_t) th->thread_id); +#if USE_OPENAT + th->cwd.fd = openat(GET_THREAD()->cwd.fd, ".", O_RDONLY); +#endif + th->cwd.path = strdup(GET_THREAD()->cwd.path); native_thread_create(th); return thval; } @@ -3420,6 +3430,28 @@ return result; } +char * +ruby_thread_getcwd(rb_thread_t *th) +{ +#if USE_OPENAT && defined HAVE_READLINK + extern char *ruby_readlink(const char *, long *); + static const char fdpat[] = "/proc/self/fd/%d"; + char fdname[sizeof(fdpat) + sizeof(int) * 5 / 2], *cwd; + long len; + + snprintf(fdname, sizeof(fdname), fdpat, th->cwd.fd); + cwd = ruby_readlink(fdname, &len); + if (cwd) return cwd; +#endif + return ruby_strdup(th->cwd.path); +} + +char * +ruby_getcwd(void) +{ + return ruby_thread_getcwd(GET_THREAD()); +} + /* * +Thread+ encapsulates the behavior of a thread of * execution, including the main thread of the Ruby script. Index: mvm/common.mk =================================================================== --- mvm/common.mk (revision 18020) +++ mvm/common.mk (revision 18021) @@ -446,7 +446,7 @@ complex.$(OBJEXT): {$(VPATH)}complex.c $(RUBY_H_INCLUDES) \ {$(VPATH)}st.h dir.$(OBJEXT): {$(VPATH)}dir.c $(RUBY_H_INCLUDES) \ - {$(VPATH)}st.h {$(VPATH)}util.h + {$(VPATH)}st.h {$(VPATH)}util.h $(VM_CORE_H_INCLUDES) dln.$(OBJEXT): {$(VPATH)}dln.c $(RUBY_H_INCLUDES) \ {$(VPATH)}st.h {$(VPATH)}dln.h dmydln.$(OBJEXT): {$(VPATH)}dmydln.c dln.$(OBJEXT) Index: mvm/dir.c =================================================================== --- mvm/dir.c (revision 18020) +++ mvm/dir.c (revision 18021) @@ -13,6 +13,7 @@ #include "ruby/ruby.h" #include "ruby/encoding.h" +#include "vm_core.h" #include <sys/types.h> #include <sys/stat.h> @@ -701,18 +702,46 @@ static void dir_chdir(VALUE path) { - if (chdir(RSTRING_PTR(path)) < 0) + rb_thread_t *th = GET_THREAD(); +#if USE_OPENAT + int dir = openat(th->cwd.fd, RSTRING_PTR(path), O_RDONLY); + if (dir < 0) rb_sys_fail(RSTRING_PTR(path)); + if (ruby_system_alone() && rb_thread_alone()) { + if (fchdir(dir) < 0) { + rb_sys_fail(RSTRING_PTR(path)); + } + } + close(th->cwd.fd); + th->cwd.fd = dir; +#else + VALUE fullpath; + struct stat st; + const char *dir; + char *olddir = th->cwd.path; + + RB_GC_GUARD(fullpath) = rb_file_expand_path(path, Qnil); + dir = RSTRING_PTR(fullpath); + if (eaccess(dir, X_OK) != 0 || stat(dir, &st) != 0 || + (!S_ISDIR(st.st_mode) && (errno = ENOTDIR))) { + rb_sys_fail(RSTRING_PTR(path)); + } + if (ruby_system_alone() && rb_thread_alone()) { + if (chdir(dir) < 0) { + rb_sys_fail(RSTRING_PTR(path)); + } + } + th->cwd.path = ruby_strdup(dir); + xfree(olddir); +#endif } -static int chdir_blocking = 0; -static VALUE chdir_thread = Qnil; - struct chdir_data { - VALUE old_path, new_path; -#if defined HAVE_DIRFD && defined HAVE_FCHDIR - DIR *old_dir; + VALUE new_path; +#if USE_OPENAT + int old_dir; #endif + VALUE old_path; int done; }; @@ -721,9 +750,6 @@ { dir_chdir(args->new_path); args->done = Qtrue; - chdir_blocking++; - if (chdir_thread == Qnil) - chdir_thread = rb_thread_current(); return rb_yield(args->new_path); } @@ -731,15 +757,12 @@ chdir_restore(struct chdir_data *args) { if (args->done) { - chdir_blocking--; - if (chdir_blocking == 0) - chdir_thread = Qnil; #if defined HAVE_DIRFD && defined HAVE_FCHDIR - if (fchdir(dirfd(args->old_dir)) < 0) { - closedir(args->old_dir); + if (fchdir(args->old_dir) < 0) { + close(args->old_dir); rb_sys_fail(RSTRING_PTR(args->old_path)); } - closedir(args->old_dir); + close(args->old_dir); #else dir_chdir(args->old_path); #endif @@ -747,26 +770,15 @@ return Qnil; } -static void -dir_chdir_check(void) -{ - if (chdir_blocking > 0) { - if (!rb_block_given_p() || rb_thread_current() != chdir_thread) - rb_warn("conflicting chdir during another chdir block"); - } -} - static VALUE dir_chdir_block(VALUE path) { struct chdir_data args; - char *cwd = my_getcwd(); - - args.old_path = rb_tainted_str_new2(cwd); xfree(cwd); +#if USE_OPENAT + args.old_dir = openat(GET_THREAD()->cwd.fd, ".", O_RDONLY); +#endif + args.old_path = rb_tainted_str_new2(GET_THREAD()->cwd.path); args.new_path = path; -#if defined HAVE_DIRFD && defined HAVE_FCHDIR - args.old_dir = opendir("."); -#endif args.done = Qfalse; return rb_ensure(chdir_yield, (VALUE)&args, chdir_restore, (VALUE)&args); } @@ -828,7 +840,6 @@ path = rb_str_new2(dist); } - dir_chdir_check(); if (rb_block_given_p()) { return dir_chdir_block(path); } @@ -1935,11 +1946,6 @@ } VALUE rb_openat(int argc, VALUE *argv, int base, const char *path); -#if defined HAVE_DIRFD && defined HAVE_OPENAT && defined AT_FDCWD -#define USE_OPENAT 1 -#else -#define USE_OPENAT 0 -#endif /* * call-seq: Index: mvm/util.c =================================================================== --- mvm/util.c (revision 18020) +++ mvm/util.c (revision 18021) @@ -739,35 +739,6 @@ return tmp; } -char * -ruby_getcwd(void) -{ -#ifdef HAVE_GETCWD - int size = 200; - char *buf = xmalloc(size); - - while (!getcwd(buf, size)) { - if (errno != ERANGE) { - xfree(buf); - rb_sys_fail("getcwd"); - } - size *= 2; - buf = xrealloc(buf, size); - } -#else -# ifndef PATH_MAX -# define PATH_MAX 8192 -# endif - char *buf = xmalloc(PATH_MAX+1); - - if (!getwd(buf)) { - xfree(buf); - rb_sys_fail("getwd"); - } -#endif - return buf; -} - /**************************************************************** * * The author of this software is David M. Gay. Index: mvm/vm.c =================================================================== --- mvm/vm.c (revision 18020) +++ mvm/vm.c (revision 18021) @@ -1829,6 +1829,10 @@ th->cfp->iseq = iseq; th->cfp->pc = iseq->iseq_encoded; vm_init_redefined_flag(vm); +#if USE_OPENAT + th->cwd.fd = openat(AT_FDCWD, ".", O_RDONLY); +#endif + th->cwd.path = ruby_getcwd(); } } Index: mvm/file.c =================================================================== --- mvm/file.c (revision 18020) +++ mvm/file.c (revision 18021) @@ -2256,6 +2256,41 @@ #endif } +#ifdef HAVE_READLINK +char * +ruby_readlink(const char *path, long *len) +{ + char *buf, *tmp; + int size = 100; + int rv; + + buf = malloc(size); + if (!buf) return 0; + while ((rv = readlink(path, buf, size)) == size +#ifdef _AIX + || (rv < 0 && errno == ERANGE) /* quirky behavior of GPFS */ +#endif + ) { + size *= 2; + tmp = realloc(buf, size); + if (!tmp) { + free(buf); + return 0; + } + } + if (rv < 0) { + free(buf); + return 0; + } + buf[rv] = '\0'; + if (size > rv + 1) { + buf = realloc(buf, rv + 1); + } + *len = rv; + return buf; +} +#endif + /* * call-seq: * File.readlink(link_name) -> file_name @@ -2272,29 +2307,15 @@ { #ifdef HAVE_READLINK char *buf; - int size = 100; - int rv; - VALUE v; + long rv; rb_secure(2); FilePathValue(path); - buf = xmalloc(size); - while ((rv = readlink(RSTRING_PTR(path), buf, size)) == size -#ifdef _AIX - || (rv < 0 && errno == ERANGE) /* quirky behavior of GPFS */ -#endif - ) { - size *= 2; - buf = xrealloc(buf, size); - } - if (rv < 0) { - xfree(buf); + buf = ruby_readlink(RSTRING_PTR(path), &rv); + if (!buf) { rb_sys_fail(RSTRING_PTR(path)); } - v = rb_tainted_str_new(buf, rv); - xfree(buf); - - return v; + return rb_str_wrap(buf, rv); #else rb_notimplement(); return Qnil; /* not reached */ @@ -2608,7 +2629,8 @@ (void)(extenc || (extenc = rb_default_external_encoding())),\ rb_enc_associate(result, extenc)) -static int is_absolute_path(const char*); +#define is_absolute_path(path) ruby_absolute_path_p(path) +int ruby_absolute_path_p(const char*); static VALUE file_expand_path(VALUE fname, VALUE dname, VALUE result) @@ -4347,8 +4369,8 @@ rb_define_const(rb_mFConst, name, value); } -static int -is_absolute_path(const char *path) +int +ruby_absolute_path_p(const char *path) { #ifdef DOSISH_DRIVE_LETTER if (has_drive_letter(path) && isdirsep(path[2])) return 1; -- ML: ruby-changes@q... Info: http://www.atdot.net/~ko1/quickml/