From 46620a87720943120cabe6ffe2e94a2c3506c4c2 Mon Sep 17 00:00:00 2001 From: matz Date: Tue, 14 Nov 2000 07:10:31 +0000 Subject: matz git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@1039 b2dd03c8-39d4-4d8f-98ff-823fe69b080e --- ChangeLog | 4 + Makefile.in | 2 +- error.c | 4 +- gc.c | 47 ++--- numeric.c | 2 +- object.c | 5 +- random.c | 1 + rubysig.h | 38 +++- signal.c | 25 ++- version.h | 4 +- win32/win32.c | 552 +++++++++++++++++++++++++++++++++++++++++++--------------- win32/win32.h | 26 +-- 12 files changed, 508 insertions(+), 202 deletions(-) diff --git a/ChangeLog b/ChangeLog index 9b7ab98aa4..d97eef3dc1 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,7 @@ +Mon Nov 13 22:44:52 2000 Yukihiro Matsumoto + + * error.c (rb_bug): print version to stderr. + Mon Nov 13 19:02:08 2000 WATANABE Hirofumi * win32/win32.c, io.c, process.c: the exit status of program must be diff --git a/Makefile.in b/Makefile.in index 60cbd40eb1..94ebfb9a8c 100644 --- a/Makefile.in +++ b/Makefile.in @@ -245,7 +245,7 @@ dir.@OBJEXT@: dir.c ruby.h config.h defines.h intern.h dln.@OBJEXT@: dln.c config.h defines.h dln.h dmyext.@OBJEXT@: dmyext.c enum.@OBJEXT@: enum.c ruby.h config.h defines.h intern.h -error.@OBJEXT@: error.c ruby.h config.h defines.h intern.h env.h +error.@OBJEXT@: error.c ruby.h config.h defines.h intern.h env.h version.h eval.@OBJEXT@: eval.c ruby.h config.h defines.h intern.h node.h env.h rubysig.h st.h dln.h file.@OBJEXT@: file.c ruby.h config.h defines.h intern.h rubyio.h rubysig.h gc.@OBJEXT@: gc.c ruby.h config.h defines.h intern.h rubysig.h st.h node.h env.h re.h regex.h diff --git a/error.c b/error.c index 34eae0d6b0..4527128030 100644 --- a/error.c +++ b/error.c @@ -12,6 +12,8 @@ #include "ruby.h" #include "env.h" +#include "version.h" + #include #ifdef HAVE_STDARG_PROTOTYPES #include @@ -172,7 +174,7 @@ rb_bug(fmt, va_alist) va_init_list(args, fmt); warn_print(buf, args); va_end(args); - ruby_show_version(); + fprintf(stderr, "ruby %s (%s) [%s]\n", RUBY_VERSION, RUBY_RELEASE_DATE, RUBY_PLATFORM); abort(); } diff --git a/gc.c b/gc.c index 1444a960a2..ac05c11c22 100644 --- a/gc.c +++ b/gc.c @@ -74,10 +74,10 @@ ruby_xmalloc(size) if (malloc_memories > GC_MALLOC_LIMIT) { rb_gc(); } - mem = malloc(size); + RUBY_CRITICAL(mem = malloc(size)); if (!mem) { rb_gc(); - mem = malloc(size); + RUBY_CRITICAL(mem = malloc(size)); if (!mem) { if (size >= 10 * 1024 * 1024) { rb_raise(rb_eNoMemError, "tried to allocate too big memory"); @@ -114,10 +114,10 @@ ruby_xrealloc(ptr, size) if (!ptr) return xmalloc(size); if (size == 0) size = 1; malloc_memories += size; - mem = realloc(ptr, size); + RUBY_CRITICAL(mem = realloc(ptr, size)); if (!mem) { rb_gc(); - mem = realloc(ptr, size); + RUBY_CRITICAL(mem = realloc(ptr, size)); if (!mem) if (size >= 50 * 1024 * 1024) { rb_raise(rb_eNoMemError, "tried to re-allocate too big memory"); @@ -132,7 +132,8 @@ void ruby_xfree(x) void *x; { - if (x) free(x); + if (x) + RUBY_CRITICAL(free(x)); } extern int ruby_in_compile; @@ -186,7 +187,7 @@ rb_gc_unregister_address(addr) if (tmp->varptr == addr) { Global_List = tmp->next; - free(tmp); + RUBY_CRITICAL(free(tmp)); return; } while (tmp->next) { @@ -194,7 +195,7 @@ rb_gc_unregister_address(addr) struct gc_list *t = tmp->next; tmp->next = tmp->next->next; - free(t); + RUBY_CRITICAL(free(t)); break; } tmp = tmp->next; @@ -253,13 +254,13 @@ add_heap() if (heaps_used == heaps_length) { /* Realloc heaps */ heaps_length += HEAPS_INCREMENT; - heaps = (heaps_used>0)? - (RVALUE**)realloc(heaps, heaps_length*sizeof(RVALUE*)): - (RVALUE**)malloc(heaps_length*sizeof(RVALUE*)); + RUBY_CRITICAL(heaps = (heaps_used>0)? + (RVALUE**)realloc(heaps, heaps_length*sizeof(RVALUE*)): + (RVALUE**)malloc(heaps_length*sizeof(RVALUE*))); if (heaps == 0) mem_error("heaps: can't alloc memory"); } - p = heaps[heaps_used++] = (RVALUE*)malloc(sizeof(RVALUE)*HEAP_SLOTS); + RUBY_CRITICAL(p = heaps[heaps_used++] = (RVALUE*)malloc(sizeof(RVALUE)*HEAP_SLOTS)); if (p == 0) mem_error("add_heap: can't alloc memory"); pend = p + HEAP_SLOTS; if (lomem == 0 || lomem > p) lomem = p; @@ -754,12 +755,12 @@ obj_free(obj) case T_STRING: #define STR_NO_ORIG FL_USER2 /* copied from string.c */ if (!RANY(obj)->as.string.orig || FL_TEST(obj, STR_NO_ORIG)) { - free(RANY(obj)->as.string.ptr); + RUBY_CRITICAL(free(RANY(obj)->as.string.ptr)); } break; case T_ARRAY: if (RANY(obj)->as.array.ptr) { - free(RANY(obj)->as.array.ptr); + RUBY_CRITICAL(free(RANY(obj)->as.array.ptr)); } break; case T_HASH: @@ -772,13 +773,13 @@ obj_free(obj) re_free_pattern(RANY(obj)->as.regexp.ptr); } if (RANY(obj)->as.regexp.str) { - free(RANY(obj)->as.regexp.str); + RUBY_CRITICAL(free(RANY(obj)->as.regexp.str)); } break; case T_DATA: if (DATA_PTR(obj)) { if ((long)RANY(obj)->as.data.dfree == -1) { - free(DATA_PTR(obj)); + RUBY_CRITICAL(free(DATA_PTR(obj))); } else if (RANY(obj)->as.data.dfree) { (*RANY(obj)->as.data.dfree)(DATA_PTR(obj)); @@ -788,13 +789,13 @@ obj_free(obj) case T_MATCH: if (RANY(obj)->as.match.regs) { re_free_registers(RANY(obj)->as.match.regs); - free(RANY(obj)->as.match.regs); + RUBY_CRITICAL(free(RANY(obj)->as.match.regs)); } break; case T_FILE: if (RANY(obj)->as.file.fptr) { rb_io_fptr_finalize(RANY(obj)->as.file.fptr); - free(RANY(obj)->as.file.fptr); + RUBY_CRITICAL(free(RANY(obj)->as.file.fptr)); } break; case T_ICLASS: @@ -807,19 +808,19 @@ obj_free(obj) case T_BIGNUM: if (RANY(obj)->as.bignum.digits) { - free(RANY(obj)->as.bignum.digits); + RUBY_CRITICAL(free(RANY(obj)->as.bignum.digits)); } break; case T_NODE: switch (nd_type(obj)) { case NODE_SCOPE: if (RANY(obj)->as.node.u1.tbl) { - free(RANY(obj)->as.node.u1.tbl); + RUBY_CRITICAL(free(RANY(obj)->as.node.u1.tbl)); } break; #ifdef C_ALLOCA case NODE_ALLOCA: - free(RANY(obj)->as.node.u1.value); + RUBY_CRITICAL(free(RANY(obj)->as.node.u1.value)); break; #endif } @@ -830,15 +831,15 @@ obj_free(obj) RANY(obj)->as.scope.flag != SCOPE_ALLOCA) { VALUE *vars = RANY(obj)->as.scope.local_vars-1; if (vars[0] == 0) - free(RANY(obj)->as.scope.local_tbl); + RUBY_CRITICAL(free(RANY(obj)->as.scope.local_tbl)); if (RANY(obj)->as.scope.flag&SCOPE_MALLOC) - free(vars); + RUBY_CRITICAL(free(vars)); } break; case T_STRUCT: if (RANY(obj)->as.rstruct.ptr) { - free(RANY(obj)->as.rstruct.ptr); + RUBY_CRITICAL(free(RANY(obj)->as.rstruct.ptr)); } break; diff --git a/numeric.c b/numeric.c index cc53c3660a..0a172a3965 100644 --- a/numeric.c +++ b/numeric.c @@ -785,7 +785,7 @@ rb_num2long(val) return Qnil; /* not reached */ default: - val = rb_Integer(val); + val = rb_to_int(val); return NUM2LONG(val); } } diff --git a/object.c b/object.c index 9b5d50a457..c4283cb069 100644 --- a/object.c +++ b/object.c @@ -885,8 +885,8 @@ rb_to_integer(val, method) val = rb_rescue2(to_type, (VALUE)&arg1, fail_to_type, (VALUE)&arg2, rb_eStandardError, rb_eNameError, 0); if (!rb_obj_is_kind_of(val, rb_cInteger)) { - rb_raise(rb_eTypeError, "%s#%s_i should return Integer", - method, rb_class2name(CLASS_OF(arg1.val))); + rb_raise(rb_eTypeError, "%s#%s should return Integer", + rb_class2name(CLASS_OF(arg1.val)), method); } return val; } @@ -1166,6 +1166,7 @@ Init_Object() rb_undef_method(CLASS_OF(rb_cSymbol), "new"); rb_define_method(rb_cSymbol, "type", sym_type, 0); rb_define_method(rb_cSymbol, "to_i", sym_to_i, 0); + rb_define_method(rb_cSymbol, "to_int", sym_to_i, 0); rb_define_method(rb_cSymbol, "inspect", sym_inspect, 0); rb_define_method(rb_cSymbol, "to_s", sym_to_s, 0); rb_define_method(rb_cSymbol, "id2name", sym_to_s, 0); diff --git a/random.c b/random.c index 028820d861..5ac7868bf1 100644 --- a/random.c +++ b/random.c @@ -114,6 +114,7 @@ rb_f_srand(argc, argv, obj) VALUE a; unsigned int seed, old; + rb_secure(4); if (rb_scan_args(argc, argv, "01", &a) == 0) { static int n = 0; struct timeval tv; diff --git a/rubysig.h b/rubysig.h index a837c017b2..88b30830d9 100644 --- a/rubysig.h +++ b/rubysig.h @@ -13,14 +13,40 @@ #ifndef SIG_H #define SIG_H -EXTERN int rb_trap_immediate; #ifdef NT -#define TRAP_BEG (rb_trap_immediate=1, SetEvent(rb_InterruptEvent)) -#define TRAP_END (rb_trap_immediate=0, ResetEvent(rb_InterruptEvent)) +typedef LONG rb_atomic_t; + +# define ATOMIC_TEST(var) InterlockedExchange(&(var), 0) +# define ATOMIC_SET(var, val) InterlockedExchange(&(var), (val)) +# define ATOMIC_INC(var) InterlockedIncrement(&(var)) +# define ATOMIC_DEC(var) InterlockedDecrement(&(var)) + +/* Windows doesn't allow interrupt while system calls */ +# define TRAP_BEG win32_enter_syscall() +# define TRAP_END win32_leave_syscall() +# define RUBY_CRITICAL(statements) do {\ + win32_disable_interrupt();\ + statements;\ + win32_enable_interrupt();\ +} while (0) #else -#define TRAP_BEG (rb_trap_immediate=1) -#define TRAP_END (rb_trap_immediate=0) +typedef int rb_atomic_t; + +# define ATOMIC_TEST(var) ((var) ? ((var) = 0, 1) : 0) +# define ATOMIC_SET(var, val) ((var) = (val)) +# define ATOMIC_INC(var) (++(var)) +# define ATOMIC_DEC(var) (--(var)) + +# define TRAP_BEG (rb_trap_immediate=1) +# define TRAP_END (rb_trap_immediate=0) +# define RUBY_CRITICAL(statements) do {\ + int trap_immediate = rb_trap_immediate;\ + rb_trap_immediate = 0;\ + statements;\ + rb_trap_immediate = trap_immediate;\ +} while (0) #endif +EXTERN rb_atomic_t rb_trap_immediate; EXTERN int rb_prohibit_interrupt; #define DEFER_INTS {rb_prohibit_interrupt++;} @@ -29,7 +55,7 @@ EXTERN int rb_prohibit_interrupt; VALUE rb_with_disable_interrupt _((VALUE(*)(),VALUE)); -EXTERN int rb_trap_pending; +EXTERN rb_atomic_t rb_trap_pending; void rb_trap_restore_mask _((void)); EXTERN int rb_thread_critical; diff --git a/signal.c b/signal.c index b5ed58ec75..77233c1852 100644 --- a/signal.c +++ b/signal.c @@ -260,9 +260,9 @@ rb_f_kill(argc, argv) } static VALUE trap_list[NSIG]; -static int trap_pending_list[NSIG]; -int rb_trap_pending; -int rb_trap_immediate; +static rb_atomic_t trap_pending_list[NSIG]; +rb_atomic_t rb_trap_pending; +rb_atomic_t rb_trap_immediate; int rb_prohibit_interrupt; void @@ -303,6 +303,7 @@ posix_signal(signum, handler) #define ruby_signal(sig,handle) signal((sig),(handle)) #endif +static void signal_exec _((int sig)); static void signal_exec(sig) int sig; @@ -342,11 +343,9 @@ sighandle(sig) int sig; { #ifdef NT -#define end_interrupt() win32_thread_resume_main() - if (win32_main_context(sig, sighandle)) return; - +#define IN_MAIN_CONTEXT(f, a) (win32_main_context(a, f) ? (void)0 : f(a)) #else -#define end_interrupt() (void)0 +#define IN_MAIN_CONTEXT(f, a) f(a) #endif if (sig >= NSIG) { @@ -357,16 +356,14 @@ sighandle(sig) ruby_signal(sig, sighandle); #endif - if (rb_trap_immediate) { - rb_trap_immediate = 0; - signal_exec(sig); - rb_trap_immediate = 1; + if (ATOMIC_TEST(rb_trap_immediate)) { + IN_MAIN_CONTEXT(signal_exec, sig); + ATOMIC_SET(rb_trap_immediate, 1); } else { - rb_trap_pending++; - trap_pending_list[sig]++; + ATOMIC_INC(rb_trap_pending); + ATOMIC_INC(trap_pending_list[sig]); } - end_interrupt(); } #ifdef SIGBUS diff --git a/version.h b/version.h index 9f8914673b..d731091ec4 100644 --- a/version.h +++ b/version.h @@ -1,4 +1,4 @@ #define RUBY_VERSION "1.6.2" -#define RUBY_RELEASE_DATE "2000-11-13" +#define RUBY_RELEASE_DATE "2000-11-14" #define RUBY_VERSION_CODE 162 -#define RUBY_RELEASE_CODE 20001113 +#define RUBY_RELEASE_CODE 20001114 diff --git a/win32/win32.c b/win32/win32.c index 7a2dea8848..74c18aa3ed 100644 --- a/win32/win32.c +++ b/win32/win32.c @@ -34,6 +34,32 @@ #define bool int #endif +#if USE_INTERRUPT_WINSOCK + +# if defined(_MSC_VER) && _MSC_VER <= 1000 +/* VC++4.0 doesn't have this. */ +extern DWORD WSAWaitForMultipleEvents(DWORD nevent, const HANDLE *events, + BOOL waitall, DWORD timeout, + BOOL alertable); +# endif + +# define WaitForMultipleEvents WSAWaitForMultipleEvents +# define CreateSignal() (HANDLE)WSACreateEvent() +# define SetSignal(ev) WSASetEvent(ev) +# define ResetSignal(ev) WSAResetEvent(ev) +#else /* USE_INTERRUPT_WINSOCK */ +# define WaitForMultipleEvents WaitForMultipleObjectsEx +# define CreateSignal() CreateEvent(NULL, FALSE, FALSE, NULL); +# define SetSignal(ev) SetEvent(ev) +# define ResetSignal(ev) (void)0 +#endif /* USE_INTERRUPT_WINSOCK */ + +#ifdef WIN32_DEBUG +#define Debug(something) something +#else +#define Debug(something) /* nothing */ +#endif + #define TO_SOCKET(x) _get_osfhandle(x) bool NtSyncProcess = TRUE; @@ -46,6 +72,7 @@ static bool NtHasRedirection (char *); static int valid_filename(char *s); static void StartSockets (); static char *str_grow(struct RString *str, size_t new_size); +static DWORD wait_events(HANDLE event, DWORD timeout); char *NTLoginName; @@ -76,17 +103,23 @@ IsWinNT(void) { } /* main thread constants */ -HANDLE rb_CurrentProcessHandle; -HANDLE rb_MainThreadHandle; -DWORD rb_MainThreadId; -HANDLE rb_InterruptEvent; +static struct { + HANDLE handle; + DWORD id; +} main_thread; + +/* interrupt stuff */ +static HANDLE interrupted_event; HANDLE GetCurrentThreadHandle(void) { + static HANDLE current_process_handle = NULL; HANDLE h; - HANDLE proc = rb_CurrentProcessHandle; - if (!DuplicateHandle(proc, GetCurrentThread(), proc, &h, + if (!current_process_handle) + current_process_handle = GetCurrentProcess(); + if (!DuplicateHandle(current_process_handle, GetCurrentThread(), + current_process_handle, &h, 0, FALSE, DUPLICATE_SAME_ACCESS)) return NULL; return h; @@ -98,68 +131,78 @@ HANDLE GetCurrentThreadHandle(void) #define LK_ERR(f,i) ((f) ? (i = 0) : (errno = GetLastError())) #define LK_LEN 0xffff0000 -int -flock(int fd, int oper) +static VALUE +flock_winnt(VALUE self, int argc, VALUE* argv) { OVERLAPPED o; int i = -1; - HANDLE fh; + const HANDLE fh = (HANDLE)self; + const int oper = argc; - fh = (HANDLE)_get_osfhandle(fd); memset(&o, 0, sizeof(o)); - if(IsWinNT()) { - switch(oper) { - case LOCK_SH: /* shared lock */ - LK_ERR(LockFileEx(fh, 0, 0, LK_LEN, 0, &o),i); - break; - case LOCK_EX: /* exclusive lock */ - LK_ERR(LockFileEx(fh, LOCKFILE_EXCLUSIVE_LOCK, 0, LK_LEN, 0, &o),i); - break; - case LOCK_SH|LOCK_NB: /* non-blocking shared lock */ - LK_ERR(LockFileEx(fh, LOCKFILE_FAIL_IMMEDIATELY, 0, LK_LEN, 0, &o),i); - break; - case LOCK_EX|LOCK_NB: /* non-blocking exclusive lock */ - LK_ERR(LockFileEx(fh, - LOCKFILE_EXCLUSIVE_LOCK|LOCKFILE_FAIL_IMMEDIATELY, - 0, LK_LEN, 0, &o),i); - if(errno == EDOM) errno = EWOULDBLOCK; - break; - case LOCK_UN: /* unlock lock */ - if (UnlockFileEx(fh, 0, LK_LEN, 0, &o)) { - i = 0; - } - else { - /* GetLastError() must returns `ERROR_NOT_LOCKED' */ + switch(oper) { + case LOCK_SH: /* shared lock */ + LK_ERR(LockFileEx(fh, 0, 0, LK_LEN, 0, &o), i); + break; + case LOCK_EX: /* exclusive lock */ + LK_ERR(LockFileEx(fh, LOCKFILE_EXCLUSIVE_LOCK, 0, LK_LEN, 0, &o), i); + break; + case LOCK_SH|LOCK_NB: /* non-blocking shared lock */ + LK_ERR(LockFileEx(fh, LOCKFILE_FAIL_IMMEDIATELY, 0, LK_LEN, 0, &o), i); + break; + case LOCK_EX|LOCK_NB: /* non-blocking exclusive lock */ + LK_ERR(LockFileEx(fh, + LOCKFILE_EXCLUSIVE_LOCK|LOCKFILE_FAIL_IMMEDIATELY, + 0, LK_LEN, 0, &o), i); + if (errno == EDOM) + errno = EWOULDBLOCK; + break; + case LOCK_UN: /* unlock lock */ + if (UnlockFileEx(fh, 0, LK_LEN, 0, &o)) { + i = 0; + if (errno == EDOM) errno = EWOULDBLOCK; - } - if(errno == EDOM) errno = EWOULDBLOCK; - break; - default: /* unknown */ - errno = EINVAL; - break; - } + } + else { + /* GetLastError() must returns `ERROR_NOT_LOCKED' */ + errno = EWOULDBLOCK; + } + break; + default: /* unknown */ + errno = EINVAL; + break; } - else if(IsWin95()) { - switch(oper) { - case LOCK_EX: - while(i == -1) { - LK_ERR(LockFile(fh, 0, 0, LK_LEN, 0), i); - if(errno != EDOM && i == -1) break; - } - break; - case LOCK_EX | LOCK_NB: + return i; +} + +static VALUE +flock_win95(VALUE self, int argc, VALUE* argv) +{ + int i = -1; + const HANDLE fh = (HANDLE)self; + const int oper = argc; + + switch(oper) { + case LOCK_EX: + while(i == -1) { LK_ERR(LockFile(fh, 0, 0, LK_LEN, 0), i); - if(errno == EDOM) errno = EWOULDBLOCK; - break; - case LOCK_UN: - LK_ERR(UnlockFile(fh, 0, 0, LK_LEN, 0), i); - if(errno == EDOM) errno = EWOULDBLOCK; - break; - default: - errno = EINVAL; - break; - } + if (errno != EDOM && i == -1) break; + } + break; + case LOCK_EX | LOCK_NB: + LK_ERR(LockFile(fh, 0, 0, LK_LEN, 0), i); + if (errno == EDOM) + errno = EWOULDBLOCK; + break; + case LOCK_UN: + LK_ERR(UnlockFile(fh, 0, 0, LK_LEN, 0), i); + if (errno == EDOM) + errno = EWOULDBLOCK; + break; + default: + errno = EINVAL; + break; } return i; } @@ -167,6 +210,22 @@ flock(int fd, int oper) #undef LK_ERR #undef LK_LEN +int +flock(int fd, int oper) +{ + static asynchronous_func_t locker = NULL; + + if (!locker) { + if (IsWinNT()) + locker = flock_winnt; + else + locker = flock_win95; + } + + return win32_asynchronize(locker, + (VALUE)_get_osfhandle(fd), oper, NULL, + (DWORD)-1); +} //#undef const //FILE *fdopen(int, const char *); @@ -1908,8 +1967,10 @@ myselect (int nfds, fd_set *rd, fd_set *wr, fd_set *ex, long r; fd_set file_rd; fd_set file_wr; +#ifdef USE_INTERRUPT_WINSOCK + fd_set trap; +#endif /* USE_INTERRUPT_WINSOCK */ int file_nfds; - int trap_immediate = rb_trap_immediate; if (!NtSocketsInitialized++) { StartSockets(); @@ -1928,18 +1989,26 @@ myselect (int nfds, fd_set *rd, fd_set *wr, fd_set *ex, if (wr) *wr = file_wr; return file_nfds; } - if (trap_immediate) - TRAP_END; + +#if USE_INTERRUPT_WINSOCK + if (ex) + trap = *ex; + else + trap.fd_count = 0; + if (trap.fd_count < FD_SETSIZE) + trap.fd_array[trap.fd_count++] = (SOCKET)interrupted_event; + // else unable to catch interrupt. + ex = &trap; +#endif /* USE_INTERRUPT_WINSOCK */ + if ((r = select (nfds, rd, wr, ex, timeout)) == SOCKET_ERROR) { errno = WSAGetLastError(); switch (errno) { - case WSAEINTR: + case WSAEINTR: errno = EINTR; break; } } - if (trap_immediate) - TRAP_BEG; return r; } @@ -1957,31 +2026,33 @@ StartSockets () // version = MAKEWORD(1, 1); if (ret = WSAStartup(version, &retdata)) - rb_fatal ("Unable to locate winsock library!\n"); + rb_fatal ("Unable to locate winsock library!\n"); if (LOBYTE(retdata.wVersion) != 1) - rb_fatal("could not find version 1 of winsock dll\n"); + rb_fatal("could not find version 1 of winsock dll\n"); if (HIBYTE(retdata.wVersion) != 1) - rb_fatal("could not find version 1 of winsock dll\n"); + rb_fatal("could not find version 1 of winsock dll\n"); atexit((void (*)(void)) WSACleanup); - iSockOpt = SO_SYNCHRONOUS_NONALERT; + iSockOpt = SO_SYNCHRONOUS_NONALERT; /* * Enable the use of sockets as filehandles */ setsockopt(INVALID_SOCKET, SOL_SOCKET, SO_OPENTYPE, - (char *)&iSockOpt, sizeof(iSockOpt)); + (char *)&iSockOpt, sizeof(iSockOpt)); - rb_CurrentProcessHandle = GetCurrentProcess(); - rb_MainThreadHandle = GetCurrentThreadHandle(); - rb_MainThreadId = GetCurrentThreadId(); - rb_InterruptEvent = CreateEvent(NULL, FALSE, TRUE, NULL); + main_thread.handle = GetCurrentThreadHandle(); + main_thread.id = GetCurrentThreadId(); + + interrupted_event = CreateSignal(); + if (!interrupted_event) + rb_fatal("Unable to create interrupt event!\n"); } #undef accept -SOCKET +SOCKET myaccept (SOCKET s, struct sockaddr *addr, int *addrlen) { SOCKET r; @@ -1990,12 +2061,8 @@ myaccept (SOCKET s, struct sockaddr *addr, int *addrlen) if (!NtSocketsInitialized++) { StartSockets(); } - if (trap_immediate) - TRAP_END; if ((r = accept (TO_SOCKET(s), addr, addrlen)) == INVALID_SOCKET) errno = WSAGetLastError(); - if (trap_immediate) - TRAP_BEG; return r; } @@ -2342,7 +2409,7 @@ waitpid (pid_t pid, int *stat_loc, int options) } else { timeout = INFINITE; } - if (WaitForSingleObject((HANDLE) pid, timeout) == WAIT_OBJECT_0) { + if (wait_events((HANDLE)pid, timeout) == WAIT_OBJECT_0) { pid = _cwait(stat_loc, pid, 0); #if !defined __BORLANDC__ *stat_loc <<= 8; @@ -2487,25 +2554,26 @@ myrename(const char *oldpath, const char *newpath) if (!MoveFile(oldpath, newpath)) res = -1; - if (res == 0 || (GetLastError() != ERROR_ALREADY_EXISTS - && GetLastError() != ERROR_FILE_EXISTS)) - goto done; - - if (IsWinNT()) { - if (MoveFileEx(oldpath, newpath, MOVEFILE_REPLACE_EXISTING)) - res = 0; - } else { - for (;;) { - if (!DeleteFile(newpath) && GetLastError() != ERROR_FILE_NOT_FOUND) - break; - else if (MoveFile(oldpath, newpath)) { - res = 0; - break; + if (res) { + switch (GetLastError()) { + case ERROR_ALREADY_EXISTS: + case ERROR_FILE_EXISTS: + if (IsWinNT()) { + if (MoveFileEx(oldpath, newpath, MOVEFILE_REPLACE_EXISTING)) + res = 0; + } else { + for (;;) { + if (!DeleteFile(newpath) && GetLastError() != ERROR_FILE_NOT_FOUND) + break; + else if (MoveFile(oldpath, newpath)) { + res = 0; + break; + } + } } } } -done: if (res) errno = GetLastError(); else @@ -2545,88 +2613,288 @@ mytimes(struct tms *tmbuf) } -static int win32_thread_exclusive(void) +#undef Sleep +#define yield_once() Sleep(0) +#define yield_until(condition) do yield_once(); while (!(condition)) + +static DWORD wait_events(HANDLE event, DWORD timeout) { - if (GetCurrentThreadId() == rb_MainThreadId) return FALSE; + HANDLE events[2]; + int count = 0; + DWORD ret; - SuspendThread(rb_MainThreadHandle); - return TRUE; + if (event) { + events[count++] = event; + } + events[count++] = interrupted_event; + + ret = WaitForMultipleEvents(count, events, FALSE, timeout, TRUE); + + if (ret == WAIT_OBJECT_0 + count - 1) { + ResetSignal(interrupted_event); + errno = EINTR; + } + + return ret; } -void win32_thread_resume_main(void) +static CRITICAL_SECTION* system_state(void) { - if (GetCurrentThreadId() != rb_MainThreadId) - ResumeThread(rb_MainThreadHandle); + static int initialized = 0; + static CRITICAL_SECTION syssect; + + if (!initialized) { + InitializeCriticalSection(&syssect); + initialized = 1; + } + return &syssect; } -static void win32_suspend_self(void) +static LONG flag_interrupt = -1; +static volatile DWORD tlsi_interrupt = TLS_OUT_OF_INDEXES; + +void win32_disable_interrupt(void) { - SuspendThread(GetCurrentThread()); + if (IsWinNT()) { + EnterCriticalSection(system_state()); + return; + } + + if (tlsi_interrupt == TLS_OUT_OF_INDEXES) { + tlsi_interrupt = TlsAlloc(); + } + + { + DWORD ti = (DWORD)TlsGetValue(tlsi_interrupt); + while (InterlockedIncrement(&flag_interrupt) > 0 && !ti) { + InterlockedDecrement(&flag_interrupt); + Sleep(1); + } + TlsSetValue(tlsi_interrupt, (PVOID)++ti); + } +} + +void win32_enable_interrupt(void) +{ + if (IsWinNT()) { + LeaveCriticalSection(system_state()); + return; + } + + InterlockedDecrement(&flag_interrupt); + TlsSetValue(tlsi_interrupt, (PVOID)((DWORD)TlsGetValue(tlsi_interrupt) - 1)); } -static void win32_call_handler(int arg, void (*handler)(int), CONTEXT ctx) +struct handler_arg_t { + void (*handler)(int); + int arg; + int status; + int userstate; + HANDLE handshake; +}; + +static void win32_call_handler(struct handler_arg_t* h) { - handler(arg); - ctx.ContextFlags = CONTEXT_FULL | CONTEXT_FLOATING_POINT; - SetThreadContext(rb_MainThreadHandle, &ctx); + int status; + RUBY_CRITICAL(rb_protect((VALUE (*)())h->handler, (VALUE)h->arg, &h->status); + status = h->status; + SetEvent(h->handshake)); + if (status) { + rb_jump_tag(status); + } + h->userstate = 1; /* never syscall after here */ + for (;;); /* wait here in user state */ } -static int catch_interrupt(unsigned long msec) +static struct handler_arg_t* setup_handler(struct handler_arg_t *harg, + int arg, + void (*handler)(int), + HANDLE handshake) { - return !WaitForSingleObject(rb_InterruptEvent, msec); + harg->handler = handler; + harg->arg = arg; + harg->status = 0; + harg->userstate = 0; + harg->handshake = handshake; + return harg; } -int win32_interruptible(void) +static void setup_call(CONTEXT* ctx, struct handler_arg_t *harg) { - if (catch_interrupt(0)) return TRUE; - SetEvent(rb_InterruptEvent); - return FALSE; +#ifdef _M_IX86 + DWORD *esp = (DWORD *)ctx->Esp; + *--esp = (DWORD)harg; + *--esp = ctx->Eip; + ctx->Esp = (DWORD)esp; + ctx->Eip = (DWORD)win32_call_handler; +#else +#error unsupported processor +#endif } int win32_main_context(int arg, void (*handler)(int)) { - if (!win32_thread_exclusive()) return FALSE; + static HANDLE interrupt_done = NULL; + struct handler_arg_t harg; + CONTEXT ctx_orig; + HANDLE current_thread = GetCurrentThread(); + int old_priority = GetThreadPriority(current_thread); - if (!catch_interrupt(0)) { - SetEvent(rb_InterruptEvent); - return FALSE; - } + if (GetCurrentThreadId() == main_thread.id) return FALSE; - { + SetSignal(interrupted_event); + + RUBY_CRITICAL({ /* the main thread must be in user state */ CONTEXT ctx; + SuspendThread(main_thread.handle); + SetThreadPriority(current_thread, GetThreadPriority(main_thread.handle)); + ZeroMemory(&ctx, sizeof(CONTEXT)); ctx.ContextFlags = CONTEXT_FULL | CONTEXT_FLOATING_POINT; - GetThreadContext(rb_MainThreadHandle, &ctx); -#ifdef _M_IX86 - { - DWORD *esp = (DWORD *)(ctx.Esp - sizeof(CONTEXT)); - *(CONTEXT *)esp = ctx; - *--esp = (DWORD)handler; - *--esp = arg; - *--esp = ctx.Eip; - ctx.Esp = (DWORD)esp; + GetThreadContext(main_thread.handle, &ctx); + ctx_orig = ctx; + + /* handler context setup */ + if (!interrupt_done) { + interrupt_done = CreateEvent(NULL, FALSE, FALSE, NULL); + /* anonymous one-shot event */ } - ctx.Eip = (DWORD)win32_call_handler; -#else -#error -#endif + else { + ResetEvent(interrupt_done); + } + setup_call(&ctx, setup_handler(&harg, arg, handler, interrupt_done)); ctx.ContextFlags = CONTEXT_CONTROL; - SetThreadContext(rb_MainThreadHandle, &ctx); + SetThreadContext(main_thread.handle, &ctx); + ResumeThread(main_thread.handle); + }); + + /* give a chance to the main thread */ + yield_once(); + WaitForSingleObject(interrupt_done, INFINITE); /* handshaking */ + + if (!harg.status) { + /* no exceptions raised, restore old context. */ + RUBY_CRITICAL({ + /* ensure the main thread is in user state. */ + yield_until(harg.userstate); + + SuspendThread(main_thread.handle); + ctx_orig.ContextFlags = CONTEXT_FULL | CONTEXT_FLOATING_POINT; + SetThreadContext(main_thread.handle, &ctx_orig); + ResumeThread(main_thread.handle); + }); } - ResumeThread(rb_MainThreadHandle); + /* otherwise leave the main thread raised */ + + SetThreadPriority(current_thread, old_priority); return TRUE; } -void win32_sleep(unsigned long msec) +int win32_sleep(unsigned long msec) { - int trap_immediate = rb_trap_immediate; + return wait_events(NULL, msec) != WAIT_TIMEOUT; +} + +static void catch_interrupt(void) +{ + yield_once(); + win32_sleep(0); + CHECK_INTS; +} + +void win32_enter_syscall(void) +{ + InterlockedExchange(&rb_trap_immediate, 1); + catch_interrupt(); + win32_disable_interrupt(); +} + +void win32_leave_syscall(void) +{ + win32_enable_interrupt(); + catch_interrupt(); + InterlockedExchange(&rb_trap_immediate, 0); +} + +struct asynchronous_arg_t { + /* output field */ + void* stackaddr; + + /* input field */ + VALUE (*func)(VALUE self, int argc, VALUE* argv); + VALUE self; + int argc; + VALUE* argv; +}; + +static DWORD WINAPI +call_asynchronous(PVOID argp) +{ + struct asynchronous_arg_t *arg = argp; + arg->stackaddr = &argp; + return (DWORD)arg->func(arg->self, arg->argc, arg->argv); +} + +VALUE win32_asynchronize(asynchronous_func_t func, + VALUE self, int argc, VALUE* argv, VALUE intrval) +{ + DWORD val; + BOOL interrupted = FALSE; + HANDLE thr; + + RUBY_CRITICAL({ + struct asynchronous_arg_t arg; + + arg.stackaddr = NULL; + arg.func = func; + arg.self = self; + arg.argc = argc; + arg.argv = argv; + + thr = CreateThread(NULL, 0, call_asynchronous, &arg, 0, &val); + + if (thr) { + yield_until(arg.stackaddr); + + if (wait_events(thr, INFINITE) != WAIT_OBJECT_0) { + interrupted = TRUE; + + if (TerminateThread(thr, intrval)) { + yield_once(); + } + } + + GetExitCodeThread(thr, &val); + CloseHandle(thr); + + if (interrupted) { + /* must release stack of killed thread, why doesn't Windows? */ + MEMORY_BASIC_INFORMATION m; + + memset(&m, 0, sizeof(m)); + if (!VirtualQuery(arg.stackaddr, &m, sizeof(m))) { + Debug(fprintf(stderr, "couldn't get stack base:%p:%d\n", + arg.stackaddr, GetLastError())); + } + else if (!VirtualFree(m.AllocationBase, 0, MEM_RELEASE)) { + Debug(fprintf(stderr, "couldn't release stack:%p:%d\n", + m.AllocationBase, GetLastError())); + } + } + } + }); + + if (!thr) { + rb_fatal("failed to launch waiter thread:%d", GetLastError()); + } + + if (interrupted) { + errno = EINTR; + CHECK_INTS; + } - if (trap_immediate) - TRAP_END; - catch_interrupt(msec); - if (trap_immediate) - TRAP_BEG; + return val; } diff --git a/win32/win32.h b/win32/win32.h index d0e18152e5..2976781401 100644 --- a/win32/win32.h +++ b/win32/win32.h @@ -427,17 +427,23 @@ struct tms { #define times mytimes /* thread stuff */ -/* initialized by NtInitialize() */ -HANDLE rb_CurrentProcessHandle; -HANDLE rb_MainThreadHandle; -HANDLE rb_InterruptEvent; -DWORD rb_MainThreadId; - HANDLE GetCurrentThreadHandle(void); int win32_main_context(int arg, void (*handler)(int)); -int win32_interruptible(void); -void win32_thread_resume_main(void); -void win32_sleep(unsigned long msec); -#define Sleep(msec) win32_sleep(msec) +int win32_sleep(unsigned long msec); +void win32_enter_syscall(void); +void win32_leave_syscall(void); +void win32_disable_interrupt(void); +void win32_enable_interrupt(void); +#define Sleep(msec) (void)win32_sleep(msec) + +/* +== ***CAUTION*** +Since this function is very dangerous, ((*NEVER*)) +* lock any HANDLEs(i.e. Mutex, Semaphore, CriticalSection and so on) or, +* use anything like TRAP_BEG...TRAP_END block structure, +in asynchronous_func_t. +*/ +typedef DWORD (*asynchronous_func_t)(DWORD self, int argc, DWORD* argv); +DWORD win32_asynchronize(asynchronous_func_t func, DWORD self, int argc, DWORD* argv, DWORD intrval); #endif -- cgit v1.2.3