Use safepoints for INTERRUPT-THREAD
[sbcl.git] / src / runtime / thread.h
index e1765d7..2a8ea6e 100644 (file)
 #include "os.h"
 #ifdef LISP_FEATURE_GENCGC
 #include "gencgc-alloc-region.h"
-#else
-struct alloc_region { };
 #endif
 #include "genesis/symbol.h"
 #include "genesis/static-symbols.h"
+
 #include "genesis/thread.h"
 #include "genesis/fdefn.h"
 #include "interrupt.h"
+#include "validate.h"           /* for BINDING_STACK_SIZE etc */
 
-#define STATE_RUNNING (make_fixnum(1))
-#define STATE_SUSPENDED (make_fixnum(2))
-#define STATE_DEAD (make_fixnum(3))
+#define STATE_RUNNING MAKE_FIXNUM(1)
+#define STATE_STOPPED MAKE_FIXNUM(2)
+#define STATE_DEAD MAKE_FIXNUM(3)
+#if defined(LISP_FEATURE_SB_SAFEPOINT)
+# define STATE_SUSPENDED_BRIEFLY MAKE_FIXNUM(4)
+# define STATE_GC_BLOCKER MAKE_FIXNUM(5)
+# define STATE_PHASE1_BLOCKER MAKE_FIXNUM(5)
+# define STATE_PHASE2_BLOCKER MAKE_FIXNUM(6)
+# define STATE_INTERRUPT_BLOCKER MAKE_FIXNUM(7)
+#endif
 
 #ifdef LISP_FEATURE_SB_THREAD
+lispobj thread_state(struct thread *thread);
+void set_thread_state(struct thread *thread, lispobj state);
+void wait_for_thread_state_change(struct thread *thread, lispobj state);
 
-/* Only access thread state with blockables blocked. */
-static inline lispobj
-thread_state(struct thread *thread)
-{
-    lispobj state;
-    pthread_mutex_lock(thread->state_lock);
-    state = thread->state;
-    pthread_mutex_unlock(thread->state_lock);
-    return state;
-}
+#if defined(LISP_FEATURE_SB_SAFEPOINT)
+enum threads_suspend_reason {
+    SUSPEND_REASON_NONE,
+    SUSPEND_REASON_GC,
+    SUSPEND_REASON_INTERRUPT,
+    SUSPEND_REASON_GCING
+};
 
-static inline void
-set_thread_state(struct thread *thread, lispobj state)
-{
-    pthread_mutex_lock(thread->state_lock);
-    thread->state = state;
-    pthread_cond_broadcast(thread->state_cond);
-    pthread_mutex_unlock(thread->state_lock);
-}
+struct threads_suspend_info {
+    int suspend;
+    pthread_mutex_t world_lock;
+    pthread_mutex_t lock;
+    enum threads_suspend_reason reason;
+    int phase;
+    struct thread * gc_thread;
+    struct thread * interrupted_thread;
+    int blockers;
+    int used_gc_page;
+};
 
-static inline void
-wait_for_thread_state_change(struct thread *thread, lispobj state)
-{
-    pthread_mutex_lock(thread->state_lock);
-    while (thread->state == state)
-        pthread_cond_wait(thread->state_cond, thread->state_lock);
-    pthread_mutex_unlock(thread->state_lock);
-}
+struct suspend_phase {
+    int suspend;
+    enum threads_suspend_reason reason;
+    int phase;
+    struct suspend_phase *next;
+};
+
+extern struct threads_suspend_info suspend_info;
+
+struct gcing_safety {
+    lispobj csp_around_foreign_call;
+    lispobj* pc_around_foreign_call;
+};
+
+int handle_safepoint_violation(os_context_t *context, os_vm_address_t addr);
+void** os_get_csp(struct thread* th);
+void alloc_gc_page();
+void assert_on_stack(struct thread *th, void *esp);
+#endif /* defined(LISP_FEATURE_SB_SAFEPOINT) */
 
 extern pthread_key_t lisp_thread;
 #endif
@@ -67,6 +88,57 @@ union per_thread_data {
     lispobj dynamic_values[1];  /* actually more like 4000 or so */
 };
 
+/* A helper structure for data local to a thread, which is not pointer-sized.
+ *
+ * Originally, all layouting of these fields was done manually in C code
+ * with pointer arithmetic.  We let the C compiler figure it out now.
+ *
+ * (Why is this not part of `struct thread'?  Because that structure is
+ * declared using genesis, and we would run into issues with fields that
+ * are of unknown length.)
+ */
+struct nonpointer_thread_data
+{
+#ifdef LISP_FEATURE_SB_THREAD
+    os_sem_t state_sem;
+    os_sem_t state_not_running_sem;
+    os_sem_t state_not_stopped_sem;
+# ifdef LISP_FEATURE_SB_SAFEPOINT
+   /* For safepoint-based builds, together with thread's
+    * csp_around_foreign_call pointer target, thread_qrl(thread) makes
+    * `quickly revokable lock'. Unlike most mutexes, this one is
+    * normally locked; by convention, other thread may read and use the
+    * thread's FFI-CSP location _either_ when the former holds the
+    * lock(mutex) _or_ when page permissions for FFI-CSP location were
+    * set to read-only.
+    *
+    * Combined semantic of QRL is not the same as the semantic of mutex
+    * returned by this function; rather, the mutex, when released by the
+    * owning thread, provides an edge-triggered notification of QRL
+    * release, which is represented by writing non-null
+    * csp_around_foreign_call.
+    *
+    * When owner thread is `in Lisp' (i.e. a heap mutator), its FFI-CSP
+    * contains null, otherwise it points to the top of C stack that
+    * should be preserved by GENCGC. If another thread needs to wait for
+    * mutator state change with `in Lisp => in C' direction, it disables
+    * FFI-CSP overwrite using page protection, and takes the mutex
+    * returned by thread_qrl(). Page fault handler normally ends up in a
+    * routine releasing this mutex and waiting for some appropriate
+    * event to take it back.
+    *
+    * This way, each thread may modify its own FFI-CSP content freely
+    * without memory barriers (paying with exception handling overhead
+    * whenever a contention happens). */
+    pthread_mutex_t qrl_lock;
+# endif
+#else
+    /* An unused field follows, to ensure that the struct in non-empty
+     * for non-GCC compilers. */
+    int unused;
+#endif
+};
+
 extern struct thread *all_threads;
 extern int dynamic_values_bytes;
 
@@ -95,7 +167,7 @@ SymbolValueAddress(u64 tagged_symbol_pointer, void *thread)
 #ifdef LISP_FEATURE_SB_THREAD
     if(thread && sym->tls_index) {
         lispobj *r = &(((union per_thread_data *)thread)
-                       ->dynamic_values[fixnum_value(sym->tls_index)]);
+                       ->dynamic_values[(sym->tls_index) >> WORD_SHIFT]);
         if((*r)!=NO_TLS_VALUE_MARKER_WIDETAG) return r;
     }
 #endif
@@ -111,7 +183,7 @@ SymbolValue(u64 tagged_symbol_pointer, void *thread)
     if(thread && sym->tls_index) {
         lispobj r=
             ((union per_thread_data *)thread)
-            ->dynamic_values[fixnum_value(sym->tls_index)];
+            ->dynamic_values[(sym->tls_index) >> WORD_SHIFT];
         if(r!=NO_TLS_VALUE_MARKER_WIDETAG) return r;
     }
 #endif
@@ -125,7 +197,7 @@ SymbolTlValue(u64 tagged_symbol_pointer, void *thread)
         (pointer_sized_uint_t)(tagged_symbol_pointer-OTHER_POINTER_LOWTAG);
 #ifdef LISP_FEATURE_SB_THREAD
     return ((union per_thread_data *)thread)
-        ->dynamic_values[fixnum_value(sym->tls_index)];
+        ->dynamic_values[(sym->tls_index) >> WORD_SHIFT];
 #else
     return sym->value;
 #endif
@@ -139,7 +211,7 @@ SetSymbolValue(u64 tagged_symbol_pointer,lispobj val, void *thread)
 #ifdef LISP_FEATURE_SB_THREAD
     if(thread && sym->tls_index) {
         lispobj *pr= &(((union per_thread_data *)thread)
-                       ->dynamic_values[fixnum_value(sym->tls_index)]);
+                       ->dynamic_values[(sym->tls_index) >> WORD_SHIFT]);
         if(*pr!=NO_TLS_VALUE_MARKER_WIDETAG) {
             *pr=val;
             return;
@@ -156,7 +228,7 @@ SetTlSymbolValue(u64 tagged_symbol_pointer,lispobj val, void *thread)
     struct symbol *sym= (struct symbol *)
         (pointer_sized_uint_t)(tagged_symbol_pointer-OTHER_POINTER_LOWTAG);
     ((union per_thread_data *)thread)
-        ->dynamic_values[fixnum_value(sym->tls_index)]
+        ->dynamic_values[(sym->tls_index) >> WORD_SHIFT]
         =val;
 #else
     SetSymbolValue(tagged_symbol_pointer,val,thread) ;
@@ -188,6 +260,8 @@ StaticSymbolFunction(lispobj sym)
     SymbolValue(BINDING_STACK_POINTER, thread)
 #define set_binding_stack_pointer(thread,value) \
     SetSymbolValue(BINDING_STACK_POINTER, (lispobj)(value), thread)
+#define access_control_stack_pointer(thread)    \
+    (current_control_stack_pointer)
 #else
 #define get_binding_stack_pointer(thread)       \
     (current_binding_stack_pointer)
@@ -203,6 +277,20 @@ StaticSymbolFunction(lispobj sym)
 extern __thread struct thread *current_thread;
 #endif
 
+#ifdef LISP_FEATURE_SB_SAFEPOINT
+# define THREAD_CSP_PAGE_SIZE BACKEND_PAGE_BYTES
+#else
+# define THREAD_CSP_PAGE_SIZE 0
+#endif
+
+#define THREAD_STRUCT_SIZE (thread_control_stack_size + BINDING_STACK_SIZE + \
+                            ALIEN_STACK_SIZE +                          \
+                            sizeof(struct nonpointer_thread_data) +     \
+                            dynamic_values_bytes +                      \
+                            32 * SIGSTKSZ +                             \
+                            THREAD_ALIGNMENT_BYTES +                    \
+                            THREAD_CSP_PAGE_SIZE)
+
 /* This is clearly per-arch and possibly even per-OS code, but we can't
  * put it somewhere sensible like x86-linux-os.c because it needs too
  * much stuff like struct thread and all_threads to be defined, which
@@ -255,10 +343,84 @@ static inline struct thread *arch_os_get_current_thread(void)
 }
 
 #if defined(LISP_FEATURE_MACH_EXCEPTION_HANDLER)
-#define THREAD_STRUCT_TO_EXCEPTION_PORT(th) ((mach_port_t) th)
-#define EXCEPTION_PORT_TO_THREAD_STRUCT(th) ((struct thread *) th)
+extern kern_return_t mach_lisp_thread_init(struct thread *thread);
+extern kern_return_t mach_lisp_thread_destroy(struct thread *thread);
+#endif
+
+#ifdef LISP_FEATURE_SB_SAFEPOINT
+void thread_in_safety_transition(os_context_t *ctx);
+void thread_in_lisp_raised(os_context_t *ctx);
+void thread_interrupted(os_context_t *ctx);
+void thread_pitstop(os_context_t *ctxptr);
+extern void thread_register_gc_trigger();
+
+# ifdef LISP_FEATURE_SB_THRUPTION
+int wake_thread(os_thread_t os_thread);
+int wake_thread_posix(os_thread_t os_thread);
+# endif
+
+#define thread_qrl(th) (&(th)->nonpointer_data->qrl_lock)
+
+static inline
+void push_gcing_safety(struct gcing_safety *into)
+{
+    struct thread* th = arch_os_get_current_thread();
+    asm volatile ("");
+    if ((into->csp_around_foreign_call =
+         *th->csp_around_foreign_call)) {
+        *th->csp_around_foreign_call = 0;
+        asm volatile ("");
+        into->pc_around_foreign_call = th->pc_around_foreign_call;
+        th->pc_around_foreign_call = 0;
+        asm volatile ("");
+    } else {
+        into->pc_around_foreign_call = 0;
+    }
+}
+
+static inline
+void pop_gcing_safety(struct gcing_safety *from)
+{
+    struct thread* th = arch_os_get_current_thread();
+    if (from->csp_around_foreign_call) {
+        asm volatile ("");
+        *th->csp_around_foreign_call = from->csp_around_foreign_call;
+        asm volatile ("");
+        th->pc_around_foreign_call = from->pc_around_foreign_call;
+        asm volatile ("");
+    }
+}
+
+/* Even with just -O1, gcc optimizes the jumps in this "loop" away
+ * entirely, giving the ability to define WITH-FOO-style macros. */
+#define RUN_BODY_ONCE(prefix, finally_do)               \
+    int prefix##done = 0;                               \
+    for (; !prefix##done; finally_do, prefix##done = 1)
+
+#define WITH_GC_AT_SAFEPOINTS_ONLY_hygenic(var)        \
+    struct gcing_safety var;                    \
+    push_gcing_safety(&var);                    \
+    RUN_BODY_ONCE(var, pop_gcing_safety(&var))
+
+#define WITH_GC_AT_SAFEPOINTS_ONLY()                           \
+    WITH_GC_AT_SAFEPOINTS_ONLY_hygenic(sbcl__gc_safety)
+
+#define WITH_STATE_SEM_hygenic(var, thread)                             \
+    os_sem_wait((thread)->state_sem, "thread_state");                   \
+    RUN_BODY_ONCE(var, os_sem_post((thread)->state_sem, "thread_state"))
+
+#define WITH_STATE_SEM(thread)                                     \
+    WITH_STATE_SEM_hygenic(sbcl__state_sem, thread)
+
+int check_pending_thruptions(os_context_t *ctx);
+
 #endif
 
+extern boolean is_some_thread_local_addr(os_vm_address_t addr);
 extern void create_initial_thread(lispobj);
 
+#ifdef LISP_FEATURE_SB_THREAD
+extern pthread_mutex_t all_threads_lock;
+#endif
+
 #endif /* _INCLUDE_THREAD_H_ */