1 /***********************************************************************/
5 /* Xavier Leroy and Damien Doligez, INRIA Rocquencourt */
7 /* Copyright 1995 Institut National de Recherche en Informatique et */
8 /* en Automatique. All rights reserved. This file is distributed */
9 /* under the terms of the GNU Library General Public License, with */
10 /* the special exception on linking described in file ../../LICENSE. */
12 /***********************************************************************/
14 /* $Id: posix.c 9201 2009-03-28 17:35:59Z xleroy $ */
16 /* Thread interface for POSIX 1003.1c threads */
24 #define _POSIX_PTHREAD_SEMANTICS
32 #include "backtrace.h"
50 /* Initial size of stack when a thread is created (4 Ko) */
51 #define Thread_stack_size (Stack_size / 4)
53 /* Max computation time before rescheduling, in microseconds (50ms) */
54 #define Thread_timeout 50000
56 /* The ML value describing a thread (heap-allocated) */
58 struct caml_thread_descr {
59 value ident; /* Unique integer ID */
60 value start_closure; /* The closure to start this thread */
61 value terminated; /* Mutex held while the thread is running */
64 #define Ident(v) (((struct caml_thread_descr *)(v))->ident)
65 #define Start_closure(v) (((struct caml_thread_descr *)(v))->start_closure)
66 #define Terminated(v) (((struct caml_thread_descr *)(v))->terminated)
68 /* The infos on threads (allocated via malloc()) */
70 struct caml_thread_struct {
71 pthread_t pthread; /* The Posix thread id */
72 value descr; /* The heap-allocated descriptor (root) */
73 struct caml_thread_struct * next; /* Double linking of running threads */
74 struct caml_thread_struct * prev;
76 char * bottom_of_stack; /* Saved value of caml_bottom_of_stack */
77 uintnat last_retaddr; /* Saved value of caml_last_return_address */
78 value * gc_regs; /* Saved value of caml_gc_regs */
79 char * exception_pointer; /* Saved value of caml_exception_pointer */
80 struct caml__roots_block * local_roots; /* Saved value of local_roots */
81 struct longjmp_buffer * exit_buf; /* For thread exit */
83 value * stack_low; /* The execution stack for this thread */
85 value * stack_threshold;
86 value * sp; /* Saved value of extern_sp for this thread */
87 value * trapsp; /* Saved value of trapsp for this thread */
88 struct caml__roots_block * local_roots; /* Saved value of local_roots */
89 struct longjmp_buffer * external_raise; /* Saved external_raise */
91 int backtrace_pos; /* Saved backtrace_pos */
92 code_t * backtrace_buffer; /* Saved backtrace_buffer */
93 value backtrace_last_exn; /* Saved backtrace_last_exn (root) */
96 typedef struct caml_thread_struct * caml_thread_t;
98 /* The descriptor for the currently executing thread */
99 static caml_thread_t curr_thread = NULL;
101 /* Track whether one thread is running Caml code. There can be
102 at most one such thread at any time. */
103 static volatile int caml_runtime_busy = 1;
105 /* Number of threads waiting to run Caml code. */
106 static volatile int caml_runtime_waiters = 0;
108 /* Mutex that protects the two variables above. */
109 static pthread_mutex_t caml_runtime_mutex = PTHREAD_MUTEX_INITIALIZER;
111 /* Condition signaled when caml_runtime_busy becomes 0 */
112 static pthread_cond_t caml_runtime_is_free = PTHREAD_COND_INITIALIZER;
114 /* Whether the ``tick'' thread is already running */
115 static int caml_tick_thread_running = 0;
117 /* The key used for storing the thread descriptor in the specific data
118 of the corresponding Posix thread. */
119 static pthread_key_t thread_descriptor_key;
121 /* The key used for unlocking I/O channels on exceptions */
122 static pthread_key_t last_channel_locked_key;
124 /* Identifier for next thread creation */
125 static intnat thread_next_ident = 0;
127 /* Forward declarations */
128 value caml_threadstatus_new (void);
129 void caml_threadstatus_terminate (value);
130 int caml_threadstatus_wait (value);
131 static void caml_pthread_check (int, char *);
133 /* Imports for the native-code compiler */
134 extern struct longjmp_buffer caml_termination_jmpbuf;
135 extern void (*caml_termination_hook)(void *);
137 /* Hook for scanning the stacks of the other threads */
139 static void (*prev_scan_roots_hook) (scanning_action);
141 static void caml_thread_scan_roots(scanning_action action)
147 (*action)(th->descr, &th->descr);
148 (*action)(th->backtrace_last_exn, &th->backtrace_last_exn);
149 /* Don't rescan the stack of the current thread, it was done already */
150 if (th != curr_thread) {
152 if (th->bottom_of_stack != NULL)
153 do_local_roots(action, th->bottom_of_stack, th->last_retaddr,
154 th->gc_regs, th->local_roots);
156 do_local_roots(action, th->sp, th->stack_high, th->local_roots);
160 } while (th != curr_thread);
162 if (prev_scan_roots_hook != NULL) (*prev_scan_roots_hook)(action);
165 /* Hooks for enter_blocking_section and leave_blocking_section */
167 static void caml_thread_enter_blocking_section(void)
169 /* Save the stack-related global variables in the thread descriptor
170 of the current thread */
172 curr_thread->bottom_of_stack = caml_bottom_of_stack;
173 curr_thread->last_retaddr = caml_last_return_address;
174 curr_thread->gc_regs = caml_gc_regs;
175 curr_thread->exception_pointer = caml_exception_pointer;
176 curr_thread->local_roots = local_roots;
178 curr_thread->stack_low = stack_low;
179 curr_thread->stack_high = stack_high;
180 curr_thread->stack_threshold = stack_threshold;
181 curr_thread->sp = extern_sp;
182 curr_thread->trapsp = trapsp;
183 curr_thread->local_roots = local_roots;
184 curr_thread->external_raise = external_raise;
186 curr_thread->backtrace_pos = backtrace_pos;
187 curr_thread->backtrace_buffer = backtrace_buffer;
188 curr_thread->backtrace_last_exn = backtrace_last_exn;
189 /* Tell other threads that the runtime is free */
190 pthread_mutex_lock(&caml_runtime_mutex);
191 caml_runtime_busy = 0;
192 pthread_mutex_unlock(&caml_runtime_mutex);
193 pthread_cond_signal(&caml_runtime_is_free);
196 static void caml_thread_leave_blocking_section(void)
198 /* Wait until the runtime is free */
199 pthread_mutex_lock(&caml_runtime_mutex);
200 while (caml_runtime_busy) {
201 caml_runtime_waiters++;
202 pthread_cond_wait(&caml_runtime_is_free, &caml_runtime_mutex);
203 caml_runtime_waiters--;
205 caml_runtime_busy = 1;
206 pthread_mutex_unlock(&caml_runtime_mutex);
207 /* Update curr_thread to point to the thread descriptor corresponding
208 to the thread currently executing */
209 curr_thread = pthread_getspecific(thread_descriptor_key);
210 /* Restore the stack-related global variables */
212 caml_bottom_of_stack= curr_thread->bottom_of_stack;
213 caml_last_return_address = curr_thread->last_retaddr;
214 caml_gc_regs = curr_thread->gc_regs;
215 caml_exception_pointer = curr_thread->exception_pointer;
216 local_roots = curr_thread->local_roots;
218 stack_low = curr_thread->stack_low;
219 stack_high = curr_thread->stack_high;
220 stack_threshold = curr_thread->stack_threshold;
221 extern_sp = curr_thread->sp;
222 trapsp = curr_thread->trapsp;
223 local_roots = curr_thread->local_roots;
224 external_raise = curr_thread->external_raise;
226 backtrace_pos = curr_thread->backtrace_pos;
227 backtrace_buffer = curr_thread->backtrace_buffer;
228 backtrace_last_exn = curr_thread->backtrace_last_exn;
231 static int caml_thread_try_leave_blocking_section(void)
233 /* Disable immediate processing of signals (PR#3659).
234 try_leave_blocking_section always fails, forcing the signal to be
235 recorded and processed at the next leave_blocking_section or
240 /* Hooks for I/O locking */
242 static void caml_io_mutex_free(struct channel *chan)
244 pthread_mutex_t * mutex = chan->mutex;
246 pthread_mutex_destroy(mutex);
247 stat_free((char *) mutex);
251 static void caml_io_mutex_lock(struct channel *chan)
253 if (chan->mutex == NULL) {
254 pthread_mutex_t * mutex =
255 (pthread_mutex_t *) stat_alloc(sizeof(pthread_mutex_t));
256 pthread_mutex_init(mutex, NULL);
257 chan->mutex = (void *) mutex;
259 /* PR#4351: first try to acquire mutex without releasing the master lock */
260 if (pthread_mutex_trylock(chan->mutex) == 0) {
261 pthread_setspecific(last_channel_locked_key, (void *) chan);
264 /* If unsuccessful, block on mutex */
265 enter_blocking_section();
266 pthread_mutex_lock(chan->mutex);
267 /* Problem: if a signal occurs at this point,
268 and the signal handler raises an exception, we will not
269 unlock the mutex. The alternative (doing the setspecific
270 before locking the mutex is also incorrect, since we could
271 then unlock a mutex that is unlocked or locked by someone else. */
272 pthread_setspecific(last_channel_locked_key, (void *) chan);
273 leave_blocking_section();
276 static void caml_io_mutex_unlock(struct channel *chan)
278 pthread_mutex_unlock(chan->mutex);
279 pthread_setspecific(last_channel_locked_key, NULL);
282 static void caml_io_mutex_unlock_exn(void)
284 struct channel * chan = pthread_getspecific(last_channel_locked_key);
285 if (chan != NULL) caml_io_mutex_unlock(chan);
288 /* The "tick" thread fakes a SIGVTALRM signal at regular intervals. */
290 static void * caml_thread_tick(void * arg)
292 struct timeval timeout;
298 /* Block all signals so that we don't try to execute
299 a Caml signal handler */
301 pthread_sigmask(SIG_BLOCK, &mask, NULL);
303 /* select() seems to be the most efficient way to suspend the
304 thread for sub-second intervals */
306 timeout.tv_usec = Thread_timeout;
307 select(0, NULL, NULL, NULL, &timeout);
308 /* This signal should never cause a callback, so don't go through
309 handle_signal(), tweak the global variable directly. */
310 caml_pending_signals[SIGVTALRM] = 1;
311 caml_signals_are_pending = 1;
313 young_limit = young_end;
318 /* Hack around LinuxThreads' non-standard signal handling:
319 if program is killed on a signal, e.g. SIGINT, the current
320 thread will not die on this signal (because of the signal blocking
321 above). Hence, periodically check that the thread manager (our
322 parent process) still exists. */
324 if (tickcount >= 2000000 / Thread_timeout) { /* every 2 secs approx */
326 if (getppid() == 1) pthread_exit(NULL);
330 return NULL; /* prevents compiler warning */
333 /* Reinitialize the thread machinery after a fork() (PR#4577) */
335 static void caml_thread_reinitialize(void)
337 caml_thread_t thr, next;
338 struct channel * chan;
340 /* Remove all other threads (now nonexistent)
341 from the doubly-linked list of threads */
342 thr = curr_thread->next;
343 while (thr != curr_thread) {
348 curr_thread->next = curr_thread;
349 curr_thread->prev = curr_thread;
350 /* Reinitialize the master lock machinery,
351 just in case the fork happened while other threads were doing
352 leave_blocking_section */
353 pthread_mutex_init(&caml_runtime_mutex, NULL);
354 pthread_cond_init(&caml_runtime_is_free, NULL);
355 caml_runtime_waiters = 0; /* no other thread is waiting for the RTS */
356 caml_runtime_busy = 1; /* normally useless */
357 /* Tick thread is not currently running in child process, will be
358 re-created at next Thread.create */
359 caml_tick_thread_running = 0;
360 /* Reinitialize all IO mutexes */
361 for (chan = caml_all_opened_channels;
364 if (chan->mutex != NULL) pthread_mutex_init(chan->mutex, NULL);
368 /* Initialize the thread machinery */
370 value caml_thread_initialize(value unit) /* ML */
375 /* Protect against repeated initialization (PR#1325) */
376 if (curr_thread != NULL) return Val_unit;
378 /* Initialize the keys */
379 pthread_key_create(&thread_descriptor_key, NULL);
380 pthread_key_create(&last_channel_locked_key, NULL);
381 /* Create and initialize the termination semaphore */
382 mu = caml_threadstatus_new();
383 /* Create a descriptor for the current thread */
384 descr = alloc_small(3, 0);
385 Ident(descr) = Val_long(thread_next_ident);
386 Start_closure(descr) = Val_unit;
387 Terminated(descr) = mu;
389 /* Create an info block for the current thread */
391 (caml_thread_t) stat_alloc(sizeof(struct caml_thread_struct));
392 curr_thread->pthread = pthread_self();
393 curr_thread->descr = descr;
394 curr_thread->next = curr_thread;
395 curr_thread->prev = curr_thread;
396 curr_thread->backtrace_last_exn = Val_unit;
398 curr_thread->exit_buf = &caml_termination_jmpbuf;
400 /* The stack-related fields will be filled in at the next
401 enter_blocking_section */
402 /* Associate the thread descriptor with the thread */
403 pthread_setspecific(thread_descriptor_key, (void *) curr_thread);
404 /* Set up the hooks */
405 prev_scan_roots_hook = scan_roots_hook;
406 scan_roots_hook = caml_thread_scan_roots;
407 enter_blocking_section_hook = caml_thread_enter_blocking_section;
408 leave_blocking_section_hook = caml_thread_leave_blocking_section;
409 try_leave_blocking_section_hook = caml_thread_try_leave_blocking_section;
411 caml_termination_hook = pthread_exit;
413 caml_channel_mutex_free = caml_io_mutex_free;
414 caml_channel_mutex_lock = caml_io_mutex_lock;
415 caml_channel_mutex_unlock = caml_io_mutex_unlock;
416 caml_channel_mutex_unlock_exn = caml_io_mutex_unlock_exn;
417 /* Set up fork() to reinitialize the thread machinery in the child
419 pthread_atfork(NULL, NULL, caml_thread_reinitialize);
424 /* Thread cleanup at termination */
426 static void caml_thread_stop(void)
428 caml_thread_t th = curr_thread;
430 /* Signal that the thread has terminated */
431 caml_threadstatus_terminate(Terminated(th->descr));
432 /* Remove th from the doubly-linked list of threads */
433 th->next->prev = th->prev;
434 th->prev->next = th->next;
435 /* Release the runtime system */
436 pthread_mutex_lock(&caml_runtime_mutex);
437 caml_runtime_busy = 0;
438 pthread_mutex_unlock(&caml_runtime_mutex);
439 pthread_cond_signal(&caml_runtime_is_free);
441 /* Free the memory resources */
442 stat_free(th->stack_low);
444 if (th->backtrace_buffer != NULL) free(th->backtrace_buffer);
445 /* Free the thread descriptor */
449 /* Create a thread */
451 static void * caml_thread_start(void * arg)
453 caml_thread_t th = (caml_thread_t) arg;
456 struct longjmp_buffer termination_buf;
459 /* Associate the thread descriptor with the thread */
460 pthread_setspecific(thread_descriptor_key, (void *) th);
461 /* Acquire the global mutex and set up the stack variables */
462 leave_blocking_section();
464 /* Setup termination handler (for caml_thread_exit) */
465 if (sigsetjmp(termination_buf.buf, 0) == 0) {
466 th->exit_buf = &termination_buf;
468 /* Callback the closure */
469 clos = Start_closure(th->descr);
470 modify(&(Start_closure(th->descr)), Val_unit);
471 callback_exn(clos, Val_unit);
476 /* The thread now stops running */
480 value caml_thread_new(value clos) /* ML */
484 pthread_t tick_pthread;
489 Begin_roots2 (clos, mu)
490 /* Create and initialize the termination semaphore */
491 mu = caml_threadstatus_new();
492 /* Create a descriptor for the new thread */
493 descr = alloc_small(3, 0);
494 Ident(descr) = Val_long(thread_next_ident);
495 Start_closure(descr) = clos;
496 Terminated(descr) = mu;
498 /* Create an info block for the current thread */
499 th = (caml_thread_t) stat_alloc(sizeof(struct caml_thread_struct));
502 th->bottom_of_stack = NULL;
503 th->exception_pointer = NULL;
504 th->local_roots = NULL;
506 /* Allocate the stacks */
507 th->stack_low = (value *) stat_alloc(Thread_stack_size);
508 th->stack_high = th->stack_low + Thread_stack_size / sizeof(value);
509 th->stack_threshold = th->stack_low + Stack_threshold / sizeof(value);
510 th->sp = th->stack_high;
511 th->trapsp = th->stack_high;
512 th->local_roots = NULL;
513 th->external_raise = NULL;
515 th->backtrace_pos = 0;
516 th->backtrace_buffer = NULL;
517 th->backtrace_last_exn = Val_unit;
518 /* Add thread info block to the list of threads */
519 th->next = curr_thread->next;
520 th->prev = curr_thread;
521 curr_thread->next->prev = th;
522 curr_thread->next = th;
523 /* Create the new thread */
524 pthread_attr_init(&attr);
525 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
526 err = pthread_create(&th->pthread, &attr, caml_thread_start, (void *) th);
528 /* Creation failed, remove thread info block from list of threads */
529 th->next->prev = curr_thread;
530 curr_thread->next = th->next;
532 stat_free(th->stack_low);
535 caml_pthread_check(err, "Thread.create");
538 /* Create the tick thread if not already done.
539 Because of PR#4666, we start the tick thread late, only when we create
540 the first additional thread in the current process*/
541 if (! caml_tick_thread_running) {
542 caml_tick_thread_running = 1;
543 pthread_attr_init(&attr);
544 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
545 err = pthread_create(&tick_pthread, &attr, caml_thread_tick, NULL);
546 caml_pthread_check(err, "Thread.create");
551 /* Return the current thread */
553 value caml_thread_self(value unit) /* ML */
555 if (curr_thread == NULL) invalid_argument("Thread.self: not initialized");
556 return curr_thread->descr;
559 /* Return the identifier of a thread */
561 value caml_thread_id(value th) /* ML */
566 /* Print uncaught exception and backtrace */
568 value caml_thread_uncaught_exception(value exn) /* ML */
570 char * msg = format_caml_exception(exn);
571 fprintf(stderr, "Thread %d killed on uncaught exception %s\n",
572 Int_val(Ident(curr_thread->descr)), msg);
574 if (caml_backtrace_active) print_exception_backtrace();
579 /* Terminate current thread */
581 value caml_thread_exit(value unit) /* ML */
584 /* We cannot call pthread_exit here because on some systems this
585 raises a C++ exception, and ocamlopt-generated stack frames
586 cannot be unwound. Instead, we longjmp to the thread creation
587 point (in caml_thread_start) or to the point in caml_main
588 where caml_termination_hook will be called. */
589 struct longjmp_buffer * exit_buf;
590 if (curr_thread == NULL) invalid_argument("Thread.exit: not initialized");
591 exit_buf = curr_thread->exit_buf;
593 siglongjmp(exit_buf->buf, 1);
595 /* No such problem in bytecode */
596 if (curr_thread == NULL) invalid_argument("Thread.exit: not initialized");
600 return Val_unit; /* not reached */
603 /* Allow re-scheduling */
605 value caml_thread_yield(value unit) /* ML */
607 if (caml_runtime_waiters == 0) return Val_unit;
608 enter_blocking_section();
610 /* sched_yield() doesn't do what we want in Linux 2.6 and up (PR#2663) */
613 leave_blocking_section();
617 /* Suspend the current thread until another thread terminates */
619 value caml_thread_join(value th) /* ML */
621 int retcode = caml_threadstatus_wait(Terminated(th));
622 caml_pthread_check(retcode, "Thread.join");
626 /* Mutex operations */
628 #define Mutex_val(v) (* ((pthread_mutex_t **) Data_custom_val(v)))
629 #define Max_mutex_number 1000
631 static void caml_mutex_finalize(value wrapper)
633 pthread_mutex_t * mut = Mutex_val(wrapper);
634 pthread_mutex_destroy(mut);
638 static int caml_mutex_condition_compare(value wrapper1, value wrapper2)
640 pthread_mutex_t * mut1 = Mutex_val(wrapper1);
641 pthread_mutex_t * mut2 = Mutex_val(wrapper2);
642 return mut1 == mut2 ? 0 : mut1 < mut2 ? -1 : 1;
645 static struct custom_operations caml_mutex_ops = {
648 caml_mutex_condition_compare,
650 custom_serialize_default,
651 custom_deserialize_default
654 value caml_mutex_new(value unit) /* ML */
656 pthread_mutex_t * mut;
658 mut = stat_alloc(sizeof(pthread_mutex_t));
659 caml_pthread_check(pthread_mutex_init(mut, NULL), "Mutex.create");
660 wrapper = alloc_custom(&caml_mutex_ops, sizeof(pthread_mutex_t *),
661 1, Max_mutex_number);
662 Mutex_val(wrapper) = mut;
666 value caml_mutex_lock(value wrapper) /* ML */
669 pthread_mutex_t * mut = Mutex_val(wrapper);
670 /* PR#4351: first try to acquire mutex without releasing the master lock */
671 retcode = pthread_mutex_trylock(mut);
672 if (retcode == 0) return Val_unit;
673 /* If unsuccessful, block on mutex */
674 Begin_root(wrapper) /* prevent the deallocation of mutex */
675 enter_blocking_section();
676 retcode = pthread_mutex_lock(mut);
677 leave_blocking_section();
679 caml_pthread_check(retcode, "Mutex.lock");
683 value caml_mutex_unlock(value wrapper) /* ML */
686 pthread_mutex_t * mut = Mutex_val(wrapper);
687 /* PR#4351: no need to release and reacquire master lock */
688 retcode = pthread_mutex_unlock(mut);
689 caml_pthread_check(retcode, "Mutex.unlock");
693 value caml_mutex_try_lock(value wrapper) /* ML */
696 pthread_mutex_t * mut = Mutex_val(wrapper);
697 retcode = pthread_mutex_trylock(mut);
698 if (retcode == EBUSY) return Val_false;
699 caml_pthread_check(retcode, "Mutex.try_lock");
703 /* Conditions operations */
705 #define Condition_val(v) (* ((pthread_cond_t **) Data_custom_val(v)))
706 #define Max_condition_number 1000
708 static void caml_condition_finalize(value wrapper)
710 pthread_cond_t * cond = Condition_val(wrapper);
711 pthread_cond_destroy(cond);
715 static struct custom_operations caml_condition_ops = {
717 caml_condition_finalize,
718 caml_mutex_condition_compare,
720 custom_serialize_default,
721 custom_deserialize_default
724 value caml_condition_new(value unit) /* ML */
726 pthread_cond_t * cond;
728 cond = stat_alloc(sizeof(pthread_cond_t));
729 caml_pthread_check(pthread_cond_init(cond, NULL), "Condition.create");
730 wrapper = alloc_custom(&caml_condition_ops, sizeof(pthread_cond_t *),
731 1, Max_condition_number);
732 Condition_val(wrapper) = cond;
736 value caml_condition_wait(value wcond, value wmut) /* ML */
739 pthread_cond_t * cond = Condition_val(wcond);
740 pthread_mutex_t * mut = Mutex_val(wmut);
741 Begin_roots2(wcond, wmut) /* prevent deallocation of cond and mutex */
742 enter_blocking_section();
743 retcode = pthread_cond_wait(cond, mut);
744 leave_blocking_section();
746 caml_pthread_check(retcode, "Condition.wait");
750 value caml_condition_signal(value wrapper) /* ML */
753 pthread_cond_t * cond = Condition_val(wrapper);
754 retcode = pthread_cond_signal(cond);
755 caml_pthread_check(retcode, "Condition.signal");
759 value caml_condition_broadcast(value wrapper) /* ML */
762 pthread_cond_t * cond = Condition_val(wrapper);
763 retcode = pthread_cond_broadcast(cond);
764 caml_pthread_check(retcode, "Condition.broadcast");
768 /* Thread status blocks */
770 struct caml_threadstatus {
771 pthread_mutex_t lock; /* mutex for mutual exclusion */
772 enum { ALIVE, TERMINATED } status; /* status of thread */
773 pthread_cond_t terminated; /* signaled when thread terminates */
776 #define Threadstatus_val(v) \
777 (* ((struct caml_threadstatus **) Data_custom_val(v)))
778 #define Max_threadstatus_number 500
780 static void caml_threadstatus_finalize(value wrapper)
782 struct caml_threadstatus * ts = Threadstatus_val(wrapper);
783 pthread_mutex_destroy(&ts->lock);
784 pthread_cond_destroy(&ts->terminated);
788 static struct custom_operations caml_threadstatus_ops = {
790 caml_threadstatus_finalize,
791 caml_mutex_condition_compare,
793 custom_serialize_default,
794 custom_deserialize_default
797 value caml_threadstatus_new (void)
799 struct caml_threadstatus * ts;
801 ts = stat_alloc(sizeof(struct caml_threadstatus));
802 caml_pthread_check(pthread_mutex_init(&ts->lock, NULL), "Thread.create");
803 caml_pthread_check(pthread_cond_init(&ts->terminated, NULL),
806 wrapper = alloc_custom(&caml_threadstatus_ops,
807 sizeof(struct caml_threadstatus *),
808 1, Max_threadstatus_number);
809 Threadstatus_val(wrapper) = ts;
813 void caml_threadstatus_terminate (value wrapper)
815 struct caml_threadstatus * ts = Threadstatus_val(wrapper);
816 pthread_mutex_lock(&ts->lock);
817 ts->status = TERMINATED;
818 pthread_mutex_unlock(&ts->lock);
819 pthread_cond_broadcast(&ts->terminated);
822 int caml_threadstatus_wait (value wrapper)
824 struct caml_threadstatus * ts = Threadstatus_val(wrapper);
827 Begin_roots1(wrapper) /* prevent deallocation of ts */
828 enter_blocking_section();
829 retcode = pthread_mutex_lock(&ts->lock);
830 if (retcode != 0) goto error;
831 while (ts->status != TERMINATED) {
832 retcode = pthread_cond_wait(&ts->terminated, &ts->lock);
833 if (retcode != 0) goto error;
835 retcode = pthread_mutex_unlock(&ts->lock);
837 leave_blocking_section();
844 static void decode_sigset(value vset, sigset_t * set)
847 while (vset != Val_int(0)) {
848 int sig = caml_convert_signal_number(Int_val(Field(vset, 0)));
850 vset = Field(vset, 1);
858 static value encode_sigset(sigset_t * set)
860 value res = Val_int(0);
864 for (i = 1; i < NSIG; i++)
865 if (sigismember(set, i) > 0) {
866 value newcons = alloc_small(2, 0);
867 Field(newcons, 0) = Val_int(caml_rev_convert_signal_number(i));
868 Field(newcons, 1) = res;
875 static int sigmask_cmd[3] = { SIG_SETMASK, SIG_BLOCK, SIG_UNBLOCK };
877 value caml_thread_sigmask(value cmd, value sigs) /* ML */
880 sigset_t set, oldset;
883 how = sigmask_cmd[Int_val(cmd)];
884 decode_sigset(sigs, &set);
885 enter_blocking_section();
886 retcode = pthread_sigmask(how, &set, &oldset);
887 leave_blocking_section();
888 caml_pthread_check(retcode, "Thread.sigmask");
889 return encode_sigset(&oldset);
892 /* Synchronous signal wait */
894 value caml_wait_signal(value sigs) /* ML */
900 decode_sigset(sigs, &set);
901 enter_blocking_section();
902 retcode = sigwait(&set, &signo);
903 leave_blocking_section();
904 caml_pthread_check(retcode, "Thread.wait_signal");
905 return Val_int(signo);
907 invalid_argument("Thread.wait_signal not implemented");
908 return Val_int(0); /* not reached */
914 static void caml_pthread_check(int retcode, char *msg)
920 if (retcode == 0) return;
921 err = strerror(retcode);
922 msglen = strlen(msg);
923 errlen = strlen(err);
924 str = alloc_string(msglen + 2 + errlen);
925 memmove (&Byte(str, 0), msg, msglen);
926 memmove (&Byte(str, msglen), ": ", 2);
927 memmove (&Byte(str, msglen + 2), err, errlen);
928 raise_sys_error(str);