1996-09-09 05:25:20 -07:00
|
|
|
/***********************************************************************/
|
|
|
|
/* */
|
1997-09-01 07:26:16 -07:00
|
|
|
/* Objective Caml */
|
1996-09-09 05:25:20 -07:00
|
|
|
/* */
|
|
|
|
/* Xavier Leroy and Damien Doligez, INRIA Rocquencourt */
|
|
|
|
/* */
|
|
|
|
/* Copyright 1995 Institut National de Recherche en Informatique et */
|
|
|
|
/* Automatique. Distributed only by permission. */
|
|
|
|
/* */
|
|
|
|
/***********************************************************************/
|
|
|
|
|
|
|
|
/* $Id$ */
|
|
|
|
|
|
|
|
/* Thread interface for POSIX 1003.1c threads */
|
|
|
|
|
1997-11-17 05:04:18 -08:00
|
|
|
#include <errno.h>
|
1997-05-14 02:43:45 -07:00
|
|
|
#include <string.h>
|
1996-09-09 05:25:20 -07:00
|
|
|
#include <pthread.h>
|
|
|
|
#include <signal.h>
|
|
|
|
#include <sys/time.h>
|
|
|
|
#include "alloc.h"
|
1997-05-14 02:43:45 -07:00
|
|
|
#include "callback.h"
|
1996-09-09 05:25:20 -07:00
|
|
|
#include "fail.h"
|
1997-08-29 08:37:22 -07:00
|
|
|
#include "io.h"
|
1996-09-09 05:25:20 -07:00
|
|
|
#include "memory.h"
|
|
|
|
#include "misc.h"
|
|
|
|
#include "mlvalues.h"
|
|
|
|
#include "roots.h"
|
|
|
|
#include "signals.h"
|
1997-11-20 07:34:38 -08:00
|
|
|
#ifdef NATIVE_CODE
|
|
|
|
#include "stack.h"
|
|
|
|
#else
|
1996-09-09 05:25:20 -07:00
|
|
|
#include "stacks.h"
|
1997-11-20 07:34:38 -08:00
|
|
|
#endif
|
1996-09-09 05:25:20 -07:00
|
|
|
#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 microseconds (50ms) */
|
|
|
|
#define Thread_timeout 50000
|
|
|
|
|
1997-08-29 08:37:22 -07:00
|
|
|
/* The ML value describing a thread (heap-allocated) */
|
|
|
|
|
|
|
|
struct caml_thread_descr {
|
|
|
|
value ident; /* Unique integer ID */
|
|
|
|
value start_closure; /* The closure to start this thread */
|
|
|
|
value terminated; /* Mutex held while the thread is running */
|
|
|
|
};
|
|
|
|
|
|
|
|
#define Ident(v) (((struct caml_thread_descr *)(v))->ident)
|
|
|
|
#define Start_closure(v) (((struct caml_thread_descr *)(v))->start_closure)
|
|
|
|
#define Terminated(v) (((struct caml_thread_descr *)(v))->terminated)
|
|
|
|
|
|
|
|
/* The infos on threads (allocated via malloc()) */
|
1996-09-09 05:25:20 -07:00
|
|
|
|
|
|
|
struct caml_thread_struct {
|
|
|
|
pthread_t pthread; /* The Posix thread id */
|
1997-08-29 08:37:22 -07:00
|
|
|
value descr; /* The heap-allocated descriptor */
|
|
|
|
struct caml_thread_struct * next; /* Double linking of running threads */
|
|
|
|
struct caml_thread_struct * prev;
|
|
|
|
#ifdef NATIVE_CODE
|
1997-11-27 08:28:40 -08:00
|
|
|
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 */
|
1997-08-29 08:37:22 -07:00
|
|
|
char * exception_pointer; /* Saved value of caml_exception_pointer */
|
|
|
|
struct caml__roots_block * local_roots; /* Saved value of local_roots */
|
|
|
|
#else
|
1996-09-09 05:25:20 -07:00
|
|
|
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 */
|
1997-08-29 08:37:22 -07:00
|
|
|
struct caml__roots_block * local_roots; /* Saved value of local_roots */
|
1996-09-09 05:25:20 -07:00
|
|
|
struct longjmp_buffer * external_raise; /* Saved external_raise */
|
1997-08-29 08:37:22 -07:00
|
|
|
#endif
|
1996-09-09 05:25:20 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
typedef struct caml_thread_struct * caml_thread_t;
|
|
|
|
|
1997-08-29 08:37:22 -07:00
|
|
|
/* The descriptor for the currently executing thread */
|
|
|
|
|
|
|
|
static caml_thread_t curr_thread = NULL;
|
1996-09-09 05:25:20 -07:00
|
|
|
|
|
|
|
/* The global mutex used to ensure that at most one thread is running
|
|
|
|
Caml code */
|
1997-08-29 08:37:22 -07:00
|
|
|
static pthread_mutex_t caml_mutex;
|
1996-09-09 05:25:20 -07:00
|
|
|
|
|
|
|
/* The key used for storing the thread descriptor in the specific data
|
|
|
|
of the corresponding Posix thread. */
|
1997-09-01 07:26:16 -07:00
|
|
|
static pthread_key_t thread_descriptor_key;
|
1996-09-09 05:25:20 -07:00
|
|
|
|
1997-08-29 08:37:22 -07:00
|
|
|
/* The key used for unlocking I/O channels on exceptions */
|
1997-09-01 07:26:16 -07:00
|
|
|
static pthread_key_t last_channel_locked_key;
|
1997-08-29 08:37:22 -07:00
|
|
|
|
1996-09-09 05:25:20 -07:00
|
|
|
/* Identifier for next thread creation */
|
|
|
|
static long thread_next_ident = 0;
|
|
|
|
|
|
|
|
/* Forward declarations */
|
|
|
|
|
1997-09-02 05:55:01 -07:00
|
|
|
value caml_mutex_new (value);
|
|
|
|
value caml_mutex_lock (value);
|
|
|
|
value caml_mutex_unlock (value);
|
|
|
|
static void caml_pthread_check (int, char *);
|
1996-09-09 05:25:20 -07:00
|
|
|
|
|
|
|
/* Hook for scanning the stacks of the other threads */
|
|
|
|
|
1997-09-02 05:55:01 -07:00
|
|
|
static void (*prev_scan_roots_hook) (scanning_action);
|
1996-09-09 05:25:20 -07:00
|
|
|
|
1997-09-02 05:55:01 -07:00
|
|
|
static void caml_thread_scan_roots(scanning_action action)
|
1996-09-09 05:25:20 -07:00
|
|
|
{
|
1997-08-29 08:37:22 -07:00
|
|
|
caml_thread_t th;
|
1996-09-09 05:25:20 -07:00
|
|
|
|
1997-10-24 08:53:03 -07:00
|
|
|
th = curr_thread;
|
|
|
|
do {
|
1997-08-29 08:37:22 -07:00
|
|
|
(*action)(th->descr, &th->descr);
|
1997-10-24 08:53:03 -07:00
|
|
|
/* Don't rescan the stack of the current thread, it was done already */
|
|
|
|
if (th != curr_thread) {
|
1997-08-29 08:37:22 -07:00
|
|
|
#ifdef NATIVE_CODE
|
1997-11-27 08:28:40 -08:00
|
|
|
if (th->bottom_of_stack != NULL)
|
|
|
|
do_local_roots(action, th->bottom_of_stack, th->last_retaddr,
|
|
|
|
th->gc_regs, th->local_roots);
|
1997-08-29 08:37:22 -07:00
|
|
|
#else
|
1997-10-24 08:53:03 -07:00
|
|
|
do_local_roots(action, th->sp, th->stack_high, th->local_roots);
|
1997-08-29 08:37:22 -07:00
|
|
|
#endif
|
1997-10-24 08:53:03 -07:00
|
|
|
}
|
|
|
|
th = th->next;
|
|
|
|
} while (th != curr_thread);
|
1996-09-09 05:25:20 -07:00
|
|
|
/* Hook */
|
|
|
|
if (prev_scan_roots_hook != NULL) (*prev_scan_roots_hook)(action);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Hooks for enter_blocking_section and leave_blocking_section */
|
|
|
|
|
1997-08-29 08:37:22 -07:00
|
|
|
static void (*prev_enter_blocking_section_hook) () = NULL;
|
|
|
|
static void (*prev_leave_blocking_section_hook) () = NULL;
|
1996-09-09 05:25:20 -07:00
|
|
|
|
1997-09-02 05:55:01 -07:00
|
|
|
static void caml_thread_enter_blocking_section(void)
|
1996-09-09 05:25:20 -07:00
|
|
|
{
|
|
|
|
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 */
|
1997-08-29 08:37:22 -07:00
|
|
|
#ifdef NATIVE_CODE
|
1997-11-27 08:28:40 -08:00
|
|
|
curr_thread->bottom_of_stack = caml_bottom_of_stack;
|
|
|
|
curr_thread->last_retaddr = caml_last_return_address;
|
|
|
|
curr_thread->gc_regs = caml_gc_regs;
|
1997-08-29 08:37:22 -07:00
|
|
|
curr_thread->exception_pointer = caml_exception_pointer;
|
|
|
|
curr_thread->local_roots = local_roots;
|
|
|
|
#else
|
1996-09-09 05:25:20 -07:00
|
|
|
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;
|
1997-08-29 08:37:22 -07:00
|
|
|
#endif
|
1996-09-09 05:25:20 -07:00
|
|
|
/* Release the global mutex */
|
|
|
|
pthread_mutex_unlock(&caml_mutex);
|
|
|
|
}
|
|
|
|
|
1997-09-02 05:55:01 -07:00
|
|
|
static void caml_thread_leave_blocking_section(void)
|
1996-09-09 05:25:20 -07:00
|
|
|
{
|
|
|
|
/* Re-acquire the global mutex */
|
|
|
|
pthread_mutex_lock(&caml_mutex);
|
1997-08-29 08:37:22 -07:00
|
|
|
/* Update curr_thread to point to the thread descriptor corresponding
|
|
|
|
to the thread currently executing */
|
1996-09-09 05:25:20 -07:00
|
|
|
curr_thread = pthread_getspecific(thread_descriptor_key);
|
1997-08-29 08:37:22 -07:00
|
|
|
/* Restore the stack-related global variables */
|
|
|
|
#ifdef NATIVE_CODE
|
1997-11-27 08:28:40 -08:00
|
|
|
caml_bottom_of_stack= curr_thread->bottom_of_stack;
|
|
|
|
caml_last_return_address = curr_thread->last_retaddr;
|
|
|
|
caml_gc_regs = curr_thread->gc_regs;
|
1997-08-29 08:37:22 -07:00
|
|
|
caml_exception_pointer = curr_thread->exception_pointer;
|
|
|
|
local_roots = curr_thread->local_roots;
|
|
|
|
#else
|
1996-09-09 05:25:20 -07:00
|
|
|
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;
|
1997-08-29 08:37:22 -07:00
|
|
|
#endif
|
1996-09-09 05:25:20 -07:00
|
|
|
if (prev_leave_blocking_section_hook != NULL)
|
|
|
|
(*prev_leave_blocking_section_hook)();
|
|
|
|
}
|
|
|
|
|
1997-08-29 08:37:22 -07:00
|
|
|
/* Hooks for I/O locking */
|
|
|
|
|
1997-09-02 05:55:01 -07:00
|
|
|
static void caml_io_mutex_free(struct channel *chan)
|
1997-08-29 08:37:22 -07:00
|
|
|
{
|
|
|
|
pthread_mutex_t * mutex = chan->mutex;
|
|
|
|
if (mutex != NULL) {
|
|
|
|
pthread_mutex_destroy(mutex);
|
|
|
|
stat_free((char *) mutex);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
1997-09-02 05:55:01 -07:00
|
|
|
static void caml_io_mutex_lock(struct channel *chan)
|
1997-08-29 08:37:22 -07:00
|
|
|
{
|
|
|
|
if (chan->mutex == NULL) {
|
|
|
|
pthread_mutex_t * mutex =
|
|
|
|
(pthread_mutex_t *) stat_alloc(sizeof(pthread_mutex_t));
|
|
|
|
pthread_mutex_init(mutex, NULL);
|
|
|
|
chan->mutex = (void *) mutex;
|
|
|
|
}
|
|
|
|
enter_blocking_section();
|
|
|
|
pthread_mutex_lock(chan->mutex);
|
|
|
|
leave_blocking_section();
|
|
|
|
pthread_setspecific(last_channel_locked_key, (void *) chan);
|
|
|
|
}
|
|
|
|
|
1997-09-02 05:55:01 -07:00
|
|
|
static void caml_io_mutex_unlock(struct channel *chan)
|
1997-08-29 08:37:22 -07:00
|
|
|
{
|
|
|
|
pthread_mutex_unlock(chan->mutex);
|
|
|
|
pthread_setspecific(last_channel_locked_key, NULL);
|
|
|
|
}
|
|
|
|
|
1997-09-02 05:55:01 -07:00
|
|
|
static void caml_io_mutex_unlock_exn(void)
|
1997-08-29 08:37:22 -07:00
|
|
|
{
|
|
|
|
struct channel * chan = pthread_getspecific(last_channel_locked_key);
|
|
|
|
if (chan != NULL) caml_io_mutex_unlock(chan);
|
|
|
|
}
|
|
|
|
|
1996-09-09 05:25:20 -07:00
|
|
|
/* The "tick" thread fakes a SIGVTALRM signal at regular intervals. */
|
|
|
|
|
1997-09-02 05:55:01 -07:00
|
|
|
static void * caml_thread_tick(void * arg)
|
1996-09-09 05:25:20 -07:00
|
|
|
{
|
|
|
|
struct timeval timeout;
|
|
|
|
while(1) {
|
|
|
|
/* select() seems to be the most efficient way to suspend the
|
|
|
|
thread for sub-second intervals */
|
|
|
|
timeout.tv_sec = 0;
|
|
|
|
timeout.tv_usec = Thread_timeout;
|
|
|
|
select(0, NULL, NULL, NULL, &timeout);
|
|
|
|
/* This signal should never cause a callback, so don't go through
|
|
|
|
handle_signal(), tweak the global variables directly. */
|
|
|
|
pending_signal = SIGVTALRM;
|
1997-08-29 08:37:22 -07:00
|
|
|
#ifdef NATIVE_CODE
|
|
|
|
young_limit = young_end;
|
|
|
|
#else
|
1996-09-09 05:25:20 -07:00
|
|
|
something_to_do = 1;
|
1997-08-29 08:37:22 -07:00
|
|
|
#endif
|
1996-09-09 05:25:20 -07:00
|
|
|
}
|
1997-11-25 06:32:36 -08:00
|
|
|
return NULL; /* prevents compiler warning */
|
1996-09-09 05:25:20 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Initialize the thread machinery */
|
|
|
|
|
1997-09-02 05:55:01 -07:00
|
|
|
value caml_thread_initialize(value unit) /* ML */
|
1996-09-09 05:25:20 -07:00
|
|
|
{
|
|
|
|
pthread_t tick_pthread;
|
|
|
|
pthread_attr_t attr;
|
1997-05-26 10:16:31 -07:00
|
|
|
value mu = Val_unit;
|
1997-08-29 08:37:22 -07:00
|
|
|
value descr;
|
1997-05-26 10:16:31 -07:00
|
|
|
|
|
|
|
Begin_root (mu);
|
|
|
|
/* Initialize the main mutex */
|
|
|
|
caml_pthread_check(pthread_mutex_init(&caml_mutex, NULL),
|
|
|
|
"Thread.init");
|
|
|
|
pthread_mutex_lock(&caml_mutex);
|
1997-08-29 08:37:22 -07:00
|
|
|
/* Initialize the keys */
|
1997-05-26 10:16:31 -07:00
|
|
|
pthread_key_create(&thread_descriptor_key, NULL);
|
1997-08-29 08:37:22 -07:00
|
|
|
pthread_key_create(&last_channel_locked_key, NULL);
|
1997-05-26 10:16:31 -07:00
|
|
|
/* Create and acquire a termination lock for the current thread */
|
|
|
|
mu = caml_mutex_new(Val_unit);
|
|
|
|
caml_mutex_lock(mu);
|
|
|
|
/* Create a descriptor for the current thread */
|
1997-08-29 08:37:22 -07:00
|
|
|
descr = alloc_tuple(3);
|
|
|
|
Ident(descr) = Val_long(thread_next_ident);
|
|
|
|
Start_closure(descr) = Val_unit;
|
|
|
|
Terminated(descr) = mu;
|
1997-05-26 10:16:31 -07:00
|
|
|
thread_next_ident++;
|
1997-08-29 08:37:22 -07:00
|
|
|
/* Create an info block for the current thread */
|
|
|
|
curr_thread =
|
|
|
|
(caml_thread_t) stat_alloc(sizeof(struct caml_thread_struct));
|
1997-09-01 07:26:16 -07:00
|
|
|
curr_thread->pthread = pthread_self();
|
1997-08-29 08:37:22 -07:00
|
|
|
curr_thread->descr = descr;
|
|
|
|
curr_thread->next = curr_thread;
|
|
|
|
curr_thread->prev = curr_thread;
|
1997-05-26 10:16:31 -07:00
|
|
|
/* The stack-related fields will be filled in at the next
|
|
|
|
enter_blocking_section */
|
|
|
|
/* Associate the thread descriptor with the thread */
|
1997-08-29 08:37:22 -07:00
|
|
|
pthread_setspecific(thread_descriptor_key, (void *) curr_thread);
|
1997-05-26 10:16:31 -07:00
|
|
|
/* 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;
|
1997-08-29 08:37:22 -07:00
|
|
|
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;
|
1997-05-26 10:16:31 -07:00
|
|
|
/* Fork the tick thread */
|
|
|
|
pthread_attr_init(&attr);
|
|
|
|
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
|
|
|
|
caml_pthread_check(
|
|
|
|
pthread_create(&tick_pthread, &attr, caml_thread_tick, NULL),
|
|
|
|
"Thread.init");
|
|
|
|
pthread_detach(tick_pthread);
|
|
|
|
End_roots();
|
1996-09-09 05:25:20 -07:00
|
|
|
return Val_unit;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Create a thread */
|
|
|
|
|
1997-09-02 05:55:01 -07:00
|
|
|
static void * caml_thread_start(void * arg)
|
1996-09-09 05:25:20 -07:00
|
|
|
{
|
1997-09-02 05:55:01 -07:00
|
|
|
caml_thread_t th = (caml_thread_t) arg;
|
1996-09-09 05:25:20 -07:00
|
|
|
value clos;
|
1997-08-29 08:37:22 -07:00
|
|
|
|
1996-09-09 05:25:20 -07:00
|
|
|
/* Associate the thread descriptor with the thread */
|
|
|
|
pthread_setspecific(thread_descriptor_key, (void *) th);
|
|
|
|
/* Acquire the global mutex and set up the stack variables */
|
|
|
|
leave_blocking_section();
|
|
|
|
/* Callback the closure */
|
1997-08-29 08:37:22 -07:00
|
|
|
clos = Start_closure(th->descr);
|
|
|
|
Modify(&(Start_closure(th->descr)), Val_unit);
|
1996-09-09 05:25:20 -07:00
|
|
|
callback(clos, Val_unit);
|
1997-11-25 07:21:57 -08:00
|
|
|
/* Signal that the thread has terminated */
|
|
|
|
caml_mutex_unlock(Terminated(th->descr));
|
|
|
|
/* Remove th from the doubly-linked list of threads */
|
|
|
|
th->next->prev = th->prev;
|
|
|
|
th->prev->next = th->next;
|
|
|
|
#ifndef NATIVE_CODE
|
|
|
|
/* Free the memory resources */
|
|
|
|
stat_free(th->stack_low);
|
|
|
|
#endif
|
|
|
|
/* Free the thread descriptor */
|
|
|
|
stat_free(th);
|
|
|
|
/* Release the main mutex (forever) */
|
|
|
|
enter_blocking_section();
|
|
|
|
/* The thread now stops running */
|
1997-11-25 07:10:46 -08:00
|
|
|
return NULL;
|
1996-09-09 05:25:20 -07:00
|
|
|
}
|
|
|
|
|
1997-09-02 05:55:01 -07:00
|
|
|
value caml_thread_new(value clos) /* ML */
|
1996-09-09 05:25:20 -07:00
|
|
|
{
|
|
|
|
pthread_attr_t attr;
|
1997-08-29 08:37:22 -07:00
|
|
|
caml_thread_t th;
|
1997-05-26 10:16:31 -07:00
|
|
|
value mu = Val_unit;
|
1997-08-29 08:37:22 -07:00
|
|
|
value descr;
|
1997-11-20 06:15:35 -08:00
|
|
|
int err;
|
1997-05-26 10:16:31 -07:00
|
|
|
|
1997-08-29 08:37:22 -07:00
|
|
|
Begin_roots2 (clos, mu)
|
1997-05-26 10:16:31 -07:00
|
|
|
/* Create and acquire the termination lock */
|
|
|
|
mu = caml_mutex_new(Val_unit);
|
|
|
|
caml_mutex_lock(mu);
|
1997-08-29 08:37:22 -07:00
|
|
|
/* Create a descriptor for the new thread */
|
|
|
|
descr = alloc_tuple(3);
|
|
|
|
Ident(descr) = Val_long(thread_next_ident);
|
|
|
|
Start_closure(descr) = clos;
|
|
|
|
Terminated(descr) = mu;
|
1997-05-26 10:16:31 -07:00
|
|
|
thread_next_ident++;
|
1997-08-29 08:37:22 -07:00
|
|
|
/* 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
|
1997-11-27 08:28:40 -08:00
|
|
|
th->bottom_of_stack = NULL;
|
1997-11-21 05:46:23 -08:00
|
|
|
th->exception_pointer = NULL;
|
1997-08-29 08:37:22 -07:00
|
|
|
th->local_roots = NULL;
|
|
|
|
#else
|
|
|
|
/* Allocate the stacks */
|
1997-05-26 10:16:31 -07:00
|
|
|
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;
|
1997-08-29 08:37:22 -07:00
|
|
|
#endif
|
|
|
|
/* Add thread info block to the list of threads */
|
1997-05-26 10:16:31 -07:00
|
|
|
th->next = curr_thread->next;
|
|
|
|
th->prev = curr_thread;
|
1997-08-29 08:37:22 -07:00
|
|
|
curr_thread->next->prev = th;
|
|
|
|
curr_thread->next = th;
|
1997-05-26 10:16:31 -07:00
|
|
|
/* Fork the new thread */
|
|
|
|
pthread_attr_init(&attr);
|
|
|
|
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
|
1997-11-20 06:15:35 -08:00
|
|
|
err = pthread_create(&th->pthread, &attr, caml_thread_start, (void *) th);
|
|
|
|
if (err != 0) {
|
|
|
|
/* 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_pthread_check(err, "Thread.create");
|
|
|
|
}
|
1997-05-26 10:16:31 -07:00
|
|
|
End_roots();
|
1997-08-29 08:37:22 -07:00
|
|
|
return descr;
|
1996-09-09 05:25:20 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Return the current thread */
|
|
|
|
|
1997-09-02 05:55:01 -07:00
|
|
|
value caml_thread_self(value unit) /* ML */
|
1996-09-09 05:25:20 -07:00
|
|
|
{
|
1996-10-17 02:58:30 -07:00
|
|
|
if (curr_thread == NULL) invalid_argument("Thread.self: not initialized");
|
1997-08-29 08:37:22 -07:00
|
|
|
return curr_thread->descr;
|
1996-09-09 05:25:20 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Return the identifier of a thread */
|
|
|
|
|
1997-09-02 05:55:01 -07:00
|
|
|
value caml_thread_id(value th) /* ML */
|
1996-09-09 05:25:20 -07:00
|
|
|
{
|
1997-08-29 08:37:22 -07:00
|
|
|
return Ident(th);
|
1996-09-09 05:25:20 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Allow re-scheduling */
|
|
|
|
|
1997-09-02 05:55:01 -07:00
|
|
|
value caml_thread_yield(value unit) /* ML */
|
1996-09-09 05:25:20 -07:00
|
|
|
{
|
|
|
|
enter_blocking_section();
|
|
|
|
sched_yield();
|
|
|
|
leave_blocking_section();
|
|
|
|
return Val_unit;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Suspend the current thread until another thread terminates */
|
|
|
|
|
1997-09-02 05:55:01 -07:00
|
|
|
value caml_thread_join(value th) /* ML */
|
1996-09-09 05:25:20 -07:00
|
|
|
{
|
1998-02-26 04:52:39 -08:00
|
|
|
value mut = Terminated(th);
|
|
|
|
Begin_root(mut)
|
|
|
|
caml_mutex_lock(mut);
|
|
|
|
caml_mutex_unlock(mut);
|
1997-12-19 08:04:40 -08:00
|
|
|
End_roots();
|
1996-09-09 05:25:20 -07:00
|
|
|
return Val_unit;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Mutex operations */
|
|
|
|
|
1998-02-26 04:52:39 -08:00
|
|
|
#define Mutex_val(v) ((pthread_mutex_t *) Field(v, 1))
|
1996-09-09 05:25:20 -07:00
|
|
|
#define Max_mutex_number 1000
|
|
|
|
|
1998-02-26 04:52:39 -08:00
|
|
|
static void caml_mutex_finalize(value wrapper)
|
1996-09-09 05:25:20 -07:00
|
|
|
{
|
1998-02-26 04:52:39 -08:00
|
|
|
pthread_mutex_t * mut = Mutex_val(wrapper);
|
|
|
|
pthread_mutex_destroy(mut);
|
|
|
|
stat_free(mut);
|
1996-09-09 05:25:20 -07:00
|
|
|
}
|
|
|
|
|
1997-09-02 05:55:01 -07:00
|
|
|
value caml_mutex_new(value unit) /* ML */
|
1996-09-09 05:25:20 -07:00
|
|
|
{
|
1998-02-26 04:52:39 -08:00
|
|
|
pthread_mutex_t * mut;
|
|
|
|
value wrapper;
|
|
|
|
mut = stat_alloc(sizeof(pthread_mutex_t));
|
|
|
|
caml_pthread_check(pthread_mutex_init(mut, NULL), "Mutex.create");
|
|
|
|
wrapper = alloc_final(2, caml_mutex_finalize, 1, Max_mutex_number);
|
|
|
|
Mutex_val(wrapper) = mut;
|
|
|
|
return wrapper;
|
1996-09-09 05:25:20 -07:00
|
|
|
}
|
|
|
|
|
1998-02-26 04:52:39 -08:00
|
|
|
value caml_mutex_lock(value wrapper) /* ML */
|
1996-09-09 05:25:20 -07:00
|
|
|
{
|
|
|
|
int retcode;
|
1998-02-26 04:52:39 -08:00
|
|
|
pthread_mutex_t * mut = Mutex_val(wrapper);
|
1996-09-09 05:25:20 -07:00
|
|
|
enter_blocking_section();
|
1998-02-26 04:52:39 -08:00
|
|
|
retcode = pthread_mutex_lock(mut);
|
1996-09-09 05:25:20 -07:00
|
|
|
leave_blocking_section();
|
1997-05-14 02:43:45 -07:00
|
|
|
caml_pthread_check(retcode, "Mutex.lock");
|
1996-09-09 05:25:20 -07:00
|
|
|
return Val_unit;
|
|
|
|
}
|
|
|
|
|
1998-02-26 04:52:39 -08:00
|
|
|
value caml_mutex_unlock(value wrapper) /* ML */
|
1996-09-09 05:25:20 -07:00
|
|
|
{
|
|
|
|
int retcode;
|
1998-02-26 04:52:39 -08:00
|
|
|
pthread_mutex_t * mut = Mutex_val(wrapper);
|
1996-09-09 05:25:20 -07:00
|
|
|
enter_blocking_section();
|
1998-02-26 04:52:39 -08:00
|
|
|
retcode = pthread_mutex_unlock(mut);
|
1996-09-09 05:25:20 -07:00
|
|
|
leave_blocking_section();
|
1997-05-14 02:43:45 -07:00
|
|
|
caml_pthread_check(retcode, "Mutex.unlock");
|
1996-09-09 05:25:20 -07:00
|
|
|
return Val_unit;
|
|
|
|
}
|
|
|
|
|
1998-02-26 04:52:39 -08:00
|
|
|
value caml_mutex_try_lock(value wrapper) /* ML */
|
1996-09-09 05:25:20 -07:00
|
|
|
{
|
|
|
|
int retcode;
|
1998-02-26 04:52:39 -08:00
|
|
|
pthread_mutex_t * mut = Mutex_val(wrapper);
|
|
|
|
retcode = pthread_mutex_trylock(mut);
|
1997-08-29 08:37:22 -07:00
|
|
|
if (retcode == EBUSY) return Val_false;
|
|
|
|
caml_pthread_check(retcode, "Mutex.try_lock");
|
|
|
|
return Val_true;
|
1996-09-09 05:25:20 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Conditions operations */
|
|
|
|
|
1998-02-26 04:52:39 -08:00
|
|
|
#define Condition_val(v) ((pthread_cond_t *) Field(v, 1))
|
1996-09-09 05:25:20 -07:00
|
|
|
#define Max_condition_number 1000
|
|
|
|
|
1998-02-26 04:52:39 -08:00
|
|
|
static void caml_condition_finalize(value wrapper)
|
1996-09-09 05:25:20 -07:00
|
|
|
{
|
1998-02-26 04:52:39 -08:00
|
|
|
pthread_cond_t * cond = Condition_val(wrapper);
|
|
|
|
pthread_cond_destroy(cond);
|
|
|
|
stat_free(cond);
|
1996-09-09 05:25:20 -07:00
|
|
|
}
|
|
|
|
|
1997-09-02 05:55:01 -07:00
|
|
|
value caml_condition_new(value unit) /* ML */
|
1996-09-09 05:25:20 -07:00
|
|
|
{
|
1998-02-26 04:52:39 -08:00
|
|
|
pthread_cond_t * cond;
|
|
|
|
value wrapper;
|
|
|
|
cond = stat_alloc(sizeof(pthread_cond_t));
|
|
|
|
caml_pthread_check(pthread_cond_init(cond, NULL), "Condition.create");
|
|
|
|
wrapper = alloc_final(2, caml_condition_finalize, 1, Max_condition_number);
|
|
|
|
Condition_val(wrapper) = cond;
|
|
|
|
return wrapper;
|
1996-09-09 05:25:20 -07:00
|
|
|
}
|
|
|
|
|
1998-02-26 04:52:39 -08:00
|
|
|
value caml_condition_wait(value wcond, value wmut) /* ML */
|
1996-09-09 05:25:20 -07:00
|
|
|
{
|
|
|
|
int retcode;
|
1998-02-26 04:52:39 -08:00
|
|
|
pthread_cond_t * cond = Condition_val(wcond);
|
|
|
|
pthread_mutex_t * mut = Mutex_val(wmut);
|
1996-09-09 05:25:20 -07:00
|
|
|
enter_blocking_section();
|
1998-02-26 04:52:39 -08:00
|
|
|
retcode = pthread_cond_wait(cond, mut);
|
1996-09-09 05:25:20 -07:00
|
|
|
leave_blocking_section();
|
1997-05-14 02:43:45 -07:00
|
|
|
caml_pthread_check(retcode, "Condition.wait");
|
1996-09-09 05:25:20 -07:00
|
|
|
return Val_unit;
|
|
|
|
}
|
|
|
|
|
1998-02-26 04:52:39 -08:00
|
|
|
value caml_condition_signal(value wrapper) /* ML */
|
1996-09-09 05:25:20 -07:00
|
|
|
{
|
|
|
|
int retcode;
|
1998-02-26 04:52:39 -08:00
|
|
|
pthread_cond_t * cond = Condition_val(wrapper);
|
1996-09-09 05:25:20 -07:00
|
|
|
enter_blocking_section();
|
1998-02-26 04:52:39 -08:00
|
|
|
retcode = pthread_cond_signal(cond);
|
1996-09-09 05:25:20 -07:00
|
|
|
leave_blocking_section();
|
1997-05-14 02:43:45 -07:00
|
|
|
caml_pthread_check(retcode, "Condition.signal");
|
1996-09-09 05:25:20 -07:00
|
|
|
return Val_unit;
|
|
|
|
}
|
|
|
|
|
1998-02-26 04:52:39 -08:00
|
|
|
value caml_condition_broadcast(value wrapper) /* ML */
|
1996-09-09 05:25:20 -07:00
|
|
|
{
|
|
|
|
int retcode;
|
1998-02-26 04:52:39 -08:00
|
|
|
pthread_cond_t * cond = Condition_val(wrapper);
|
1996-09-09 05:25:20 -07:00
|
|
|
enter_blocking_section();
|
1998-02-26 04:52:39 -08:00
|
|
|
retcode = pthread_cond_broadcast(cond);
|
1996-09-09 05:25:20 -07:00
|
|
|
leave_blocking_section();
|
1997-05-14 02:43:45 -07:00
|
|
|
caml_pthread_check(retcode, "Condition.broadcast");
|
1996-09-09 05:25:20 -07:00
|
|
|
return Val_unit;
|
|
|
|
}
|
|
|
|
|
1997-05-14 02:43:45 -07:00
|
|
|
/* Error report */
|
|
|
|
|
1997-09-02 05:55:01 -07:00
|
|
|
static void caml_pthread_check(int retcode, char *msg)
|
1997-05-14 02:43:45 -07:00
|
|
|
{
|
|
|
|
char * err;
|
|
|
|
int errlen, msglen;
|
|
|
|
value str;
|
|
|
|
|
|
|
|
if (retcode == 0) return;
|
|
|
|
err = strerror(retcode);
|
|
|
|
msglen = strlen(msg);
|
|
|
|
errlen = strlen(err);
|
|
|
|
str = alloc_string(msglen + 2 + errlen);
|
|
|
|
bcopy(msg, &Byte(str, 0), msglen);
|
|
|
|
bcopy(": ", &Byte(str, msglen), 2);
|
|
|
|
bcopy(err, &Byte(str, msglen + 2), errlen);
|
|
|
|
raise_sys_error(str);
|
|
|
|
}
|