diff options
Diffstat (limited to 'otherlibs/systhreads/win32.c')
| -rw-r--r-- | otherlibs/systhreads/win32.c | 719 |
1 files changed, 0 insertions, 719 deletions
diff --git a/otherlibs/systhreads/win32.c b/otherlibs/systhreads/win32.c deleted file mode 100644 index b8cdaef302..0000000000 --- a/otherlibs/systhreads/win32.c +++ /dev/null @@ -1,719 +0,0 @@ -/***********************************************************************/ -/* */ -/* Objective Caml */ -/* */ -/* Xavier Leroy and Pascal Cuoq, INRIA Rocquencourt */ -/* */ -/* Copyright 1995 Institut National de Recherche en Informatique et */ -/* en Automatique. All rights reserved. This file is distributed */ -/* under the terms of the GNU Library General Public License, with */ -/* the special exception on linking described in file ../../LICENSE. */ -/* */ -/***********************************************************************/ - -/* $Id$ */ - -/* Thread interface for Win32 threads */ - -#include <windows.h> -#include <process.h> -#include <signal.h> -#include <stdio.h> -#include <stdlib.h> -#include "alloc.h" -#include "backtrace.h" -#include "callback.h" -#include "custom.h" -#include "fail.h" -#include "io.h" -#include "memory.h" -#include "misc.h" -#include "mlvalues.h" -#include "printexc.h" -#include "roots.h" -#include "signals.h" -#ifdef NATIVE_CODE -#include "stack.h" -#else -#include "stacks.h" -#endif -#include "sys.h" - -/* Initial size of stack when a thread is created (4 Ko) */ -#define Thread_stack_size (Stack_size / 4) - -/* Max computation time before rescheduling, in milliseconds (50ms) */ -#define Thread_timeout 50 - -/* Signal used for timer preemption (any unused, legal signal number) */ -#define SIGTIMER SIGTERM - -/* The ML value describing a thread (heap-allocated) */ - -struct caml_thread_handle { - value final_fun; /* Finalization function */ - HANDLE handle; /* Windows handle */ -}; - -struct caml_thread_descr { - value ident; /* Unique integer ID */ - value start_closure; /* The closure to start this thread */ - struct caml_thread_handle * thread_handle; /* Finalized object with handle */ -}; - -#define Ident(v) (((struct caml_thread_descr *)(v))->ident) -#define Start_closure(v) (((struct caml_thread_descr *)(v))->start_closure) -#define Threadhandle(v) (((struct caml_thread_descr *)(v))->thread_handle) - -/* The infos on threads (allocated via malloc()) */ - -struct caml_thread_struct { - HANDLE wthread; /* The Windows thread handle */ - value descr; /* The heap-allocated descriptor (root) */ - struct caml_thread_struct * next; /* Double linking of running threads */ - struct caml_thread_struct * prev; -#ifdef NATIVE_CODE - char * bottom_of_stack; /* Saved value of caml_bottom_of_stack */ - unsigned long last_retaddr; /* Saved value of caml_last_return_address */ - value * gc_regs; /* Saved value of caml_gc_regs */ - char * exception_pointer; /* Saved value of caml_exception_pointer */ - struct caml__roots_block * local_roots; /* Saved value of local_roots */ -#else - value * stack_low; /* The execution stack for this thread */ - value * stack_high; - value * stack_threshold; - value * sp; /* Saved value of extern_sp for this thread */ - value * trapsp; /* Saved value of trapsp for this thread */ - struct caml__roots_block * local_roots; /* Saved value of local_roots */ - struct longjmp_buffer * external_raise; /* Saved external_raise */ - int backtrace_pos; /* Saved backtrace_pos */ - code_t * backtrace_buffer; /* Saved backtrace_buffer */ - value backtrace_last_exn; /* Saved backtrace_last_exn (root) */ -#endif -}; - -typedef struct caml_thread_struct * caml_thread_t; - -/* The descriptor for the currently executing thread (thread-specific) */ - -static caml_thread_t curr_thread = NULL; - -/* The global mutex used to ensure that at most one thread is running - Caml code */ -static HANDLE caml_mutex; - -/* The key used for storing the thread descriptor in the specific data - of the corresponding Posix thread. */ -static DWORD thread_descriptor_key; - -/* The key used for unlocking I/O channels on exceptions */ -static DWORD last_channel_locked_key; - -/* Identifier for next thread creation */ -static long thread_next_ident = 0; - -/* Forward declarations */ - -static void caml_wthread_error (char * msg); - -/* Hook for scanning the stacks of the other threads */ - -static void (*prev_scan_roots_hook) (scanning_action); - -static void caml_thread_scan_roots(scanning_action action) -{ - caml_thread_t th; - - th = curr_thread; - do { - (*action)(th->descr, &th->descr); -#ifndef NATIVE_CODE - (*action)(th->backtrace_last_exn, &th->backtrace_last_exn); -#endif - /* Don't rescan the stack of the current thread, it was done already */ - if (th != curr_thread) { -#ifdef NATIVE_CODE - if (th->bottom_of_stack != NULL) - do_local_roots(action, th->bottom_of_stack, th->last_retaddr, - th->gc_regs, th->local_roots); -#else - do_local_roots(action, th->sp, th->stack_high, th->local_roots); -#endif - } - th = th->next; - } while (th != curr_thread); - /* Hook */ - if (prev_scan_roots_hook != NULL) (*prev_scan_roots_hook)(action); -} - -/* Hooks for enter_blocking_section and leave_blocking_section */ - -static void (*prev_enter_blocking_section_hook) () = NULL; -static void (*prev_leave_blocking_section_hook) () = NULL; - -static void caml_thread_enter_blocking_section(void) -{ - if (prev_enter_blocking_section_hook != NULL) - (*prev_enter_blocking_section_hook)(); - /* Save the stack-related global variables in the thread descriptor - of the current thread */ -#ifdef NATIVE_CODE - curr_thread->bottom_of_stack = caml_bottom_of_stack; - curr_thread->last_retaddr = caml_last_return_address; - curr_thread->gc_regs = caml_gc_regs; - curr_thread->exception_pointer = caml_exception_pointer; - curr_thread->local_roots = local_roots; -#else - curr_thread->stack_low = stack_low; - curr_thread->stack_high = stack_high; - curr_thread->stack_threshold = stack_threshold; - curr_thread->sp = extern_sp; - curr_thread->trapsp = trapsp; - curr_thread->local_roots = local_roots; - curr_thread->external_raise = external_raise; - curr_thread->backtrace_pos = backtrace_pos; - curr_thread->backtrace_buffer = backtrace_buffer; - curr_thread->backtrace_last_exn = backtrace_last_exn; -#endif - /* Release the global mutex */ - ReleaseMutex(caml_mutex); -} - -static void caml_thread_leave_blocking_section(void) -{ - /* Re-acquire the global mutex */ - WaitForSingleObject(caml_mutex, INFINITE); - /* Update curr_thread to point to the thread descriptor corresponding - to the thread currently executing */ - curr_thread = TlsGetValue(thread_descriptor_key); - /* Restore the stack-related global variables */ -#ifdef NATIVE_CODE - caml_bottom_of_stack= curr_thread->bottom_of_stack; - caml_last_return_address = curr_thread->last_retaddr; - caml_gc_regs = curr_thread->gc_regs; - caml_exception_pointer = curr_thread->exception_pointer; - local_roots = curr_thread->local_roots; -#else - stack_low = curr_thread->stack_low; - stack_high = curr_thread->stack_high; - stack_threshold = curr_thread->stack_threshold; - extern_sp = curr_thread->sp; - trapsp = curr_thread->trapsp; - local_roots = curr_thread->local_roots; - external_raise = curr_thread->external_raise; - backtrace_pos = curr_thread->backtrace_pos; - backtrace_buffer = curr_thread->backtrace_buffer; - backtrace_last_exn = curr_thread->backtrace_last_exn; -#endif - if (prev_leave_blocking_section_hook != NULL) - (*prev_leave_blocking_section_hook)(); -} - -/* Hooks for I/O locking */ - -static void caml_io_mutex_free(struct channel * chan) -{ - HANDLE mutex = chan->mutex; - if (mutex != NULL) { - CloseHandle(mutex); - } -} - -static void caml_io_mutex_lock(struct channel * chan) -{ - if (chan->mutex == NULL) { - HANDLE mutex = CreateMutex(NULL, FALSE, NULL); - if (mutex == NULL) caml_wthread_error("Thread.iolock"); - chan->mutex = (void *) mutex; - } - enter_blocking_section(); - WaitForSingleObject((HANDLE) chan->mutex, INFINITE); - /* Problem: if a signal occurs at this point, - and the signal handler raises an exception, we will not - unlock the mutex. The alternative (doing the setspecific - before locking the mutex is also incorrect, since we could - then unlock a mutex that is unlocked or locked by someone else. */ - TlsSetValue(last_channel_locked_key, (void *) chan); - leave_blocking_section(); -} - -static void caml_io_mutex_unlock(struct channel * chan) -{ - ReleaseMutex((HANDLE) chan->mutex); - TlsSetValue(last_channel_locked_key, NULL); -} - -static void caml_io_mutex_unlock_exn(void) -{ - struct channel * chan = TlsGetValue(last_channel_locked_key); - if (chan != NULL) caml_io_mutex_unlock(chan); -} - -/* The "tick" thread fakes a signal at regular intervals. */ - -static void caml_thread_tick(void * arg) -{ - while(1) { - Sleep(Thread_timeout); - pending_signal = SIGTIMER; -#ifdef NATIVE_CODE - young_limit = young_end; -#else - something_to_do = 1; -#endif - } -} - -static void caml_thread_finalize(value vthread) -{ - CloseHandle(((struct caml_thread_handle *)vthread)->handle); -} - -/* Initialize the thread machinery */ - -CAMLprim value caml_thread_initialize(value unit) -{ - value vthread = Val_unit; - value descr; - HANDLE tick_thread; - unsigned long tick_id; - - /* Protect against repeated initialization (PR#1325) */ - if (curr_thread != NULL) return Val_unit; - Begin_root (vthread); - /* Initialize the main mutex and acquire it */ - caml_mutex = CreateMutex(NULL, TRUE, NULL); - if (caml_mutex == NULL) caml_wthread_error("Thread.init"); - /* Initialize the TLS keys */ - thread_descriptor_key = TlsAlloc(); - last_channel_locked_key = TlsAlloc(); - /* Create a finalized value to hold thread handle */ - vthread = alloc_final(sizeof(struct caml_thread_handle) / sizeof(value), - caml_thread_finalize, 1, 1000); - ((struct caml_thread_handle *)vthread)->handle = NULL; - /* Create a descriptor for the current thread */ - descr = alloc_tuple(sizeof(struct caml_thread_descr) / sizeof(value)); - Ident(descr) = Val_long(thread_next_ident); - Start_closure(descr) = Val_unit; - Threadhandle(descr) = (struct caml_thread_handle *) vthread; - thread_next_ident++; - /* Create an info block for the current thread */ - curr_thread = - (caml_thread_t) stat_alloc(sizeof(struct caml_thread_struct)); - DuplicateHandle(GetCurrentProcess(), GetCurrentThread(), - GetCurrentProcess(), &(curr_thread->wthread), - 0, FALSE, DUPLICATE_SAME_ACCESS); - if (curr_thread->wthread == NULL) caml_wthread_error("Thread.init"); - ((struct caml_thread_handle *)vthread)->handle = curr_thread->wthread; - curr_thread->descr = descr; - curr_thread->next = curr_thread; - curr_thread->prev = curr_thread; - /* The stack-related fields will be filled in at the next - enter_blocking_section */ - /* Associate the thread descriptor with the thread */ - TlsSetValue(thread_descriptor_key, (void *) curr_thread); - /* Set up the hooks */ - prev_scan_roots_hook = scan_roots_hook; - scan_roots_hook = caml_thread_scan_roots; - prev_enter_blocking_section_hook = enter_blocking_section_hook; - enter_blocking_section_hook = caml_thread_enter_blocking_section; - prev_leave_blocking_section_hook = leave_blocking_section_hook; - leave_blocking_section_hook = caml_thread_leave_blocking_section; - channel_mutex_free = caml_io_mutex_free; - channel_mutex_lock = caml_io_mutex_lock; - channel_mutex_unlock = caml_io_mutex_unlock; - channel_mutex_unlock_exn = caml_io_mutex_unlock_exn; - /* Fork the tick thread */ - tick_thread = (HANDLE) _beginthread(caml_thread_tick, 0, NULL); - if (tick_thread == (HANDLE)(-1)) caml_wthread_error("Thread.init"); - CloseHandle(tick_thread); - End_roots(); - return Val_unit; -} - -/* Create a thread */ - -static void caml_thread_start(void * arg) -{ - caml_thread_t th = (caml_thread_t) arg; - value clos; - - /* Associate the thread descriptor with the thread */ - TlsSetValue(thread_descriptor_key, (void *) th); - TlsSetValue(last_channel_locked_key, NULL); - /* Acquire the global mutex and set up the stack variables */ - leave_blocking_section(); - /* Callback the closure */ - clos = Start_closure(th->descr); - modify(&(Start_closure(th->descr)), Val_unit); - callback_exn(clos, Val_unit); - /* Remove th from the doubly-linked list of threads */ - th->next->prev = th->prev; - th->prev->next = th->next; - /* Release the main mutex (forever) */ - async_signal_mode = 1; - ReleaseMutex(caml_mutex); -#ifndef NATIVE_CODE - /* Free the memory resources */ - stat_free(th->stack_low); - if (th->backtrace_buffer != NULL) free(th->backtrace_buffer); -#endif - /* Free the thread descriptor */ - stat_free(th); - /* The thread now stops running */ -} - -CAMLprim value caml_thread_new(value clos) -{ - caml_thread_t th; - value vthread = Val_unit; - value descr; - unsigned long th_id; - - Begin_roots2 (clos, vthread) - /* Create a finalized value to hold thread handle */ - vthread = alloc_final(sizeof(struct caml_thread_handle) / sizeof(value), - caml_thread_finalize, 1, 1000); - ((struct caml_thread_handle *)vthread)->handle = NULL; - /* Create a descriptor for the new thread */ - descr = alloc_tuple(sizeof(struct caml_thread_descr) / sizeof(value)); - Ident(descr) = Val_long(thread_next_ident); - Start_closure(descr) = clos; - Threadhandle(descr) = (struct caml_thread_handle *) vthread; - thread_next_ident++; - /* Create an info block for the current thread */ - th = (caml_thread_t) stat_alloc(sizeof(struct caml_thread_struct)); - th->descr = descr; -#ifdef NATIVE_CODE - th->bottom_of_stack = NULL; - th->exception_pointer = NULL; - th->local_roots = NULL; -#else - /* Allocate the stacks */ - th->stack_low = (value *) stat_alloc(Thread_stack_size); - th->stack_high = th->stack_low + Thread_stack_size / sizeof(value); - th->stack_threshold = th->stack_low + Stack_threshold / sizeof(value); - th->sp = th->stack_high; - th->trapsp = th->stack_high; - th->local_roots = NULL; - th->external_raise = NULL; - th->backtrace_pos = 0; - th->backtrace_buffer = NULL; - th->backtrace_last_exn = Val_unit; -#endif - /* Add thread info block to the list of threads */ - th->next = curr_thread->next; - th->prev = curr_thread; - curr_thread->next->prev = th; - curr_thread->next = th; - /* Fork the new thread */ -#if 0 - th->wthread = - CreateThread(NULL,0, (LPTHREAD_START_ROUTINE) caml_thread_start, - (void *) th, 0, &th_id); - if (th->wthread == NULL) { -#endif - th->wthread = (HANDLE) _beginthread(caml_thread_start, 0, (void *) th); - if (th->wthread == (HANDLE)(-1)) { - /* Fork failed, remove thread info block from list of threads */ - th->next->prev = curr_thread; - curr_thread->next = th->next; -#ifndef NATIVE_CODE - stat_free(th->stack_low); -#endif - stat_free(th); - caml_wthread_error("Thread.create"); - } - ((struct caml_thread_handle *)vthread)->handle = th->wthread; - End_roots(); - return descr; -} - -/* Return the current thread */ - -CAMLprim value caml_thread_self(value unit) -{ - if (curr_thread == NULL) invalid_argument("Thread.self: not initialized"); - return curr_thread->descr; -} - -/* Return the identifier of a thread */ - -CAMLprim value caml_thread_id(value th) -{ - return Ident(th); -} - -/* Print uncaught exception and backtrace */ - -CAMLprim value caml_thread_uncaught_exception(value exn) -{ - char * msg = format_caml_exception(exn); - fprintf(stderr, "Thread %d killed on uncaught exception %s\n", - Int_val(Ident(curr_thread->descr)), msg); - free(msg); -#ifndef NATIVE_CODE - if (backtrace_active) print_exception_backtrace(); -#endif - fflush(stderr); - return Val_unit; -} - -/* Allow re-scheduling */ - -CAMLprim value caml_thread_yield(value unit) -{ - enter_blocking_section(); - Sleep(0); - leave_blocking_section(); - return Val_unit; -} - -/* Suspend the current thread until another thread terminates */ - -CAMLprim value caml_thread_join(value th) -{ - HANDLE h; - Begin_root(th) /* prevent deallocation of handle */ - h = Threadhandle(th)->handle; - enter_blocking_section(); - WaitForSingleObject(h, INFINITE); - leave_blocking_section(); - End_roots(); - return Val_unit; -} - -/* Mutex operations */ - -#define Mutex_val(v) (*((HANDLE *) Data_custom_val(v))) -#define Max_mutex_number 1000 - -static void caml_mutex_finalize(value mut) -{ - CloseHandle(Mutex_val(mut)); -} - -static int caml_mutex_compare(value wrapper1, value wrapper2) -{ - HANDLE h1 = Mutex_val(wrapper1); - HANDLE h2 = Mutex_val(wrapper2); - return h1 == h2 ? 0 : h1 < h2 ? -1 : 1; -} - -static struct custom_operations caml_mutex_ops = { - "_mutex", - caml_mutex_finalize, - caml_mutex_compare, - custom_hash_default, - custom_serialize_default, - custom_deserialize_default -}; - -CAMLprim value caml_mutex_new(value unit) -{ - value mut; - mut = alloc_custom(&caml_mutex_ops, sizeof(HANDLE), 1, Max_mutex_number); - Mutex_val(mut) = CreateMutex(0, FALSE, NULL); - if (Mutex_val(mut) == NULL) caml_wthread_error("Mutex.create"); - return mut; -} - -CAMLprim value caml_mutex_lock(value mut) -{ - int retcode; - Begin_root(mut) /* prevent deallocation of mutex */ - enter_blocking_section(); - retcode = WaitForSingleObject(Mutex_val(mut), INFINITE); - leave_blocking_section(); - End_roots(); - if (retcode == WAIT_FAILED) caml_wthread_error("Mutex.lock"); - return Val_unit; -} - -CAMLprim value caml_mutex_unlock(value mut) -{ - BOOL retcode; - Begin_root(mut) /* prevent deallocation of mutex */ - enter_blocking_section(); - retcode = ReleaseMutex(Mutex_val(mut)); - leave_blocking_section(); - End_roots(); - if (!retcode) caml_wthread_error("Mutex.unlock"); - return Val_unit; -} - -CAMLprim value caml_mutex_try_lock(value mut) -{ - int retcode; - retcode = WaitForSingleObject(Mutex_val(mut), 0); - if (retcode == WAIT_FAILED || retcode == WAIT_ABANDONED) - caml_wthread_error("Mutex.try_lock"); - return Val_bool(retcode == WAIT_OBJECT_0); -} - -/* Delay */ - -CAMLprim value caml_thread_delay(value val) -{ - enter_blocking_section(); - Sleep((DWORD)(Double_val(val)*1000)); /* milliseconds */ - leave_blocking_section(); - return Val_unit; -} - -/* Conditions operations */ - -struct caml_condvar { - unsigned long count; /* Number of waiting threads */ - HANDLE sem; /* Semaphore on which threads are waiting */ -}; - -#define Condition_val(v) ((struct caml_condvar *) Data_custom_val(v)) -#define Max_condition_number 1000 - -static void caml_condition_finalize(value cond) -{ - CloseHandle(Condition_val(cond)->sem); -} - -static int caml_condition_compare(value wrapper1, value wrapper2) -{ - HANDLE h1 = Condition_val(wrapper1)->sem; - HANDLE h2 = Condition_val(wrapper2)->sem; - return h1 == h2 ? 0 : h1 < h2 ? -1 : 1; -} - -static struct custom_operations caml_condition_ops = { - "_condition", - caml_condition_finalize, - caml_condition_compare, - custom_hash_default, - custom_serialize_default, - custom_deserialize_default -}; - -CAMLprim value caml_condition_new(value unit) -{ - value cond; - cond = alloc_custom(&caml_condition_ops, sizeof(struct caml_condvar), - 1, Max_condition_number); - Condition_val(cond)->sem = CreateSemaphore(NULL, 0, 0x7FFFFFFF, NULL); - if (Condition_val(cond)->sem == NULL) - caml_wthread_error("Condition.create"); - Condition_val(cond)->count = 0; - return cond; -} - -CAMLprim value caml_condition_wait(value cond, value mut) -{ - int retcode; - HANDLE m = Mutex_val(mut); - HANDLE s = Condition_val(cond)->sem; - HANDLE handles[2]; - - Condition_val(cond)->count ++; - Begin_roots2(cond, mut) /* prevent deallocation of cond and mutex */ - enter_blocking_section(); - /* Release mutex */ - ReleaseMutex(m); - /* Wait for semaphore to be non-null, and decrement it. - Simultaneously, re-acquire mutex. */ - handles[0] = s; - handles[1] = m; - retcode = WaitForMultipleObjects(2, handles, TRUE, INFINITE); - leave_blocking_section(); - End_roots(); - if (retcode == WAIT_FAILED) caml_wthread_error("Condition.wait"); - return Val_unit; -} - -CAMLprim value caml_condition_signal(value cond) -{ - HANDLE s = Condition_val(cond)->sem; - - if (Condition_val(cond)->count > 0) { - Condition_val(cond)->count --; - Begin_root(cond) /* prevent deallocation of cond */ - enter_blocking_section(); - /* Increment semaphore by 1, waking up one waiter */ - ReleaseSemaphore(s, 1, NULL); - leave_blocking_section(); - End_roots(); - } - return Val_unit; -} - -CAMLprim value caml_condition_broadcast(value cond) -{ - HANDLE s = Condition_val(cond)->sem; - unsigned long c = Condition_val(cond)->count; - - if (c > 0) { - Condition_val(cond)->count = 0; - Begin_root(cond) /* prevent deallocation of cond */ - enter_blocking_section(); - /* Increment semaphore by c, waking up all waiters */ - ReleaseSemaphore(s, c, NULL); - leave_blocking_section(); - End_roots(); - } - return Val_unit; -} - -/* Synchronous signal wait */ - -static HANDLE wait_signal_event[NSIG]; -static int * wait_signal_received[NSIG]; - -static void caml_wait_signal_handler(int signo) -{ - *(wait_signal_received[signo]) = signo; - SetEvent(wait_signal_event[signo]); -} - -typedef void (*sighandler_type)(int); - -CAMLprim value caml_wait_signal(value sigs) -{ - HANDLE event; - int res, s, retcode; - value l; - sighandler_type oldsignals[NSIG]; - - Begin_root(sigs); - event = CreateEvent(NULL, FALSE, FALSE, NULL); - if (event == NULL) - caml_wthread_error("Thread.wait_signal (CreateEvent)"); - res = 0; - for (l = sigs; l != Val_int(0); l = Field(l, 1)) { - s = convert_signal_number(Int_val(Field(l, 0))); - oldsignals[s] = signal(s, caml_wait_signal_handler); - if (oldsignals[s] == SIG_ERR) { - CloseHandle(event); - caml_wthread_error("Thread.wait_signal (signal)"); - } - wait_signal_event[s] = event; - wait_signal_received[s] = &res; - } - enter_blocking_section(); - retcode = WaitForSingleObject(event, INFINITE); - leave_blocking_section(); - for (l = sigs; l != Val_int(0); l = Field(l, 1)) { - s = convert_signal_number(Int_val(Field(l, 0))); - signal(s, oldsignals[s]); - } - CloseHandle(event); - End_roots(); - if (retcode == WAIT_FAILED) - caml_wthread_error("Thread.wait_signal (WaitForSingleObject)"); - return Val_int(res); -} - -/* Error report */ - -static void caml_wthread_error(char * msg) -{ - char errmsg[1024]; - sprintf(errmsg, "%s: error code %lx", msg, GetLastError()); - raise_sys_error(copy_string(errmsg)); -} |
