Simplify (and robustify) regular PACKing
[sbcl.git] / src / runtime / thread.h
index 71d3342..1a004c0 100644 (file)
 #include <sys/types.h>
 #include <unistd.h>
 #include <stddef.h>
-#include <errno.h>
-#include <stdio.h>
 #include "sbcl.h"
 #include "globals.h"
 #include "runtime.h"
 #include "os.h"
 #ifdef LISP_FEATURE_GENCGC
 #include "gencgc-alloc-region.h"
-#else
-struct alloc_region { };
+#endif
+#ifdef LISP_FEATURE_WIN32
+#include "win32-thread-private-events.h"
 #endif
 #include "genesis/symbol.h"
 #include "genesis/static-symbols.h"
 
-#ifdef LISP_FEATURE_SB_THREAD
-# ifndef LISP_FEATURE_DARWIN
-#   include <semaphore.h>
-    typedef sem_t os_sem_t;
-# else
-#   include <mach/semaphore.h>
-    typedef semaphore_t os_sem_t;
-# endif
-#endif
-
 #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_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);
+
+#if defined(LISP_FEATURE_SB_SAFEPOINT)
+enum threads_suspend_reason {
+    SUSPEND_REASON_NONE,
+    SUSPEND_REASON_GC,
+    SUSPEND_REASON_INTERRUPT,
+    SUSPEND_REASON_GCING
+};
 
-# ifndef LISP_FEATURE_DARWIN
-
-static inline void
-os_sem_init(os_sem_t *sem, unsigned int value)
-{
-    if (-1==sem_init(sem, 0, value))
-        lose("os_sem_init(%p, %u): %s", sem, value, strerror(errno));
-    FSHOW((stderr, "os_sem_init(%p, %u)\n", sem, value));
-}
-
-static inline void
-os_sem_wait(os_sem_t *sem, char *what)
-{
-    FSHOW((stderr, "%s: os_sem_wait(%p) ...\n", what, sem));
-    while (-1 == sem_wait(sem))
-        if (EINTR!=errno)
-            lose("%s: os_sem_wait(%p): %s", what, sem, strerror(errno));
-    FSHOW((stderr, "%s: os_sem_wait(%p) => ok\n", what, sem));
-}
-
-static inline void
-os_sem_post(sem_t *sem, char *what)
-{
-    if (-1 == sem_post(sem))
-        lose("%s: os_sem_post(%p): %s", what, sem, strerror(errno));
-    FSHOW((stderr, "%s: os_sem_post(%p)\n", what, sem));
-}
-
-static inline void
-os_sem_destroy(os_sem_t *sem)
-{
-    if (-1==sem_destroy(sem))
-        lose("os_sem_destroy(%p): %s", sem, strerror(errno));
-}
-
-# else
-
-static inline void
-os_sem_init(os_sem_t *sem, unsigned int value)
-{
-    if (KERN_SUCCESS!=semaphore_create(current_mach_task, sem, SYNC_POLICY_FIFO, (int)value))
-        lose("os_sem_init(%p): %s", sem, strerror(errno));
-}
-
-static inline void
-os_sem_wait(os_sem_t *sem, char *what)
-{
-    kern_return_t ret;
-  restart:
-    FSHOW((stderr, "%s: os_sem_wait(%p)\n", what, sem));
-    ret = semaphore_wait(*sem);
-    FSHOW((stderr, "%s: os_sem_wait(%p) => %s\n", what, sem,
-           KERN_SUCCESS==ret ? "ok" : strerror(errno)));
-    switch (ret) {
-    case KERN_SUCCESS:
-        return;
-    case KERN_OPERATION_TIMED_OUT:
-        fprintf(stderr, "%s: os_sem_wait(%p): %s", what, sem, strerror(errno));
-        goto restart;
-    default:
-        lose("%s: os_sem_wait(%p): %s", what, sem, strerror(errno));
-    }
-}
-
-static inline void
-os_sem_post(os_sem_t *sem, char *what)
-{
-    if (KERN_SUCCESS!=semaphore_signal(*sem))
-        lose("%s: os_sem_post(%p): %s", what, sem, strerror(errno));
-    FSHOW((stderr, "%s: os_sem_post(%p) ok\n", what, sem));
-}
-
-static inline void
-os_sem_destroy(os_sem_t *sem)
-{
-    if (-1==semaphore_destroy(current_mach_task, *sem))
-        lose("os_sem_destroy(%p): %s", sem, strerror(errno));
-}
+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;
+};
 
-# endif
+struct suspend_phase {
+    int suspend;
+    enum threads_suspend_reason reason;
+    int phase;
+    struct suspend_phase *next;
+};
 
-/* Only access thread state with blockables blocked. */
-static inline lispobj
-thread_state(struct thread *thread)
-{
-    lispobj state;
-    sigset_t old;
-    block_blockable_signals(NULL, &old);
-    os_sem_wait(thread->state_sem, "thread_state");
-    state = thread->state;
-    os_sem_post(thread->state_sem, "thread_state");
-    thread_sigmask(SIG_SETMASK, &old, NULL);
-    return state;
-}
+extern struct threads_suspend_info suspend_info;
 
-static inline void
-set_thread_state(struct thread *thread, lispobj state)
-{
-    int i, waitcount = 0;
-    sigset_t old;
-    block_blockable_signals(NULL, &old);
-    os_sem_wait(thread->state_sem, "set_thread_state");
-    if (thread->state != state) {
-        if ((STATE_STOPPED==state) ||
-            (STATE_DEAD==state)) {
-            waitcount = thread->state_not_running_waitcount;
-            thread->state_not_running_waitcount = 0;
-            for (i=0; i<waitcount; i++)
-                os_sem_post(thread->state_not_running_sem, "set_thread_state (not running)");
-        }
-        if ((STATE_RUNNING==state) ||
-            (STATE_DEAD==state)) {
-            waitcount = thread->state_not_stopped_waitcount;
-            thread->state_not_stopped_waitcount = 0;
-            for (i=0; i<waitcount; i++)
-                os_sem_post(thread->state_not_stopped_sem, "set_thread_state (not stopped)");
-        }
-        thread->state = state;
-    }
-    os_sem_post(thread->state_sem, "set_thread_state");
-    thread_sigmask(SIG_SETMASK, &old, NULL);
-}
+struct gcing_safety {
+    lispobj csp_around_foreign_call;
+#ifdef LISP_FEATURE_C_STACK_IS_CONTROL_STACK
+    lispobj* pc_around_foreign_call;
+#endif
+};
 
-static inline void
-wait_for_thread_state_change(struct thread *thread, lispobj state)
-{
-    sigset_t old;
-    os_sem_t *wait_sem;
-    block_blockable_signals(NULL, &old);
-  start:
-    os_sem_wait(thread->state_sem, "wait_for_thread_state_change");
-    if (thread->state == state) {
-        switch (state) {
-        case STATE_RUNNING:
-            wait_sem = thread->state_not_running_sem;
-            thread->state_not_running_waitcount++;
-            break;
-        case STATE_STOPPED:
-            wait_sem = thread->state_not_stopped_sem;
-            thread->state_not_stopped_waitcount++;
-            break;
-        default:
-            lose("Invalid state in wait_for_thread_state_change: "OBJ_FMTX"\n", state);
-        }
-    } else {
-        wait_sem = NULL;
-    }
-    os_sem_post(thread->state_sem, "wait_for_thread_state_change");
-    if (wait_sem) {
-        os_sem_wait(wait_sem, "wait_for_thread_state_change");
-        goto start;
-    }
-    thread_sigmask(SIG_SETMASK, &old, NULL);
-}
+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
@@ -206,6 +93,28 @@ 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
+{
+#if defined(LISP_FEATURE_SB_THREAD) && !defined(LISP_FEATURE_SB_SAFEPOINT)
+    os_sem_t state_sem;
+    os_sem_t state_not_running_sem;
+    os_sem_t state_not_stopped_sem;
+#else
+    /* An unused field follows, to ensure that the struct is non-empty
+     * for non-GCC compilers. */
+    int unused;
+#endif
+};
+
 extern struct thread *all_threads;
 extern int dynamic_values_bytes;
 
@@ -322,28 +231,62 @@ StaticSymbolFunction(lispobj sym)
 #define access_control_frame_pointer(thread) \
     ((thread)->control_frame_pointer)
 #  endif
-#elif defined(LISP_FEATURE_X86) || defined(LISP_FEATURE_X86_64)
+#else
+#  if defined(BINDING_STACK_POINTER)
 #define get_binding_stack_pointer(thread)       \
     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
+#  else
 #define get_binding_stack_pointer(thread)       \
     (current_binding_stack_pointer)
 #define set_binding_stack_pointer(thread,value) \
     (current_binding_stack_pointer = (lispobj *)(value))
-#define access_control_stack_pointer(thread) \
+#  endif
+#define access_control_stack_pointer(thread)    \
     (current_control_stack_pointer)
+#  if !defined(LISP_FEATURE_X86) && !defined(LISP_FEATURE_X86_64)
 #define access_control_frame_pointer(thread) \
     (current_control_frame_pointer)
+#  endif
 #endif
 
 #if defined(LISP_FEATURE_SB_THREAD) && defined(LISP_FEATURE_GCC_TLS)
 extern __thread struct thread *current_thread;
 #endif
 
+#ifndef LISP_FEATURE_SB_SAFEPOINT
+# define THREAD_CSP_PAGE_SIZE 0
+#elif defined(LISP_FEATURE_PPC)
+  /* BACKEND_PAGE_BYTES is nice and large on this platform, but therefore
+   * does not fit into an immediate, making it awkward to access the page
+   * relative to the thread-tn... */
+# define THREAD_CSP_PAGE_SIZE 4096
+#else
+# define THREAD_CSP_PAGE_SIZE BACKEND_PAGE_BYTES
+#endif
+
+#ifdef LISP_FEATURE_WIN32
+/*
+ * Win32 doesn't have SIGSTKSZ, and we're not switching stacks anyway,
+ * so define it arbitrarily
+ */
+#define SIGSTKSZ 1024
+#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)
+
+#if defined(LISP_FEATURE_WIN32)
+static inline struct thread* arch_os_get_current_thread()
+    __attribute__((__const__));
+#endif
+
 /* 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
@@ -351,55 +294,145 @@ extern __thread struct thread *current_thread;
 
 static inline struct thread *arch_os_get_current_thread(void)
 {
-#if defined(LISP_FEATURE_SB_THREAD)
-#if defined(LISP_FEATURE_X86)
+#if !defined(LISP_FEATURE_SB_THREAD)
+     return all_threads;
+
+#elif defined(LISP_FEATURE_X86) && defined(LISP_FEATURE_WIN32)
     register struct thread *me=0;
-    if(all_threads) {
-#if defined(LISP_FEATURE_DARWIN) && defined(LISP_FEATURE_RESTORE_FS_SEGMENT_REGISTER_FROM_TLS)
-        sel_t sel;
-        struct thread *th = pthread_getspecific(specials);
-        sel.index = th->tls_cookie;
-        sel.rpl = USER_PRIV;
-        sel.ti = SEL_LDT;
-        __asm__ __volatile__ ("movw %w0, %%fs" : : "r"(sel));
-#elif defined(LISP_FEATURE_FREEBSD)
-#ifdef LISP_FEATURE_GCC_TLS
-        struct thread *th = current_thread;
+    __asm__ ("movl %%fs:0xE10+(4*63), %0" : "=r"(me) :);
+    return me;
+
 #else
-        struct thread *th = pthread_getspecific(specials);
+
+# if defined(LISP_FEATURE_X86)
+    if (!all_threads) return 0;
 #endif
-#ifdef LISP_FEATURE_RESTORE_TLS_SEGMENT_REGISTER_FROM_TLS
-        unsigned int sel = LSEL(th->tls_cookie, SEL_UPL);
-        unsigned int fs = rfs();
-
-        /* Load FS only if it's necessary.  Modifying a selector
-         * causes privilege checking and it takes long time. */
-        if (fs != sel)
-            load_fs(sel);
+
+    /* Otherwise, use pthreads to find the right value.  We do not load
+     * directly from %fs:this even on x86 platforms (like Linux and
+     * Solaris) with dependable %fs, because we want to return NULL if
+     * called by a non-Lisp thread, and %fs would not be initialized
+     * suitably in that case. */
+    struct thread *th;
+# ifdef LISP_FEATURE_GCC_TLS
+    th = current_thread;
+# else
+    th = pthread_getspecific(specials);
+# endif
+
+# if defined(LISP_FEATURE_RESTORE_FS_SEGMENT_REGISTER_FROM_TLS)
+    /* If enabled by make-config (currently Darwin and FreeBSD only),
+     * re-setup %fs.  This is an out-of-line call, and potentially
+     * expensive.*/
+    if (th)
+        arch_os_load_ldt(th);
+# endif
+
+    return th;
 #endif
-        return th;
+}
+
+#if defined(LISP_FEATURE_MACH_EXCEPTION_HANDLER)
+extern kern_return_t mach_lisp_thread_init(struct thread *thread);
+extern kern_return_t mach_lisp_thread_destroy(struct thread *thread);
 #endif
-        __asm__ __volatile__ ("movl %%fs:%c1,%0" : "=r" (me)
-                 : "i" (offsetof (struct thread,this)));
-    }
-    return me;
-#else
-#ifdef LISP_FEATURE_GCC_TLS
-    return current_thread;
-#else
-    return pthread_getspecific(specials);
+
+typedef struct init_thread_data {
+    sigset_t oldset;
+#ifdef LISP_FEATURE_SB_SAFEPOINT
+    struct gcing_safety safety;
 #endif
-#endif /* x86 */
-#else
-     return all_threads;
+} init_thread_data;
+
+#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);
+#  ifdef LISP_FEATURE_WIN32
+void wake_thread_win32(struct thread *thread);
+#  else
+int wake_thread_posix(os_thread_t os_thread);
+#  endif
+# endif
+
+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 ("");
+#ifdef LISP_FEATURE_C_STACK_IS_CONTROL_STACK
+        into->pc_around_foreign_call = th->pc_around_foreign_call;
+        th->pc_around_foreign_call = 0;
+        asm volatile ("");
+#endif
+    } else {
+#ifdef LISP_FEATURE_C_STACK_IS_CONTROL_STACK
+        into->pc_around_foreign_call = 0;
 #endif
+    }
 }
 
-#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)
+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 ("");
+#ifdef LISP_FEATURE_C_STACK_IS_CONTROL_STACK
+        th->pc_around_foreign_call = from->pc_around_foreign_call;
+        asm volatile ("");
+#endif
+    }
+}
+
+/* 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);
+
+void attach_os_thread(init_thread_data *);
+void detach_os_thread(init_thread_data *);
+
+# if defined(LISP_FEATURE_SB_SAFEPOINT_STRICTLY) && !defined(LISP_FEATURE_WIN32)
+
+void signal_handler_callback(lispobj, int, void *, void *);
+# endif
+
 #endif
 
 extern void create_initial_thread(lispobj);
 
+#ifdef LISP_FEATURE_SB_THREAD
+extern pthread_mutex_t all_threads_lock;
+#endif
+
 #endif /* _INCLUDE_THREAD_H_ */