Add a new thread.c file.
Move selected globals to a thread-specific structure.
Add elisp functions run-in-thread and yield.
Update GC to work with threads.
eval.o floatfns.o fns.o font.o print.o lread.o \
syntax.o UNEXEC bytecode.o \
process.o callproc.o \
- region-cache.o sound.o atimer.o \
+ region-cache.o sound.o atimer.o thread.o \
doprnt.o strftime.o intervals.o textprop.o composite.o md5.o \
$(MSDOS_OBJ) $(NS_OBJ) $(CYGWIN_OBJ) $(FONT_DRIVERS)
syssignal.h $(INTERVALS_H) buffer.h
termcap.o: termcap.c lisp.h $(config_h)
terminal.o: terminal.c frame.h termchar.h termhooks.h charset.h coding.h \
- keyboard.h lisp.h $(config_h) dispextern.h composite.h systime.h
-terminfo.o: terminfo.c lisp.h $(config_h)
-tparam.o: tparam.c lisp.h $(config_h)
-undo.o: undo.c buffer.h commands.h window.h dispextern.h lisp.h $(config_h)
-unexaix.o: unexaix.c lisp.h $(config_h)
+ keyboard.h $(config_h)
+terminfo.o: terminfo.c $(config_h)
+thread.o: thread.c $(config_h) lisp.h
+tparam.o: tparam.c $(config_h)
+undo.o: undo.c buffer.h commands.h window.h $(config_h)
+unexaix.o: unexaix.c $(config_h)
unexalpha.o: unexalpha.c $(config_h)
unexcw.o: unexcw.c lisp.h $(config_h)
unexec.o: unexec.c lisp.h $(config_h)
static void mark_terminals P_ ((void));
extern void mark_kboards P_ ((void));
extern void mark_ttys P_ ((void));
-extern void mark_backtrace P_ ((void));
+extern void mark_threads P_ ((void));
static void gc_sweep P_ ((void));
static void mark_glyph_matrix P_ ((struct glyph_matrix *));
static void mark_face_cache P_ ((struct face_cache *));
enum mem_type type;
};
-/* Base address of stack. Set in main. */
-
-Lisp_Object *stack_base;
-
/* Root of the tree describing allocated Lisp memory. */
static struct mem_node *mem_root;
static POINTER_TYPE *lisp_malloc P_ ((size_t, enum mem_type));
static struct Lisp_Vector *allocate_vectorlike P_ ((EMACS_INT));
static void lisp_free P_ ((POINTER_TYPE *));
-static void mark_stack P_ ((void));
static int live_vector_p P_ ((struct mem_node *, void *));
static int live_buffer_p P_ ((struct mem_node *, void *));
static int live_string_p P_ ((struct mem_node *, void *));
#endif /* GC_MARK_STACK || GC_MALLOC_CHECK */
-/* Recording what needs to be marked for gc. */
-
-struct gcpro *gcprolist;
-
/* Addresses of staticpro'd variables. Initialize it to a nonzero
value; otherwise some compilers put it into BSS. */
The current code assumes by default that Lisp_Objects are aligned
equally on the stack. */
-static void
-mark_stack ()
+void
+mark_stack (bottom, end)
+ char *bottom;
+ char *end;
{
int i;
+
+ /* This assumes that the stack is a contiguous region in memory. If
+ that's not the case, something has to be done here to iterate
+ over the stack segments. */
+#ifndef GC_LISP_OBJECT_ALIGNMENT
+#ifdef __GNUC__
+#define GC_LISP_OBJECT_ALIGNMENT __alignof__ (Lisp_Object)
+#else
+#define GC_LISP_OBJECT_ALIGNMENT sizeof (Lisp_Object)
+#endif
+#endif
+ for (i = 0; i < sizeof (Lisp_Object); i += GC_LISP_OBJECT_ALIGNMENT)
+ mark_memory (bottom, end, i);
+ /* Allow for marking a secondary stack, like the register stack on the
+ ia64. */
+#ifdef GC_MARK_SECONDARY_STACK
+ GC_MARK_SECONDARY_STACK ();
+#endif
+
+#if GC_MARK_STACK == GC_MARK_STACK_CHECK_GCPROS
+ check_gcpros ();
+#endif
+}
+
+#endif /* GC_MARK_STACK != 0 */
+
+void
+flush_stack_call_func (func)
+ void (*func) P_ ((char *end));
+{
+#if GC_MARK_STACK
/* jmp_buf may not be aligned enough on darwin-ppc64 */
union aligned_jmpbuf {
Lisp_Object o;
jmp_buf j;
} j;
- volatile int stack_grows_down_p = (char *) &j > (char *) stack_base;
+ volatile int stack_grows_down_p = (char *) &j > (char *) current_thread->stack_bottom;
void *end;
/* This trick flushes the register windows so that all the state of
setjmp (j.j);
end = stack_grows_down_p ? (char *) &j + sizeof j : (char *) &j;
#endif /* not GC_SAVE_REGISTERS_ON_STACK */
+#endif /* GC_MARK_STACK != 0 */
- /* This assumes that the stack is a contiguous region in memory. If
- that's not the case, something has to be done here to iterate
- over the stack segments. */
-#ifndef GC_LISP_OBJECT_ALIGNMENT
-#ifdef __GNUC__
-#define GC_LISP_OBJECT_ALIGNMENT __alignof__ (Lisp_Object)
-#else
-#define GC_LISP_OBJECT_ALIGNMENT sizeof (Lisp_Object)
-#endif
-#endif
- for (i = 0; i < sizeof (Lisp_Object); i += GC_LISP_OBJECT_ALIGNMENT)
- mark_memory (stack_base, end, i);
- /* Allow for marking a secondary stack, like the register stack on the
- ia64. */
-#ifdef GC_MARK_SECONDARY_STACK
- GC_MARK_SECONDARY_STACK ();
-#endif
-
-#if GC_MARK_STACK == GC_MARK_STACK_CHECK_GCPROS
- check_gcpros ();
-#endif
+ (*func) (end);
}
-#endif /* GC_MARK_STACK != 0 */
-
/* Determine whether it is safe to access memory at address P. */
static int
()
{
register struct specbinding *bind;
- struct catchtag *catch;
- struct handler *handler;
char stack_top_variable;
register int i;
int message_p;
#if MAX_SAVE_STACK > 0
if (NILP (Vpurify_flag))
{
- i = &stack_top_variable - stack_bottom;
+ i = &stack_top_variable - /*FIXME*/current_thread->stack_bottom;
if (i < 0) i = -i;
if (i < MAX_SAVE_STACK)
{
stack_copy = (char *) xrealloc (stack_copy, (stack_copy_size = i));
if (stack_copy)
{
- if ((EMACS_INT) (&stack_top_variable - stack_bottom) > 0)
- bcopy (stack_bottom, stack_copy, i);
+ if ((EMACS_INT) (&stack_top_variable - /*FIXME*/current_thread->stack_bottom) > 0)
+ bcopy (/*FIXME*/current_thread->stack_bottom, stack_copy, i);
else
bcopy (&stack_top_variable, stack_copy, i);
}
for (i = 0; i < staticidx; i++)
mark_object (*staticvec[i]);
- for (bind = specpdl; bind != specpdl_ptr; bind++)
- {
- mark_object (bind->symbol);
- mark_object (bind->old_value);
- }
+ mark_threads ();
mark_terminals ();
mark_kboards ();
mark_ttys ();
}
#endif
-#if (GC_MARK_STACK == GC_MAKE_GCPROS_NOOPS \
- || GC_MARK_STACK == GC_MARK_STACK_CHECK_GCPROS)
- mark_stack ();
-#else
- {
- register struct gcpro *tail;
- for (tail = gcprolist; tail; tail = tail->next)
- for (i = 0; i < tail->nvars; i++)
- mark_object (tail->var[i]);
- }
-#endif
-
- mark_byte_stack ();
- for (catch = catchlist; catch; catch = catch->next)
- {
- mark_object (catch->tag);
- mark_object (catch->val);
- }
- for (handler = handlerlist; handler; handler = handler->next)
- {
- mark_object (handler->handler);
- mark_object (handler->var);
- }
- mark_backtrace ();
-
#ifdef HAVE_WINDOW_SYSTEM
mark_fringe_data ();
#endif
#if GC_MARK_STACK == GC_USE_GCPROS_CHECK_ZOMBIES
+ FIXME;
mark_stack ();
#endif
/* Clear the mark bits that we set in certain root slots. */
- unmark_byte_stack ();
+ unmark_threads ();
VECTOR_UNMARK (&buffer_defaults);
VECTOR_UNMARK (&buffer_local_symbols);
struct byte_stack *next;
};
-/* A list of currently active byte-code execution value stacks.
- Fbyte_code adds an entry to the head of this list before it starts
- processing byte-code, and it removed the entry again when it is
- done. Signalling an error truncates the list analoguous to
- gcprolist. */
-
-struct byte_stack *byte_stack_list;
-
\f
/* Mark objects on byte_stack_list. Called during GC. */
void
-mark_byte_stack ()
+mark_byte_stack (struct byte_stack *stack)
{
- struct byte_stack *stack;
Lisp_Object *obj;
- for (stack = byte_stack_list; stack; stack = stack->next)
+ for (; stack; stack = stack->next)
{
/* If STACK->top is null here, this means there's an opcode in
Fbyte_code that wasn't expected to GC, but did. To find out
counters. Called when GC has completed. */
void
-unmark_byte_stack ()
+unmark_byte_stack (struct byte_stack *stack)
{
- struct byte_stack *stack;
-
- for (stack = byte_stack_list; stack; stack = stack->next)
+ for (; stack; stack = stack->next)
{
if (stack->byte_string_start != SDATA (stack->byte_string))
{
int display_arg;
#endif
-/* An address near the bottom of the stack.
- Tells GC how to save a copy of the stack. */
-char *stack_bottom;
+#ifdef HAVE_NS
+extern char ns_no_defaults;
+#endif
/* The address where the heap starts (from the first sbrk (0) call). */
static void *my_heap_start;
int
main (int argc, char **argv)
{
-#if GC_MARK_STACK
- Lisp_Object dummy;
-#endif
char stack_bottom_variable;
int do_initial_setlocale;
int skip_args = 0;
char dname_arg2[80];
#endif
-#if GC_MARK_STACK
- extern Lisp_Object *stack_base;
- stack_base = &dummy;
-#endif
+ /* Record (approximately) where the stack begins. */
+ current_thread->stack_bottom = &stack_bottom_variable;
#if defined (USE_GTK) && defined (G_SLICE_ALWAYS_MALLOC)
/* This is used by the Cygwin build. */
}
#endif /* HAVE_SETRLIMIT and RLIMIT_STACK */
- /* Record (approximately) where the stack begins. */
- stack_bottom = &stack_bottom_variable;
-
clearerr (stdin);
#ifndef SYSTEM_MALLOC
init_callproc (); /* Must follow init_cmdargs but not init_sys_modes. */
init_lread ();
+ init_threads ();
+
/* Intern the names of all standard functions and variables;
define standard keys. */
SYMS_MACHINE;
#endif
+ syms_of_threads ();
+
keys_of_casefiddle ();
keys_of_cmds ();
keys_of_buffer ();
char debug_on_exit;
};
-struct backtrace *backtrace_list;
-
-struct catchtag *catchlist;
-
-#ifdef DEBUG_GCPRO
-/* Count levels of GCPRO to detect failure to UNGCPRO. */
-int gcpro_level;
-#endif
-
Lisp_Object Qautoload, Qmacro, Qexit, Qinteractive, Qcommandp, Qdefun;
Lisp_Object Qinhibit_quit, impl_Vinhibit_quit, impl_Vquit_flag;
Lisp_Object Qand_rest, Qand_optional;
Lisp_Object Vautoload_queue;
-/* Current number of specbindings allocated in specpdl. */
-
-int specpdl_size;
-
-/* Pointer to beginning of specpdl. */
-
-struct specbinding *specpdl;
-
-/* Pointer to first unused element in specpdl. */
-
-struct specbinding *specpdl_ptr;
-
/* Maximum size allowed for specpdl allocation */
EMACS_INT max_specpdl_size;
-/* Depth in Lisp evaluations and function calls. */
-
-int lisp_eval_depth;
-
/* Maximum allowed depth in Lisp evaluations and function calls. */
EMACS_INT max_lisp_eval_depth;
when_entered_debugger = -1;
}
+void
+mark_catchlist (struct catchtag *catch)
+{
+ for (; catch; catch = catch->next)
+ {
+ mark_object (catch->tag);
+ mark_object (catch->val);
+ }
+}
+
/* unwind-protect function used by call_debugger. */
static Lisp_Object
c.tag = tag;
c.val = Qnil;
c.backlist = backtrace_list;
- c.handlerlist = handlerlist;
- c.lisp_eval_depth = lisp_eval_depth;
+ c.m_handlerlist = handlerlist;
+ c.m_lisp_eval_depth = lisp_eval_depth;
c.pdlcount = SPECPDL_INDEX ();
c.poll_suppress_count = poll_suppress_count;
c.interrupt_input_blocked = interrupt_input_blocked;
/* Unwind the specpdl stack, and then restore the proper set of
handlers. */
unbind_to (catchlist->pdlcount, Qnil);
- handlerlist = catchlist->handlerlist;
+ handlerlist = catchlist->m_handlerlist;
catchlist = catchlist->next;
}
while (! last_time);
gcpro_level = 0;
#endif
backtrace_list = catch->backlist;
- lisp_eval_depth = catch->lisp_eval_depth;
+ lisp_eval_depth = catch->m_lisp_eval_depth;
_longjmp (catch->jmp, 1);
}
return unbind_to (count, val);
}
\f
-/* Chain of condition handlers currently in effect.
- The elements of this chain are contained in the stack frames
- of Fcondition_case and internal_condition_case.
- When an error is signaled (by calling Fsignal, below),
- this chain is searched for an element that applies. */
-
-struct handler *handlerlist;
-
DEFUN ("condition-case", Fcondition_case, Scondition_case, 2, UNEVALLED, 0,
doc: /* Regain control when an error is signaled.
Executes BODYFORM and returns its value if no error happens.
c.tag = Qnil;
c.val = Qnil;
c.backlist = backtrace_list;
- c.handlerlist = handlerlist;
- c.lisp_eval_depth = lisp_eval_depth;
+ c.m_handlerlist = handlerlist;
+ c.m_lisp_eval_depth = lisp_eval_depth;
c.pdlcount = SPECPDL_INDEX ();
c.poll_suppress_count = poll_suppress_count;
c.interrupt_input_blocked = interrupt_input_blocked;
c.tag = Qnil;
c.val = Qnil;
c.backlist = backtrace_list;
- c.handlerlist = handlerlist;
- c.lisp_eval_depth = lisp_eval_depth;
+ c.m_handlerlist = handlerlist;
+ c.m_lisp_eval_depth = lisp_eval_depth;
c.pdlcount = SPECPDL_INDEX ();
c.poll_suppress_count = poll_suppress_count;
c.interrupt_input_blocked = interrupt_input_blocked;
c.tag = Qnil;
c.val = Qnil;
c.backlist = backtrace_list;
- c.handlerlist = handlerlist;
- c.lisp_eval_depth = lisp_eval_depth;
+ c.m_handlerlist = handlerlist;
+ c.m_lisp_eval_depth = lisp_eval_depth;
c.pdlcount = SPECPDL_INDEX ();
c.poll_suppress_count = poll_suppress_count;
c.interrupt_input_blocked = interrupt_input_blocked;
c.tag = Qnil;
c.val = Qnil;
c.backlist = backtrace_list;
- c.handlerlist = handlerlist;
- c.lisp_eval_depth = lisp_eval_depth;
+ c.m_handlerlist = handlerlist;
+ c.m_lisp_eval_depth = lisp_eval_depth;
c.pdlcount = SPECPDL_INDEX ();
c.poll_suppress_count = poll_suppress_count;
c.interrupt_input_blocked = interrupt_input_blocked;
\f
void
-mark_backtrace ()
+mark_backtrace (struct backtrace *backlist)
{
- register struct backtrace *backlist;
register int i;
- for (backlist = backtrace_list; backlist; backlist = backlist->next)
+ for (; backlist; backlist = backlist->next)
{
mark_object (*backlist->function);
Lisp_Object unused; /* Dividing by 16 is faster than by 12 */
};
-extern struct specbinding *specpdl;
-extern struct specbinding *specpdl_ptr;
-extern int specpdl_size;
-
extern EMACS_INT max_specpdl_size;
#define SPECPDL_INDEX() (specpdl_ptr - specpdl)
extern Lisp_Object memory_signal_data;
-/* An address near the bottom of the stack.
- Tells GC how to save a copy of the stack. */
-extern char *stack_bottom;
-
/* Check quit-flag and quit if it is non-nil.
Typing C-g does not directly cause a quit; it only sets Vquit_flag.
So the program needs to do QUIT at times when it is safe to quit.
Every function that can call Feval must protect in this fashion all
Lisp_Object variables whose contents will be used again. */
-extern struct gcpro *gcprolist;
-
struct gcpro
{
struct gcpro *next;
#else
-extern int gcpro_level;
-
#define GCPRO1(varname) \
{gcpro1.next = gcprolist; gcpro1.var = &varname; gcpro1.nvars = 1; \
gcpro1.level = gcpro_level++; \
extern Lisp_Object Qbytecode;
EXFUN (Fbyte_code, 3);
extern void syms_of_bytecode P_ ((void));
-extern struct byte_stack *byte_stack_list;
-extern void mark_byte_stack P_ ((void));
-extern void unmark_byte_stack P_ ((void));
/* defined in macros.c */
extern Lisp_Object Qexecute_kbd_macro;
#include "globals.h"
+#include "thread.h"
+
#endif /* EMACS_LISP_H */
/* arch-tag: 9b2ed020-70eb-47ac-94ee-e1c2a5107d5e
--- /dev/null
+
+#include <config.h>
+#include "lisp.h"
+#include <pthread.h>
+
+void mark_byte_stack P_ ((struct byte_stack *));
+void mark_backtrace P_ ((struct backtrace *));
+void mark_catchlist P_ ((struct catchtag *));
+void mark_stack P_ ((char *, char *));
+void flush_stack_call_func P_ ((void (*) (char *)));
+
+
+static struct thread_state primary_thread;
+
+static struct thread_state *all_threads = &primary_thread;
+
+__thread struct thread_state *current_thread = &primary_thread;
+
+static pthread_mutex_t global_lock;
+
+static void
+mark_one_thread (struct thread_state *thread)
+{
+ register struct specbinding *bind;
+ struct handler *handler;
+
+ for (bind = thread->m_specpdl; bind != thread->m_specpdl_ptr; bind++)
+ {
+ mark_object (bind->symbol);
+ mark_object (bind->old_value);
+ }
+
+#if (GC_MARK_STACK == GC_MAKE_GCPROS_NOOPS \
+ || GC_MARK_STACK == GC_MARK_STACK_CHECK_GCPROS)
+ mark_stack (thread->stack_bottom, thread->stack_top);
+#else
+ {
+ register struct gcpro *tail;
+ for (tail = thread->m_gcprolist; tail; tail = tail->next)
+ for (i = 0; i < tail->nvars; i++)
+ mark_object (tail->var[i]);
+ }
+#endif
+
+ mark_byte_stack (thread->m_byte_stack_list);
+
+ mark_catchlist (thread->m_catchlist);
+
+ for (handler = thread->m_handlerlist; handler; handler = handler->next)
+ {
+ mark_object (handler->handler);
+ mark_object (handler->var);
+ }
+
+ mark_backtrace (thread->m_backtrace_list);
+
+ if (thread->func)
+ mark_object (thread->func);
+}
+
+static void
+mark_threads_continuation (char *end)
+{
+ struct thread_state *iter;
+
+ current_thread->stack_top = end;
+ for (iter = all_threads; iter; iter = iter->next)
+ mark_one_thread (iter);
+}
+
+void
+mark_threads (void)
+{
+ flush_stack_call_func (mark_threads_continuation);
+}
+
+void
+unmark_threads (void)
+{
+ struct thread_state *iter;
+
+ for (iter = all_threads; iter; iter = iter->next)
+ unmark_byte_stack (iter->m_byte_stack_list);
+}
+
+static void
+thread_yield_continuation (char *end)
+{
+ current_thread->stack_top = end;
+ pthread_mutex_unlock (&global_lock);
+ sched_yield ();
+ pthread_mutex_lock (&global_lock);
+}
+
+void
+thread_yield (void)
+{
+ /* Note: currently it is safe to check this here, but eventually it
+ will require a lock to ensure non-racy operation. */
+ /* Only yield if there is another thread to yield to. */
+ if (all_threads->next)
+ flush_stack_call_func (thread_yield_continuation);
+}
+
+DEFUN ("yield", Fyield, Syield, 0, 0, 0,
+ doc: /* Yield to the next thread. */)
+ (void)
+{
+ thread_yield ();
+}
+
+static void *
+run_thread (void *state)
+{
+ char stack_bottom_variable;
+ struct thread_state *self = state;
+ struct thread_state **iter;
+
+ self->stack_bottom = &stack_bottom_variable;
+
+ self->m_specpdl_size = 50;
+ self->m_specpdl = xmalloc (self->m_specpdl_size
+ * sizeof (struct specbinding));
+ self->m_specpdl_ptr = self->m_specpdl;
+
+ /* Thread-local assignment. */
+ current_thread = self;
+
+ pthread_mutex_lock (&global_lock);
+
+ /* FIXME: unwind protect here. */
+ Ffuncall (1, &self->func);
+
+ /* Unlink this thread from the list of all threads. */
+ for (iter = &all_threads; *iter != self; iter = &(*iter)->next)
+ ;
+ *iter = (*iter)->next;
+
+ xfree (self->m_specpdl);
+ /* FIXME: other cleanups here. */
+ xfree (self);
+
+ pthread_mutex_unlock (&global_lock);
+
+ return NULL;
+}
+
+DEFUN ("run-in-thread", Frun_in_thread, Srun_in_thread, 1, 1, 0,
+ doc: /* Start a new thread and run FUNCTION in it.
+When the function exits, the thread dies. */)
+ (function)
+ Lisp_Object function;
+{
+ pthread_t thr;
+ struct thread_state *new_thread;
+
+ /* Can't start a thread in temacs. */
+ if (!initialized)
+ abort ();
+
+ new_thread = xmalloc (sizeof (struct thread_state));
+ memset (new_thread, 0, sizeof (struct thread_state));
+
+ new_thread->func = function;
+
+ /* We'll need locking here. */
+ new_thread->next = all_threads;
+ all_threads = new_thread;
+
+ /* FIXME check result */
+ pthread_create (&thr, NULL, run_thread, new_thread);
+}
+
+void
+init_threads (void)
+{
+ pthread_mutex_init (&global_lock, NULL);
+ pthread_mutex_lock (&global_lock);
+}
+
+void
+syms_of_threads (void)
+{
+ defsubr (&Srun_in_thread);
+ defsubr (&Syield);
+}
--- /dev/null
+
+struct thread_state
+{
+ /* Recording what needs to be marked for gc. */
+ struct gcpro *m_gcprolist;
+#define gcprolist (current_thread->m_gcprolist)
+
+ /* A list of currently active byte-code execution value stacks.
+ Fbyte_code adds an entry to the head of this list before it starts
+ processing byte-code, and it removed the entry again when it is
+ done. Signalling an error truncates the list analoguous to
+ gcprolist. */
+ struct byte_stack *m_byte_stack_list;
+#define byte_stack_list (current_thread->m_byte_stack_list)
+
+ /* An address near the bottom of the stack.
+ Tells GC how to save a copy of the stack. */
+ char *stack_bottom;
+
+ /* An address near the top of the stack. */
+ char *stack_top;
+
+ struct backtrace *m_backtrace_list;
+#define backtrace_list (current_thread->m_backtrace_list)
+
+ struct catchtag *m_catchlist;
+#define catchlist (current_thread->m_catchlist)
+
+ /* Chain of condition handlers currently in effect.
+ The elements of this chain are contained in the stack frames
+ of Fcondition_case and internal_condition_case.
+ When an error is signaled (by calling Fsignal, below),
+ this chain is searched for an element that applies. */
+ struct handler *m_handlerlist;
+#define handlerlist (current_thread->m_handlerlist)
+
+ /* Count levels of GCPRO to detect failure to UNGCPRO. */
+ int m_gcpro_level;
+#define gcpro_level (current_thread->m_gcpro_level)
+
+ /* Current number of specbindings allocated in specpdl. */
+ int m_specpdl_size;
+#define specpdl_size (current_thread->m_specpdl_size)
+
+ /* Pointer to beginning of specpdl. */
+ struct specbinding *m_specpdl;
+#define specpdl (current_thread->m_specpdl)
+
+ /* Pointer to first unused element in specpdl. */
+ struct specbinding *m_specpdl_ptr;
+#define specpdl_ptr (current_thread->m_specpdl_ptr)
+
+ /* Depth in Lisp evaluations and function calls. */
+ int m_lisp_eval_depth;
+#define lisp_eval_depth (current_thread->m_lisp_eval_depth)
+
+ /* The function we are evaluating, or 0 in the main thread. */
+ Lisp_Object func;
+
+ struct thread_state *next;
+};
+
+extern __thread struct thread_state *current_thread;
+
+extern void init_threads P_ ((void));
+
+extern void thread_yield P_ ((void));
+
+extern void syms_of_threads P_ ((void));