#include "interr.h" /* for lose() */
#include "alloc.h"
#include "gc-internal.h"
+#include "cpputil.h"
+#include "pseudo-atomic.h"
+#include "interrupt.h"
+#include "lispregs.h"
#ifdef LISP_FEATURE_WIN32
/*
#ifdef LISP_FEATURE_SB_THREAD
pthread_setspecific(lisp_thread, (void *)1);
#endif
-#if defined(LISP_FEATURE_SB_THREAD) && defined(LISP_FEATURE_PPC)
+#if defined(THREADS_USING_GCSIGNAL) && defined(LISP_FEATURE_PPC)
/* SIG_STOP_FOR_GC defaults to blocked on PPC? */
unblock_gc_signals(0,0);
#endif
function = th->no_tls_value_marker;
th->no_tls_value_marker = NO_TLS_VALUE_MARKER_WIDETAG;
if(arch_os_thread_init(th)==0) return 1;
+#ifdef LISP_FEATURE_SB_SAFEPOINT
+ pthread_mutex_lock(thread_qrl(th));
+#endif
link_thread(th);
th->os_thread=thread_self();
#ifndef LISP_FEATURE_WIN32
}
#ifdef LISP_FEATURE_SB_THREAD
-#define THREAD_STATE_LOCK_SIZE \
- ((sizeof(os_sem_t))+(sizeof(os_sem_t))+(sizeof(os_sem_t)))
-#else
-#define THREAD_STATE_LOCK_SIZE 0
-#endif
-
-#define THREAD_STRUCT_SIZE (thread_control_stack_size + BINDING_STACK_SIZE + \
- ALIEN_STACK_SIZE + \
- THREAD_STATE_LOCK_SIZE + \
- dynamic_values_bytes + \
- 32 * SIGSTKSZ + \
- THREAD_ALIGNMENT_BYTES)
-
-#ifdef LISP_FEATURE_SB_THREAD
/* THREAD POST MORTEM CLEANUP
*
* Memory allocated for the thread stacks cannot be reclaimed while
FSHOW((stderr,"/creating thread %lu\n", thread_self()));
check_deferrables_blocked_or_lose(0);
+#ifndef LISP_FEATURE_SB_SAFEPOINT
check_gc_signals_unblocked_or_lose(0);
+#endif
pthread_setspecific(lisp_thread, (void *)1);
function = th->no_tls_value_marker;
th->no_tls_value_marker = NO_TLS_VALUE_MARKER_WIDETAG;
* list and we're just adding this thread to it, there is no
* danger of deadlocking even with SIG_STOP_FOR_GC blocked (which
* it is not). */
+#ifdef LISP_FEATURE_SB_SAFEPOINT
+ *th->csp_around_foreign_call = (lispobj)&function;
+ pthread_mutex_lock(thread_qrl(th));
+#endif
lock_ret = pthread_mutex_lock(&all_threads_lock);
gc_assert(lock_ret == 0);
link_thread(th);
lock_ret = pthread_mutex_unlock(&all_threads_lock);
gc_assert(lock_ret == 0);
+ /* Kludge: Changed the order of some steps between the safepoint/
+ * non-safepoint versions of this code. Can we unify this more?
+ */
+#ifdef LISP_FEATURE_SB_SAFEPOINT
+ WITH_GC_AT_SAFEPOINTS_ONLY() {
+ result = funcall0(function);
+ block_blockable_signals(0, 0);
+ gc_alloc_update_page_tables(BOXED_PAGE_FLAG, &th->alloc_region);
+ }
+ lock_ret = pthread_mutex_lock(&all_threads_lock);
+ gc_assert(lock_ret == 0);
+ unlink_thread(th);
+ lock_ret = pthread_mutex_unlock(&all_threads_lock);
+ gc_assert(lock_ret == 0);
+ pthread_mutex_unlock(thread_qrl(th));
+ set_thread_state(th,STATE_DEAD);
+#else
result = funcall0(function);
/* Block GC */
unlink_thread(th);
pthread_mutex_unlock(&all_threads_lock);
gc_assert(lock_ret == 0);
+#endif
if(th->tls_cookie>=0) arch_os_thread_cleanup(th);
os_sem_destroy(th->state_sem);
(sizeof (struct interrupt_data)));
#ifdef LISP_FEATURE_MACH_EXCEPTION_HANDLER
- FSHOW((stderr, "Deallocating mach port %x\n", THREAD_STRUCT_TO_EXCEPTION_PORT(th)));
- mach_port_move_member(current_mach_task,
- THREAD_STRUCT_TO_EXCEPTION_PORT(th),
- MACH_PORT_NULL);
- mach_port_deallocate(current_mach_task,
- THREAD_STRUCT_TO_EXCEPTION_PORT(th));
- mach_port_destroy(current_mach_task,
- THREAD_STRUCT_TO_EXCEPTION_PORT(th));
+ mach_lisp_thread_destroy(th);
#endif
schedule_thread_post_mortem(th);
aligned_spaces = (void *)((((unsigned long)(char *)spaces)
+ THREAD_ALIGNMENT_BYTES-1)
&~(unsigned long)(THREAD_ALIGNMENT_BYTES-1));
- per_thread=(union per_thread_data *)
+ void* csp_page=
(aligned_spaces+
thread_control_stack_size+
BINDING_STACK_SIZE+
- ALIEN_STACK_SIZE +
- THREAD_STATE_LOCK_SIZE);
+ ALIEN_STACK_SIZE);
+ per_thread=(union per_thread_data *)
+ (csp_page + THREAD_CSP_PAGE_SIZE);
+ struct nonpointer_thread_data *nonpointer_data
+ = (void *) &per_thread->dynamic_values[TLS_SIZE];
#ifdef LISP_FEATURE_SB_THREAD
for(i = 0; i < (dynamic_values_bytes / sizeof(lispobj)); i++)
set_binding_stack_pointer(th,th->binding_stack_start);
th->this=th;
th->os_thread=0;
+
+#ifdef LISP_FEATURE_SB_SAFEPOINT
+ th->pc_around_foreign_call = 0;
+ th->csp_around_foreign_call = csp_page;
+#endif
+
#ifdef LISP_FEATURE_SB_THREAD
+ /* Contrary to the "allocate all the spaces at once" comment above,
+ * the os_attr is allocated separately. We cannot put it into the
+ * nonpointer data, because it's used for post_mortem and freed
+ * separately */
th->os_attr=malloc(sizeof(pthread_attr_t));
- th->state_sem=(os_sem_t *)((void *)th->alien_stack_start + ALIEN_STACK_SIZE);
- th->state_not_running_sem=(os_sem_t *)
- ((void *)th->state_sem + (sizeof(os_sem_t)));
- th->state_not_stopped_sem=(os_sem_t *)
- ((void *)th->state_not_running_sem + (sizeof(os_sem_t)));
+ th->nonpointer_data = nonpointer_data;
+ th->state_sem=&nonpointer_data->state_sem;
+ th->state_not_running_sem=&nonpointer_data->state_not_running_sem;
+ th->state_not_stopped_sem=&nonpointer_data->state_not_stopped_sem;
th->state_not_running_waitcount = 0;
th->state_not_stopped_waitcount = 0;
os_sem_init(th->state_sem, 1);
os_sem_init(th->state_not_running_sem, 0);
os_sem_init(th->state_not_stopped_sem, 0);
+# ifdef LISP_FEATURE_SB_SAFEPOINT
+ pthread_mutex_init(thread_qrl(th), NULL);
+# endif
#endif
th->state=STATE_RUNNING;
#ifdef LISP_FEATURE_STACK_GROWS_DOWNWARD_NOT_UPWARD
#ifdef LISP_FEATURE_SB_THREAD
bind_variable(STOP_FOR_GC_PENDING,NIL,th);
#endif
+#if defined(LISP_FEATURE_SB_SAFEPOINT)
+ bind_variable(GC_SAFE,NIL,th);
+ bind_variable(IN_SAFEPOINT,NIL,th);
+#endif
+#ifdef LISP_FEATURE_SB_THRUPTION
+ bind_variable(THRUPTION_PENDING,NIL,th);
+ bind_variable(RESTART_CLUSTERS,NIL,th);
+#endif
#ifndef LISP_FEATURE_C_STACK_IS_CONTROL_STACK
access_control_stack_pointer(th)=th->control_stack_start;
#endif
return th;
}
-#ifdef LISP_FEATURE_MACH_EXCEPTION_HANDLER
-mach_port_t setup_mach_exception_handling_thread();
-kern_return_t mach_thread_init(mach_port_t thread_exception_port);
-
-#endif
-
void create_initial_thread(lispobj initial_function) {
struct thread *th=create_thread_struct(initial_function);
#ifdef LISP_FEATURE_SB_THREAD
* the usual pseudo-atomic checks (we don't want to stop a thread while
* it's in the middle of allocation) then waits for another SIG_STOP_FOR_GC.
*/
+/*
+ * (With SB-SAFEPOINT, see the definitions in safepoint.c instead.)
+ */
+#ifndef LISP_FEATURE_SB_SAFEPOINT
/* To avoid deadlocks when gc stops the world all clients of each
* mutex must enable or disable SIG_STOP_FOR_GC for the duration of
FSHOW_SIGNAL((stderr,"/gc_start_the_world:end\n"));
}
-#endif
+
+#endif /* !LISP_FEATURE_SB_SAFEPOINT */
+#endif /* !LISP_FEATURE_SB_THREAD */
int
thread_yield()
#endif
}
+int
+wake_thread(os_thread_t os_thread)
+{
+#ifdef LISP_FEATURE_WIN32
+# define SIGPIPE 1
+#endif
+#if !defined(LISP_FEATURE_SB_THRUPTION) || defined(LISP_FEATURE_WIN32)
+ return kill_safely(os_thread, SIGPIPE);
+#else
+ return wake_thread_posix(os_thread);
+#endif
+}
+
/* If the thread id given does not belong to a running thread (it has
* exited or never even existed) pthread_kill _may_ fail with ESRCH,
* but it is also allowed to just segfault, see
* (NPTL recycles them extremely fast) so a signal can be sent to
* another process if the one it was sent to exited.
*
- * We send signals in two places: signal_interrupt_thread sends a
- * signal that's harmless if delivered to another thread, but
- * SIG_STOP_FOR_GC is fatal.
- *
* For these reasons, we must make sure that the thread is still alive
* when the pthread_kill is called and return if the thread is
- * exiting. */
+ * exiting.
+ *
+ * Note (DFL, 2011-06-22): At the time of writing, this function is only
+ * used for INTERRUPT-THREAD, hence the wake_thread special-case for
+ * Windows is OK. */
int
kill_safely(os_thread_t os_thread, int signal)
{
#ifdef LISP_FEATURE_SB_THREAD
sigset_t oldset;
struct thread *thread;
+ /* Frequent special case: resignalling to self. The idea is
+ * that leave_region safepoint will acknowledge the signal, so
+ * there is no need to take locks, roll thread to safepoint
+ * etc. */
+ /* Kludge (on safepoint builds): At the moment, this isn't just
+ * an optimization; rather it masks the fact that
+ * gc_stop_the_world() grabs the all_threads mutex without
+ * releasing it, and since we're not using recursive pthread
+ * mutexes, the pthread_mutex_lock() around the all_threads loop
+ * would go wrong. Why are we running interruptions while
+ * stopping the world though? Test case is (:ASYNC-UNWIND
+ * :SPECIALS), especially with s/10/100/ in both loops. */
+ if (os_thread == pthread_self()) {
+ pthread_kill(os_thread, signal);
+ return 0;
+ }
+
/* pthread_kill is not async signal safe and we don't want to be
* interrupted while holding the lock. */
block_deferrable_signals(0, &oldset);