1996-04-30 07:53:58 -07:00
|
|
|
/***********************************************************************/
|
|
|
|
/* */
|
|
|
|
/* Objective Caml */
|
|
|
|
/* */
|
|
|
|
/* Xavier Leroy, projet Cristal, INRIA Rocquencourt */
|
|
|
|
/* */
|
|
|
|
/* Copyright 1996 Institut National de Recherche en Informatique et */
|
|
|
|
/* Automatique. Distributed only by permission. */
|
|
|
|
/* */
|
|
|
|
/***********************************************************************/
|
|
|
|
|
|
|
|
/* $Id$ */
|
|
|
|
|
|
|
|
/* The thread scheduler */
|
|
|
|
|
1996-11-08 06:46:52 -08:00
|
|
|
#include "callback.h"
|
1996-04-03 02:02:34 -08:00
|
|
|
#include "config.h"
|
|
|
|
#include "misc.h"
|
|
|
|
#include "mlvalues.h"
|
|
|
|
#include "stacks.h"
|
|
|
|
#include "fail.h"
|
|
|
|
#include "io.h"
|
|
|
|
#include "roots.h"
|
|
|
|
#include "alloc.h"
|
|
|
|
#include "memory.h"
|
1996-06-11 02:41:25 -07:00
|
|
|
#include "signals.h"
|
1996-04-03 02:02:34 -08:00
|
|
|
|
1996-06-11 02:41:25 -07:00
|
|
|
#if ! (defined(HAS_SELECT) && \
|
|
|
|
defined(HAS_SETITIMER) && \
|
1996-06-25 02:54:46 -07:00
|
|
|
defined(HAS_GETTIMEOFDAY) && \
|
|
|
|
(defined(HAS_WAITPID) || defined(HAS_WAIT4)))
|
1996-04-03 02:02:34 -08:00
|
|
|
#include "Cannot compile libthreads, system calls missing"
|
|
|
|
#endif
|
|
|
|
|
1997-12-09 01:10:53 -08:00
|
|
|
#ifdef __OpenBSD__
|
|
|
|
#include <string.h>
|
|
|
|
#endif
|
|
|
|
|
1996-04-03 02:02:34 -08:00
|
|
|
#include <sys/time.h>
|
|
|
|
#include <sys/types.h>
|
1996-04-18 09:32:04 -07:00
|
|
|
#include <sys/wait.h>
|
|
|
|
#include <fcntl.h>
|
|
|
|
#ifdef HAS_UNISTD
|
1996-04-03 02:02:34 -08:00
|
|
|
#include <unistd.h>
|
1996-04-18 09:32:04 -07:00
|
|
|
#endif
|
1996-04-03 02:02:34 -08:00
|
|
|
#ifdef HAS_SYS_SELECT_H
|
|
|
|
#include <sys/select.h>
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifndef FD_ISSET
|
1997-04-11 06:57:34 -07:00
|
|
|
/* Assume old-style BSD 4.2 fd sets */
|
1996-04-03 02:02:34 -08:00
|
|
|
typedef int fd_set;
|
|
|
|
#define FD_SETSIZE (sizeof(int) * 8)
|
|
|
|
#define FD_SET(fd,fds) (*(fds) |= 1 << (fd))
|
|
|
|
#define FD_CLR(fd,fds) (*(fds) &= ~(1 << (fd)))
|
|
|
|
#define FD_ISSET(fd,fds) (*(fds) & (1 << (fd)))
|
|
|
|
#define FD_ZERO(fds) (*(fds) = 0)
|
|
|
|
#endif
|
|
|
|
|
1996-06-25 02:54:46 -07:00
|
|
|
#ifndef HAS_WAITPID
|
|
|
|
#define waitpid(pid,status,opts) wait4(pid,status,opts,NULL)
|
|
|
|
#endif
|
|
|
|
|
1996-04-03 02:02:34 -08:00
|
|
|
/* Configuration */
|
|
|
|
|
|
|
|
/* 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
|
|
|
|
|
|
|
|
/* The thread descriptors */
|
|
|
|
|
|
|
|
struct thread_struct {
|
|
|
|
value ident; /* Unique id (for equality comparisons) */
|
|
|
|
struct thread_struct * next; /* Double linking of threads */
|
|
|
|
struct thread_struct * prev;
|
|
|
|
value * stack_low; /* The execution stack for this thread */
|
|
|
|
value * stack_high;
|
|
|
|
value * stack_threshold;
|
|
|
|
value * sp;
|
|
|
|
value * trapsp;
|
|
|
|
value status; /* RUNNABLE, KILLED. etc (see below) */
|
1997-04-11 06:57:34 -07:00
|
|
|
value readfds, writefds, exceptfds;
|
|
|
|
/* Lists of file descriptors on which we're doing select() */
|
1996-04-03 02:02:34 -08:00
|
|
|
value delay; /* Time until which this thread is blocked */
|
|
|
|
value joining; /* Thread we're trying to join */
|
1996-04-18 09:32:04 -07:00
|
|
|
value waitpid; /* PID of process we're waiting for */
|
|
|
|
value retval; /* Value to return when thread resumes */
|
1996-04-03 02:02:34 -08:00
|
|
|
};
|
|
|
|
|
|
|
|
typedef struct thread_struct * thread_t;
|
|
|
|
|
|
|
|
#define RUNNABLE Val_int(0)
|
|
|
|
#define KILLED Val_int(1)
|
|
|
|
#define SUSPENDED Val_int(2)
|
1997-04-11 06:57:34 -07:00
|
|
|
#define BLOCKED_IO Val_int(4)
|
|
|
|
#define BLOCKED_DELAY Val_int(8)
|
|
|
|
#define BLOCKED_JOIN Val_int(16)
|
|
|
|
#define BLOCKED_WAIT Val_int(32)
|
1996-04-18 09:32:04 -07:00
|
|
|
|
|
|
|
#define RESUMED_WAKEUP Val_int(0)
|
1997-04-11 06:57:34 -07:00
|
|
|
#define RESUMED_DELAY Val_int(1)
|
|
|
|
#define RESUMED_JOIN Val_int(2)
|
1996-04-18 09:32:04 -07:00
|
|
|
|
1997-04-11 06:57:34 -07:00
|
|
|
#define TAG_RESUMED_IO 0
|
|
|
|
#define TAG_RESUMED_WAIT 1
|
|
|
|
|
|
|
|
#define NO_FDS Val_unit
|
1996-04-03 02:02:34 -08:00
|
|
|
#define NO_DELAY Val_unit
|
|
|
|
#define NO_JOINING Val_unit
|
1996-04-18 09:32:04 -07:00
|
|
|
#define NO_WAITPID Val_int(0)
|
1996-04-03 02:02:34 -08:00
|
|
|
|
|
|
|
#define DELAY_INFTY 1E30 /* +infty, for this purpose */
|
|
|
|
|
|
|
|
/* The thread currently active */
|
|
|
|
static thread_t curr_thread = NULL;
|
|
|
|
/* Identifier for next thread creation */
|
|
|
|
static value next_ident = Val_int(0);
|
|
|
|
|
|
|
|
#define Assign(dst,src) modify((value *)&(dst), (value)(src))
|
|
|
|
|
|
|
|
/* Scan the stacks of the other threads */
|
|
|
|
|
1997-09-02 05:55:01 -07:00
|
|
|
static void (*prev_scan_roots_hook) (scanning_action);
|
1996-04-03 02:02:34 -08:00
|
|
|
|
1997-09-02 05:55:01 -07:00
|
|
|
static void thread_scan_roots(scanning_action action)
|
1996-04-03 02:02:34 -08:00
|
|
|
{
|
|
|
|
thread_t th;
|
1997-08-29 08:05:51 -07:00
|
|
|
|
1996-04-03 02:02:34 -08:00
|
|
|
/* Scan all active descriptors */
|
|
|
|
(*action)((value) curr_thread, (value *) &curr_thread);
|
1997-08-29 08:05:51 -07:00
|
|
|
/* Don't scan curr_thread->sp, this has already been done.
|
|
|
|
Don't scan local roots either, for the same reason. */
|
1996-04-03 02:02:34 -08:00
|
|
|
for (th = curr_thread->next; th != curr_thread; th = th->next) {
|
|
|
|
(*action)((value) th, (value *) &th);
|
1997-08-29 08:05:51 -07:00
|
|
|
do_local_roots(action, th->sp, th->stack_high, NULL);
|
1996-04-03 02:02:34 -08:00
|
|
|
}
|
|
|
|
/* Hook */
|
|
|
|
if (prev_scan_roots_hook != NULL) (*prev_scan_roots_hook)(action);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Initialize the thread machinery */
|
|
|
|
|
1997-09-02 05:55:01 -07:00
|
|
|
value thread_initialize(value unit) /* ML */
|
1996-04-03 02:02:34 -08:00
|
|
|
{
|
|
|
|
struct itimerval timer;
|
|
|
|
/* Create a descriptor for the current thread */
|
|
|
|
curr_thread =
|
|
|
|
(thread_t) alloc_shr(sizeof(struct thread_struct) / sizeof(value), 0);
|
|
|
|
curr_thread->ident = next_ident;
|
|
|
|
next_ident = Val_int(Int_val(next_ident) + 1);
|
|
|
|
curr_thread->next = curr_thread;
|
|
|
|
curr_thread->prev = curr_thread;
|
|
|
|
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->status = RUNNABLE;
|
1997-04-11 06:57:34 -07:00
|
|
|
curr_thread->readfds = NO_FDS;
|
|
|
|
curr_thread->writefds = NO_FDS;
|
|
|
|
curr_thread->exceptfds = NO_FDS;
|
1996-04-03 02:02:34 -08:00
|
|
|
curr_thread->delay = NO_DELAY;
|
|
|
|
curr_thread->joining = NO_JOINING;
|
1996-04-18 09:32:04 -07:00
|
|
|
curr_thread->waitpid = NO_WAITPID;
|
|
|
|
curr_thread->retval = Val_unit;
|
1996-04-03 02:02:34 -08:00
|
|
|
/* Initialize GC */
|
|
|
|
prev_scan_roots_hook = scan_roots_hook;
|
|
|
|
scan_roots_hook = thread_scan_roots;
|
|
|
|
/* Initialize interval timer */
|
|
|
|
timer.it_interval.tv_sec = 0;
|
|
|
|
timer.it_interval.tv_usec = Thread_timeout;
|
|
|
|
timer.it_value = timer.it_interval;
|
|
|
|
setitimer(ITIMER_VIRTUAL, &timer, NULL);
|
|
|
|
return Val_unit;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Create a thread */
|
|
|
|
|
1997-09-02 05:55:01 -07:00
|
|
|
value thread_new(value clos) /* ML */
|
1996-04-03 02:02:34 -08:00
|
|
|
{
|
|
|
|
thread_t th;
|
|
|
|
/* Allocate the thread and its stack */
|
1997-05-26 10:16:31 -07:00
|
|
|
Begin_root(clos);
|
|
|
|
th = (thread_t) alloc_shr(sizeof(struct thread_struct) / sizeof(value), 0);
|
|
|
|
End_roots();
|
1996-04-03 02:02:34 -08:00
|
|
|
th->ident = next_ident;
|
|
|
|
next_ident = Val_int(Int_val(next_ident) + 1);
|
|
|
|
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;
|
|
|
|
/* Set up a return frame that pretends we're applying clos to ().
|
|
|
|
This way, when this thread is activated, the RETURN will take us
|
|
|
|
to the entry point of the closure. */
|
|
|
|
th->sp -= 4;
|
|
|
|
th->sp[0] = Val_unit;
|
|
|
|
th->sp[1] = (value) Code_val(clos);
|
|
|
|
th->sp[2] = clos;
|
|
|
|
th->sp[3] = Val_long(0); /* no extra args */
|
|
|
|
/* Fake a C call frame */
|
|
|
|
th->sp--;
|
|
|
|
th->sp[0] = Val_unit; /* a dummy environment */
|
|
|
|
/* The thread is initially runnable */
|
|
|
|
th->status = RUNNABLE;
|
1997-04-11 06:57:34 -07:00
|
|
|
th->readfds = NO_FDS;
|
|
|
|
th->writefds = NO_FDS;
|
|
|
|
th->exceptfds = NO_FDS;
|
1996-04-03 02:02:34 -08:00
|
|
|
th->delay = NO_DELAY;
|
|
|
|
th->joining = NO_JOINING;
|
1996-04-18 09:32:04 -07:00
|
|
|
th->waitpid = NO_WAITPID;
|
|
|
|
th->retval = Val_unit;
|
1996-04-03 02:02:34 -08:00
|
|
|
/* Insert thread in doubly linked list of threads */
|
|
|
|
th->prev = curr_thread->prev;
|
|
|
|
th->next = curr_thread;
|
|
|
|
Assign(curr_thread->prev->next, th);
|
|
|
|
Assign(curr_thread->prev, th);
|
|
|
|
/* Return thread */
|
|
|
|
return (value) th;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Return the thread identifier */
|
|
|
|
|
1997-09-02 05:55:01 -07:00
|
|
|
value thread_id(value th) /* ML */
|
1996-04-03 02:02:34 -08:00
|
|
|
{
|
|
|
|
return ((struct thread_struct *)th)->ident;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Return the current time as a floating-point number */
|
|
|
|
|
1997-09-02 05:55:01 -07:00
|
|
|
static double timeofday(void)
|
1996-04-03 02:02:34 -08:00
|
|
|
{
|
|
|
|
struct timeval tv;
|
|
|
|
gettimeofday(&tv, NULL);
|
|
|
|
return (double) tv.tv_sec + (double) tv.tv_usec * 1e-6;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Find a runnable thread and activate it */
|
|
|
|
|
|
|
|
#define FOREACH_THREAD(x) x = curr_thread; do { x = x->next;
|
|
|
|
#define END_FOREACH(x) } while (x != curr_thread)
|
1997-04-11 06:57:34 -07:00
|
|
|
|
1997-09-02 05:55:01 -07:00
|
|
|
static value alloc_process_status(int pid, int status);
|
|
|
|
static void add_fdlist_to_set(value fdl, fd_set *set);
|
|
|
|
static value inter_fdlist_set(value fdl, fd_set *set);
|
1996-04-03 02:02:34 -08:00
|
|
|
|
1997-09-02 05:55:01 -07:00
|
|
|
static value schedule_thread(void)
|
1996-04-03 02:02:34 -08:00
|
|
|
{
|
|
|
|
thread_t run_thread, th;
|
1997-04-11 06:57:34 -07:00
|
|
|
fd_set readfds, writefds, exceptfds;
|
1996-04-03 02:02:34 -08:00
|
|
|
double delay, now;
|
1996-04-18 09:32:04 -07:00
|
|
|
int need_select, need_wait;
|
1996-04-03 02:02:34 -08:00
|
|
|
|
1996-05-28 07:50:38 -07:00
|
|
|
/* Don't allow preemption during a callback */
|
1996-06-11 02:41:25 -07:00
|
|
|
if (callback_depth > 0) return curr_thread->retval;
|
|
|
|
|
1996-04-03 02:02:34 -08:00
|
|
|
/* Save the status of the current thread */
|
|
|
|
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;
|
|
|
|
|
|
|
|
try_again:
|
|
|
|
/* Build fdsets and delay for select.
|
1996-04-18 09:32:04 -07:00
|
|
|
See if some join or wait operations succeeded. */
|
1996-04-03 02:02:34 -08:00
|
|
|
FD_ZERO(&readfds);
|
|
|
|
FD_ZERO(&writefds);
|
1997-04-11 06:57:34 -07:00
|
|
|
FD_ZERO(&exceptfds);
|
1996-04-03 02:02:34 -08:00
|
|
|
delay = DELAY_INFTY;
|
|
|
|
now = -1.0;
|
|
|
|
need_select = 0;
|
1996-04-18 09:32:04 -07:00
|
|
|
need_wait = 0;
|
1996-04-03 02:02:34 -08:00
|
|
|
|
|
|
|
FOREACH_THREAD(th)
|
1997-04-11 06:57:34 -07:00
|
|
|
if (th->status & (BLOCKED_IO - 1)) {
|
|
|
|
add_fdlist_to_set(th->readfds, &readfds);
|
|
|
|
add_fdlist_to_set(th->writefds, &writefds);
|
|
|
|
add_fdlist_to_set(th->exceptfds, &exceptfds);
|
1996-04-03 02:02:34 -08:00
|
|
|
need_select = 1;
|
1996-04-18 09:32:04 -07:00
|
|
|
}
|
1996-04-29 06:21:09 -07:00
|
|
|
if (th->status & (BLOCKED_DELAY - 1)) {
|
1996-06-10 02:15:58 -07:00
|
|
|
double th_delay;
|
1996-04-18 09:32:04 -07:00
|
|
|
if (now < 0.0) now = timeofday();
|
1996-06-10 02:15:58 -07:00
|
|
|
th_delay = Double_val(th->delay) - now;
|
|
|
|
if (th_delay <= 0) {
|
1996-04-18 09:32:04 -07:00
|
|
|
th->status = RUNNABLE;
|
|
|
|
th->retval = RESUMED_DELAY;
|
|
|
|
} else {
|
|
|
|
if (th_delay < delay) delay = th_delay;
|
1996-04-03 02:02:34 -08:00
|
|
|
}
|
1996-04-18 09:32:04 -07:00
|
|
|
}
|
1996-04-29 06:21:09 -07:00
|
|
|
if (th->status & (BLOCKED_JOIN - 1)) {
|
1996-04-03 02:02:34 -08:00
|
|
|
if (((thread_t)(th->joining))->status == KILLED) {
|
|
|
|
th->status = RUNNABLE;
|
1996-04-18 09:32:04 -07:00
|
|
|
th->retval = RESUMED_JOIN;
|
|
|
|
}
|
|
|
|
}
|
1996-04-29 06:21:09 -07:00
|
|
|
if (th->status & (BLOCKED_WAIT - 1)) {
|
|
|
|
int status, pid;
|
|
|
|
pid = waitpid(Int_val(th->waitpid), &status, WNOHANG);
|
|
|
|
if (pid > 0) {
|
1996-04-18 09:32:04 -07:00
|
|
|
th->status = RUNNABLE;
|
1996-04-29 06:21:09 -07:00
|
|
|
Assign(th->retval, alloc_process_status(pid, status));
|
1996-04-18 09:32:04 -07:00
|
|
|
} else {
|
|
|
|
need_wait = 1;
|
1996-04-03 02:02:34 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
END_FOREACH(th);
|
|
|
|
|
|
|
|
/* Find if a thread is runnable. */
|
|
|
|
run_thread = NULL;
|
|
|
|
FOREACH_THREAD(th)
|
|
|
|
if (th->status == RUNNABLE) { run_thread = th; break; }
|
|
|
|
END_FOREACH(th);
|
|
|
|
|
|
|
|
/* Do the select if needed */
|
|
|
|
if (need_select || run_thread == NULL) {
|
|
|
|
struct timeval delay_tv, * delay_ptr;
|
|
|
|
int retcode;
|
1996-06-10 02:15:58 -07:00
|
|
|
/* If a thread is blocked on wait, don't block forever */
|
|
|
|
if (need_wait && delay > Thread_timeout * 1e-6) {
|
|
|
|
delay = Thread_timeout * 1e-6;
|
|
|
|
}
|
1996-04-03 02:02:34 -08:00
|
|
|
/* Convert delay to a timeval */
|
|
|
|
/* If a thread is runnable, just poll */
|
|
|
|
if (run_thread != NULL) {
|
|
|
|
delay_tv.tv_sec = 0;
|
|
|
|
delay_tv.tv_usec = 0;
|
|
|
|
delay_ptr = &delay_tv;
|
|
|
|
}
|
1996-04-18 09:32:04 -07:00
|
|
|
else if (delay != DELAY_INFTY) {
|
1996-04-03 02:02:34 -08:00
|
|
|
delay_tv.tv_sec = (unsigned int) delay;
|
|
|
|
delay_tv.tv_usec = (delay - (double) delay_tv.tv_sec) * 1E6;
|
|
|
|
delay_ptr = &delay_tv;
|
|
|
|
}
|
1996-06-10 02:15:58 -07:00
|
|
|
else {
|
1996-04-18 09:32:04 -07:00
|
|
|
delay_ptr = NULL;
|
|
|
|
}
|
1996-06-11 02:41:25 -07:00
|
|
|
enter_blocking_section();
|
1997-04-11 06:57:34 -07:00
|
|
|
retcode = select(FD_SETSIZE, &readfds, &writefds, &exceptfds, delay_ptr);
|
1996-06-11 02:41:25 -07:00
|
|
|
leave_blocking_section();
|
1996-04-03 02:02:34 -08:00
|
|
|
if (retcode > 0) {
|
|
|
|
/* Some descriptors are ready.
|
|
|
|
Mark the corresponding threads runnable. */
|
|
|
|
FOREACH_THREAD(th)
|
1997-04-11 06:57:34 -07:00
|
|
|
if (th->status & (BLOCKED_IO - 1)) {
|
1997-05-26 10:16:31 -07:00
|
|
|
value r = Val_unit, w = Val_unit, e = Val_unit;
|
|
|
|
Begin_roots3(r,w,e);
|
|
|
|
r = inter_fdlist_set(th->readfds, &readfds);
|
|
|
|
w = inter_fdlist_set(th->writefds, &writefds);
|
|
|
|
e = inter_fdlist_set(th->exceptfds, &exceptfds);
|
|
|
|
if (r != NO_FDS || w != NO_FDS || e != NO_FDS) {
|
|
|
|
value retval = alloc(3, TAG_RESUMED_IO);
|
|
|
|
Field(retval, 0) = r;
|
|
|
|
Field(retval, 1) = w;
|
|
|
|
Field(retval, 2) = e;
|
|
|
|
Assign(th->retval, retval);
|
|
|
|
th->status = RUNNABLE;
|
|
|
|
if (run_thread == NULL) run_thread = th; /* Found one. */
|
|
|
|
}
|
|
|
|
End_roots();
|
1996-04-03 02:02:34 -08:00
|
|
|
}
|
|
|
|
END_FOREACH(th);
|
|
|
|
}
|
1996-06-11 02:41:25 -07:00
|
|
|
/* If we get here with run_thread still NULL, one of the following
|
|
|
|
may have happened:
|
|
|
|
- a delay has expired
|
|
|
|
- a wait() needs to be polled again
|
|
|
|
- the select() failed (e.g. was interrupted)
|
|
|
|
In these cases, we go through the loop once more to make the
|
1996-04-03 02:02:34 -08:00
|
|
|
corresponding threads runnable. */
|
1996-06-11 02:41:25 -07:00
|
|
|
if (run_thread == NULL &&
|
|
|
|
(delay != DELAY_INFTY || need_wait || retcode == -1))
|
1996-04-18 09:32:04 -07:00
|
|
|
goto try_again;
|
1996-04-03 02:02:34 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
/* If we haven't something to run at that point, we're in big trouble. */
|
|
|
|
if (run_thread == NULL) invalid_argument("Thread: deadlock");
|
|
|
|
|
1997-04-11 06:57:34 -07:00
|
|
|
/* Free everything the thread was waiting on */
|
|
|
|
Assign(run_thread->readfds, NO_FDS);
|
|
|
|
Assign(run_thread->writefds, NO_FDS);
|
|
|
|
Assign(run_thread->exceptfds, NO_FDS);
|
|
|
|
Assign(run_thread->delay, NO_DELAY);
|
|
|
|
Assign(run_thread->joining, NO_JOINING);
|
|
|
|
run_thread->waitpid = NO_WAITPID;
|
|
|
|
|
1996-04-03 02:02:34 -08:00
|
|
|
/* Activate the thread */
|
|
|
|
curr_thread = run_thread;
|
|
|
|
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;
|
1996-04-18 09:32:04 -07:00
|
|
|
return curr_thread->retval;
|
1996-04-03 02:02:34 -08:00
|
|
|
}
|
|
|
|
|
1996-06-11 02:41:25 -07:00
|
|
|
/* Since context switching is not allowed in callbacks, a thread that
|
|
|
|
blocks during a callback is a deadlock. */
|
|
|
|
|
1997-09-02 05:55:01 -07:00
|
|
|
static void check_callback(void)
|
1996-06-11 02:41:25 -07:00
|
|
|
{
|
|
|
|
if (callback_depth > 0)
|
|
|
|
fatal_error("Thread: deadlock during callback");
|
|
|
|
}
|
|
|
|
|
1996-04-03 02:02:34 -08:00
|
|
|
/* Reschedule without suspending the current thread */
|
|
|
|
|
1997-09-02 05:55:01 -07:00
|
|
|
value thread_yield(value unit) /* ML */
|
1996-04-03 02:02:34 -08:00
|
|
|
{
|
1996-10-17 02:59:18 -07:00
|
|
|
Assert(curr_thread != NULL);
|
1996-04-18 09:32:04 -07:00
|
|
|
curr_thread->retval = Val_unit;
|
|
|
|
return schedule_thread();
|
1996-04-03 02:02:34 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Suspend the current thread */
|
|
|
|
|
1997-09-02 05:55:01 -07:00
|
|
|
value thread_sleep(value unit) /* ML */
|
1996-04-03 02:02:34 -08:00
|
|
|
{
|
1996-10-17 02:59:18 -07:00
|
|
|
Assert(curr_thread != NULL);
|
1996-06-11 02:41:25 -07:00
|
|
|
check_callback();
|
1996-04-03 02:02:34 -08:00
|
|
|
curr_thread->status = SUSPENDED;
|
1996-04-18 09:32:04 -07:00
|
|
|
return schedule_thread();
|
1996-04-03 02:02:34 -08:00
|
|
|
}
|
|
|
|
|
1997-04-11 06:57:34 -07:00
|
|
|
/* Suspend the current thread on a select() request */
|
1996-04-03 02:02:34 -08:00
|
|
|
|
1997-09-02 05:55:01 -07:00
|
|
|
value thread_select(value arg) /* ML */
|
1996-04-03 02:02:34 -08:00
|
|
|
{
|
1997-04-11 06:57:34 -07:00
|
|
|
double date;
|
|
|
|
/* Don't do an error if we're not initialized yet
|
|
|
|
(we can be called from thread-safe Pervasives before initialization),
|
|
|
|
just return immediately. */
|
|
|
|
if (curr_thread == NULL) return RESUMED_WAKEUP;
|
1996-06-11 02:41:25 -07:00
|
|
|
check_callback();
|
1997-04-11 06:57:34 -07:00
|
|
|
Assign(curr_thread->readfds, Field(arg, 0));
|
|
|
|
Assign(curr_thread->writefds, Field(arg, 1));
|
|
|
|
Assign(curr_thread->exceptfds, Field(arg, 2));
|
|
|
|
date = Double_val(Field(arg, 3));
|
|
|
|
if (date >= 0.0) {
|
|
|
|
date += timeofday();
|
|
|
|
Assign(curr_thread->delay, copy_double(date));
|
|
|
|
curr_thread->status = BLOCKED_IO | BLOCKED_DELAY;
|
|
|
|
} else {
|
|
|
|
curr_thread->status = BLOCKED_IO;
|
|
|
|
}
|
1996-04-18 09:32:04 -07:00
|
|
|
return schedule_thread();
|
1996-04-03 02:02:34 -08:00
|
|
|
}
|
|
|
|
|
1996-04-18 09:32:04 -07:00
|
|
|
/* Primitives to implement suspension on buffered channels */
|
1996-04-03 02:02:34 -08:00
|
|
|
|
1997-09-02 05:55:01 -07:00
|
|
|
value thread_inchan_ready(value vchan) /* ML */
|
1996-04-03 02:02:34 -08:00
|
|
|
{
|
1997-08-29 08:05:51 -07:00
|
|
|
struct channel * chan = Channel(vchan);
|
1996-04-18 09:32:04 -07:00
|
|
|
return Val_bool(chan->curr < chan->max);
|
|
|
|
}
|
|
|
|
|
1997-09-02 05:55:01 -07:00
|
|
|
value thread_outchan_ready(value vchan, value vsize) /* ML */
|
1996-04-18 09:32:04 -07:00
|
|
|
{
|
1997-08-29 08:05:51 -07:00
|
|
|
struct channel * chan = Channel(vchan);
|
1996-04-18 09:32:04 -07:00
|
|
|
long size = Long_val(vsize);
|
|
|
|
/* Negative size means we want to flush the buffer entirely */
|
|
|
|
if (size < 0) {
|
|
|
|
return Val_bool(chan->curr == chan->buff);
|
|
|
|
} else {
|
1996-04-29 09:56:51 -07:00
|
|
|
int free = chan->end - chan->curr;
|
|
|
|
if (chan->curr == chan->buff)
|
|
|
|
return Val_bool(size < free);
|
|
|
|
else
|
|
|
|
return Val_bool(size <= free);
|
1996-04-18 09:32:04 -07:00
|
|
|
}
|
1996-04-03 02:02:34 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Suspend the current thread for some time */
|
|
|
|
|
1997-09-02 05:55:01 -07:00
|
|
|
value thread_delay(value time) /* ML */
|
1996-04-03 02:02:34 -08:00
|
|
|
{
|
|
|
|
double date = timeofday() + Double_val(time);
|
1996-10-17 02:59:18 -07:00
|
|
|
Assert(curr_thread != NULL);
|
1996-06-11 02:41:25 -07:00
|
|
|
check_callback();
|
1996-04-03 02:02:34 -08:00
|
|
|
curr_thread->status = BLOCKED_DELAY;
|
|
|
|
Assign(curr_thread->delay, copy_double(date));
|
1996-04-18 09:32:04 -07:00
|
|
|
return schedule_thread();
|
|
|
|
}
|
|
|
|
|
1996-04-03 02:02:34 -08:00
|
|
|
/* Suspend the current thread until another thread terminates */
|
|
|
|
|
1997-09-02 05:55:01 -07:00
|
|
|
value thread_join(value th) /* ML */
|
1996-04-03 02:02:34 -08:00
|
|
|
{
|
1996-06-11 02:41:25 -07:00
|
|
|
check_callback();
|
1996-10-17 02:59:18 -07:00
|
|
|
Assert(curr_thread != NULL);
|
1996-04-03 02:02:34 -08:00
|
|
|
if (((thread_t)th)->status == KILLED) return Val_unit;
|
|
|
|
curr_thread->status = BLOCKED_JOIN;
|
|
|
|
Assign(curr_thread->joining, th);
|
1996-04-18 09:32:04 -07:00
|
|
|
return schedule_thread();
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Suspend the current thread until a Unix process exits */
|
|
|
|
|
1997-09-02 05:55:01 -07:00
|
|
|
value thread_wait_pid(value pid) /* ML */
|
1996-04-18 09:32:04 -07:00
|
|
|
{
|
1996-10-17 02:59:18 -07:00
|
|
|
Assert(curr_thread != NULL);
|
1996-06-11 02:41:25 -07:00
|
|
|
check_callback();
|
1996-04-18 09:32:04 -07:00
|
|
|
curr_thread->status = BLOCKED_WAIT;
|
|
|
|
curr_thread->waitpid = pid;
|
|
|
|
return schedule_thread();
|
1996-04-03 02:02:34 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Reactivate another thread */
|
|
|
|
|
1997-09-02 05:55:01 -07:00
|
|
|
value thread_wakeup(value thread) /* ML */
|
1996-04-03 02:02:34 -08:00
|
|
|
{
|
|
|
|
thread_t th = (thread_t) thread;
|
|
|
|
switch (th->status) {
|
|
|
|
case SUSPENDED:
|
|
|
|
th->status = RUNNABLE;
|
1996-04-18 09:32:04 -07:00
|
|
|
th->retval = RESUMED_WAKEUP;
|
1996-04-29 06:21:09 -07:00
|
|
|
break;
|
1996-04-03 02:02:34 -08:00
|
|
|
case KILLED:
|
|
|
|
failwith("Thread.wakeup: killed thread");
|
|
|
|
default:
|
1996-04-18 09:32:04 -07:00
|
|
|
failwith("Thread.wakeup: thread not suspended");
|
1996-04-03 02:02:34 -08:00
|
|
|
}
|
|
|
|
return Val_unit;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Return the current thread */
|
|
|
|
|
1997-09-02 05:55:01 -07:00
|
|
|
value thread_self(value unit) /* ML */
|
1996-04-03 02:02:34 -08:00
|
|
|
{
|
1996-10-17 02:59:18 -07:00
|
|
|
Assert(curr_thread != NULL);
|
1996-04-03 02:02:34 -08:00
|
|
|
return (value) curr_thread;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Kill a thread */
|
|
|
|
|
1997-09-02 05:55:01 -07:00
|
|
|
value thread_kill(value thread) /* ML */
|
1996-04-03 02:02:34 -08:00
|
|
|
{
|
1996-04-18 09:32:04 -07:00
|
|
|
value retval = Val_unit;
|
1996-04-03 02:02:34 -08:00
|
|
|
thread_t th = (thread_t) thread;
|
1997-05-13 07:06:17 -07:00
|
|
|
if (th->status == KILLED) failwith("Thread.kill: killed thread");
|
1996-04-03 02:02:34 -08:00
|
|
|
/* Don't paint ourselves in a corner */
|
|
|
|
if (th == th->next) failwith("Thread.kill: cannot kill the last thread");
|
|
|
|
/* This thread is no longer waiting on anything */
|
|
|
|
th->status = KILLED;
|
|
|
|
/* If this is the current thread, activate another one */
|
1996-04-18 09:32:04 -07:00
|
|
|
if (th == curr_thread) retval = schedule_thread();
|
1996-04-03 02:02:34 -08:00
|
|
|
/* Remove thread from the doubly-linked list */
|
|
|
|
Assign(th->prev->next, th->next);
|
|
|
|
Assign(th->next->prev, th->prev);
|
|
|
|
/* Free its resources */
|
|
|
|
stat_free((char *) th->stack_low);
|
|
|
|
th->stack_low = NULL;
|
|
|
|
th->stack_high = NULL;
|
|
|
|
th->stack_threshold = NULL;
|
|
|
|
th->sp = NULL;
|
|
|
|
th->trapsp = NULL;
|
1996-04-18 09:32:04 -07:00
|
|
|
return retval;
|
1996-04-03 02:02:34 -08:00
|
|
|
}
|
|
|
|
|
1997-04-11 06:57:34 -07:00
|
|
|
/* Set a list of file descriptors in a fdset */
|
|
|
|
|
1997-09-02 05:55:01 -07:00
|
|
|
static void add_fdlist_to_set(value fdl, fd_set *set)
|
1997-04-11 06:57:34 -07:00
|
|
|
{
|
|
|
|
for (/*nothing*/; fdl != NO_FDS; fdl = Field(fdl, 1)) {
|
|
|
|
FD_SET(Int_val(Field(fdl, 0)), set);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Build the intersection of a list and a fdset (the list of file descriptors
|
|
|
|
which are both in the list and in the fdset). */
|
|
|
|
|
1997-09-02 05:55:01 -07:00
|
|
|
static value inter_fdlist_set(value fdl, fd_set *set)
|
1997-04-11 06:57:34 -07:00
|
|
|
{
|
1997-05-26 10:16:31 -07:00
|
|
|
value res = Val_unit;
|
|
|
|
value cons;
|
|
|
|
|
|
|
|
Begin_roots2(fdl, res);
|
|
|
|
for (res = NO_FDS; fdl != NO_FDS; fdl = Field(fdl, 1)) {
|
|
|
|
int fd = Int_val(Field(fdl, 0));
|
|
|
|
if (FD_ISSET(fd, set)) {
|
|
|
|
cons = alloc(2, 0);
|
|
|
|
Field(cons, 0) = Val_int(fd);
|
|
|
|
Field(cons, 1) = res;
|
|
|
|
res = cons;
|
|
|
|
FD_CLR(fd, set); /* wake up only one thread per fd ready */
|
|
|
|
}
|
1997-04-11 06:57:34 -07:00
|
|
|
}
|
1997-05-26 10:16:31 -07:00
|
|
|
End_roots();
|
1997-04-11 06:57:34 -07:00
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
1996-04-29 06:21:09 -07:00
|
|
|
/* Auxiliary function for allocating the result of a waitpid() call */
|
|
|
|
|
1996-05-07 01:15:03 -07:00
|
|
|
#if !(defined(WIFEXITED) && defined(WEXITSTATUS) && defined(WIFSTOPPED) && \
|
|
|
|
defined(WSTOPSIG) && defined(WTERMSIG))
|
1997-04-11 06:57:34 -07:00
|
|
|
/* Assume old-style V7 status word */
|
1997-06-13 08:52:16 -07:00
|
|
|
#define WIFEXITED(status) (((status) & 0xFF) == 0)
|
1996-04-29 06:21:09 -07:00
|
|
|
#define WEXITSTATUS(status) (((status) >> 8) & 0xFF)
|
1997-06-13 08:52:16 -07:00
|
|
|
#define WIFSTOPPED(status) (((status) & 0xFF) == 0xFF)
|
1996-04-29 06:21:09 -07:00
|
|
|
#define WSTOPSIG(status) (((status) >> 8) & 0xFF)
|
|
|
|
#define WTERMSIG(status) ((status) & 0x3F)
|
|
|
|
#endif
|
|
|
|
|
1997-04-11 06:57:34 -07:00
|
|
|
#define TAG_WEXITED 0
|
|
|
|
#define TAG_WSIGNALED 1
|
|
|
|
#define TAG_WSTOPPED 2
|
|
|
|
|
1997-09-02 05:55:01 -07:00
|
|
|
static value alloc_process_status(int pid, int status)
|
1996-04-29 06:21:09 -07:00
|
|
|
{
|
|
|
|
value st, res;
|
|
|
|
|
|
|
|
if (WIFEXITED(status)) {
|
1997-04-11 06:57:34 -07:00
|
|
|
st = alloc(1, TAG_WEXITED);
|
1996-04-29 06:21:09 -07:00
|
|
|
Field(st, 0) = Val_int(WEXITSTATUS(status));
|
|
|
|
}
|
|
|
|
else if (WIFSTOPPED(status)) {
|
1997-04-11 06:57:34 -07:00
|
|
|
st = alloc(1, TAG_WSTOPPED);
|
1996-04-29 06:21:09 -07:00
|
|
|
Field(st, 0) = Val_int(WSTOPSIG(status));
|
|
|
|
}
|
|
|
|
else {
|
1997-04-11 06:57:34 -07:00
|
|
|
st = alloc(1, TAG_WSIGNALED);
|
1996-04-29 06:21:09 -07:00
|
|
|
Field(st, 0) = Val_int(WTERMSIG(status));
|
|
|
|
}
|
1997-05-26 10:16:31 -07:00
|
|
|
Begin_root(st);
|
|
|
|
res = alloc(2, TAG_RESUMED_WAIT);
|
|
|
|
Field(res, 0) = Val_int(pid);
|
|
|
|
Field(res, 1) = st;
|
|
|
|
End_roots();
|
1996-04-29 06:21:09 -07:00
|
|
|
return res;
|
|
|
|
}
|