+#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
+ * the thread is still alive, so we need a mechanism for post mortem
+ * cleanups. FIXME: We actually have three, for historical reasons as
+ * the saying goes. Do we really need three? Nikodemus guesses that
+ * not anymore, now that we properly call pthread_attr_destroy before
+ * freeing the stack. */
+
+static struct thread_post_mortem *
+plan_thread_post_mortem(struct thread *corpse)
+{
+ if (corpse) {
+ struct thread_post_mortem *post_mortem = malloc(sizeof(struct thread_post_mortem));
+ gc_assert(post_mortem);
+ post_mortem->os_thread = corpse->os_thread;
+ post_mortem->os_attr = corpse->os_attr;
+ post_mortem->os_address = corpse->os_address;
+#ifdef DELAY_THREAD_POST_MORTEM
+ post_mortem->next = NULL;
+#endif
+ return post_mortem;
+ } else {
+ /* FIXME: When does this happen? */
+ return NULL;
+ }
+}
+
+static void
+perform_thread_post_mortem(struct thread_post_mortem *post_mortem)
+{
+#ifdef CREATE_POST_MORTEM_THREAD
+ pthread_detach(pthread_self());
+#endif
+ if (post_mortem) {
+ gc_assert(!pthread_join(post_mortem->os_thread, NULL));
+ gc_assert(!pthread_attr_destroy(post_mortem->os_attr));
+ free(post_mortem->os_attr);
+ os_invalidate(post_mortem->os_address, THREAD_STRUCT_SIZE);
+ free(post_mortem);
+ }
+}
+
+static void
+schedule_thread_post_mortem(struct thread *corpse)
+{
+ struct thread_post_mortem *post_mortem = NULL;
+ if (corpse) {
+ post_mortem = plan_thread_post_mortem(corpse);
+
+#ifdef DELAY_THREAD_POST_MORTEM
+ pthread_mutex_lock(&thread_post_mortem_lock);
+ /* First stick the new post mortem to the end of the queue. */
+ if (pending_thread_post_mortem) {
+ struct thread_post_mortem *next = pending_thread_post_mortem;
+ while (next->next) {
+ next = next->next;
+ }
+ next->next = post_mortem;
+ } else {
+ pending_thread_post_mortem = post_mortem;
+ }
+ /* Then, if there are enough things in the queue, clean up one
+ * from the head -- or increment the count, and null out the
+ * post_mortem we have. */
+ if (pending_thread_post_mortem_count > DELAY_THREAD_POST_MORTEM) {
+ post_mortem = pending_thread_post_mortem;
+ pending_thread_post_mortem = post_mortem->next;
+ } else {
+ pending_thread_post_mortem_count++;
+ post_mortem = NULL;
+ }
+ pthread_mutex_unlock(&thread_post_mortem_lock);
+ /* Finally run, the cleanup, if any. */
+ perform_thread_post_mortem(post_mortem);
+#elif defined(CREATE_POST_MORTEM_THREAD)
+ gc_assert(!pthread_create(&thread, NULL, perform_thread_post_mortem, post_mortem));
+#else
+ post_mortem = (struct thread_post_mortem *)
+ swap_lispobjs((lispobj *)(void *)&pending_thread_post_mortem,
+ (lispobj)post_mortem);
+ perform_thread_post_mortem(post_mortem);
+#endif
+ }
+}
+
+/* this is the first thing that runs in the child (which is why the
+ * silly calling convention). Basically it calls the user's requested
+ * lisp function after doing arch_os_thread_init and whatever other
+ * bookkeeping needs to be done
+ */