From 29b096a3a92496f2128036cf0c78b589ef0c3814 Mon Sep 17 00:00:00 2001
From: Xavier Leroy <xavier.leroy@inria.fr>
Date: Mon, 16 Jun 2003 12:31:14 +0000
Subject: Revu implementation Thread.exit dans systhreads (PR#1644)

git-svn-id: http://caml.inria.fr/svn/ocaml/trunk@5599 f963ae5c-01c2-4b8c-9fe0-0dff7051ff02
---
 otherlibs/systhreads/posix.c | 78 ++++++++++++++++++++++++++++++++++++--------
 1 file changed, 65 insertions(+), 13 deletions(-)

(limited to 'otherlibs/systhreads/posix.c')

diff --git a/otherlibs/systhreads/posix.c b/otherlibs/systhreads/posix.c
index f6a7fa570..8effa65f8 100644
--- a/otherlibs/systhreads/posix.c
+++ b/otherlibs/systhreads/posix.c
@@ -78,6 +78,7 @@ struct caml_thread_struct {
   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 */
+  struct longjmp_buffer * exit_buf; /* For thread exit */
 #else
   value * stack_low;            /* The execution stack for this thread */
   value * stack_high;
@@ -113,12 +114,15 @@ static pthread_key_t last_channel_locked_key;
 static long thread_next_ident = 0;
 
 /* Forward declarations */
-
 value caml_threadstatus_new (void);
 void caml_threadstatus_terminate (value);
 int caml_threadstatus_wait (value);
 static void caml_pthread_check (int, char *);
 
+/* Imports for the native-code compiler */
+extern struct longjmp_buffer caml_termination_jmpbuf;
+extern void (*caml_termination_hook)(void *);
+
 /* Hook for scanning the stacks of the other threads */
 
 static void (*prev_scan_roots_hook) (scanning_action);
@@ -332,6 +336,9 @@ value caml_thread_initialize(value unit)   /* ML */
     curr_thread->descr = descr;
     curr_thread->next = curr_thread;
     curr_thread->prev = curr_thread;
+#ifdef NATIVE_CODE
+    curr_thread->exit_buf = &caml_termination_jmpbuf;
+#endif
     /* The stack-related fields will be filled in at the next
        enter_blocking_section */
     /* Associate the thread descriptor with the thread */
@@ -343,6 +350,9 @@ value caml_thread_initialize(value unit)   /* ML */
     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;
+#ifdef NATIVE_CODE
+    caml_termination_hook = pthread_exit;
+#endif
     channel_mutex_free = caml_io_mutex_free;
     channel_mutex_lock = caml_io_mutex_lock;
     channel_mutex_unlock = caml_io_mutex_unlock;
@@ -357,21 +367,12 @@ value caml_thread_initialize(value unit)   /* ML */
   return Val_unit;
 }
 
-/* Create a thread */
+/* Thread cleanup at termination */
 
-static void * caml_thread_start(void * arg)
+static void caml_thread_stop(void)
 {
-  caml_thread_t th = (caml_thread_t) arg;
-  value clos;
+  caml_thread_t th = curr_thread;
 
-  /* 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 */
-  clos = Start_closure(th->descr);
-  modify(&(Start_closure(th->descr)), Val_unit);
-  callback_exn(clos, Val_unit);
   /* Signal that the thread has terminated */
   caml_threadstatus_terminate(Terminated(th->descr));
   /* Remove th from the doubly-linked list of threads */
@@ -387,6 +388,33 @@ static void * caml_thread_start(void * arg)
 #endif
   /* Free the thread descriptor */
   stat_free(th);
+}
+
+/* Create a thread */
+
+static void * caml_thread_start(void * arg)
+{
+  caml_thread_t th = (caml_thread_t) arg;
+  value clos;
+  struct longjmp_buffer termination_buf;
+
+  /* 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();
+#ifdef NATIVE_CODE
+  /* Setup termination handler (for caml_thread_exit) */
+  if (sigsetjmp(termination_buf.buf, 0) == 0) {
+    th->exit_buf = &termination_buf;
+#endif
+    /* Callback the closure */
+    clos = Start_closure(th->descr);
+    modify(&(Start_closure(th->descr)), Val_unit);
+    callback_exn(clos, Val_unit);
+    caml_thread_stop();
+#ifdef NATIVE_CODE
+  }
+#endif
   /* The thread now stops running */
   return NULL;
 }  
@@ -481,6 +509,30 @@ value caml_thread_uncaught_exception(value exn)  /* ML */
   return Val_unit;
 }
 
+/* Terminate current thread */
+
+value caml_thread_exit(value unit)   /* ML */
+{
+#ifdef NATIVE_CODE
+  /* We cannot call pthread_exit here because on some systems this
+     raises a C++ exception, and ocamlopt-generated stack frames
+     cannot be unwound.  Instead, we longjmp to the thread creation
+     point (in caml_thread_start) or to the point in caml_main
+     where caml_termination_hook will be called. */
+  struct longjmp_buffer * exit_buf;
+  if (curr_thread == NULL) invalid_argument("Thread.exit: not initialized");
+  exit_buf = curr_thread->exit_buf;
+  caml_thread_stop();
+  siglongjmp(exit_buf->buf, 1);
+#else
+  /* No such problem in bytecode */
+  if (curr_thread == NULL) invalid_argument("Thread.exit: not initialized");
+  caml_thread_stop();
+  pthread_exit(NULL);
+#endif
+  return Val_unit;  /* not reached */
+}
+
 /* Allow re-scheduling */
 
 value caml_thread_yield(value unit)        /* ML */
-- 
cgit v1.2.3-70-g09d2