Fix a segfault when m_current_buffer is NULL
[emacs.git] / src / thread.c
blob506e9d60afae06b40b839d5648edae96dd134f5c
2 #include <config.h>
3 #include "lisp.h"
4 #include <pthread.h>
6 void mark_byte_stack P_ ((struct byte_stack *));
7 void mark_backtrace P_ ((struct backtrace *));
8 void mark_catchlist P_ ((struct catchtag *));
9 void mark_stack P_ ((char *, char *));
10 void flush_stack_call_func P_ ((void (*) (char *, void *), void *));
13 static struct thread_state primary_thread;
15 static struct thread_state *all_threads = &primary_thread;
17 __thread struct thread_state *current_thread = &primary_thread;
19 pthread_mutex_t global_lock;
21 static void
22 mark_one_thread (struct thread_state *thread)
24 register struct specbinding *bind;
25 struct handler *handler;
26 Lisp_Object tem;
28 for (bind = thread->m_specpdl; bind != thread->m_specpdl_ptr; bind++)
30 mark_object (bind->symbol);
31 mark_object (bind->old_value);
34 #if (GC_MARK_STACK == GC_MAKE_GCPROS_NOOPS \
35 || GC_MARK_STACK == GC_MARK_STACK_CHECK_GCPROS)
36 mark_stack (thread->stack_bottom, thread->stack_top);
37 #else
39 register struct gcpro *tail;
40 for (tail = thread->m_gcprolist; tail; tail = tail->next)
41 for (i = 0; i < tail->nvars; i++)
42 mark_object (tail->var[i]);
44 #endif
46 mark_byte_stack (thread->m_byte_stack_list);
48 mark_catchlist (thread->m_catchlist);
50 for (handler = thread->m_handlerlist; handler; handler = handler->next)
52 mark_object (handler->handler);
53 mark_object (handler->var);
56 mark_backtrace (thread->m_backtrace_list);
58 if (thread->m_current_buffer)
60 XSETBUFFER (tem, thread->m_current_buffer);
61 mark_object (tem);
65 static void
66 mark_threads_callback (char *end, void *ignore)
68 struct thread_state *iter;
70 current_thread->stack_top = end;
71 for (iter = all_threads; iter; iter = iter->next_thread)
73 Lisp_Object thread_obj;
74 XSETTHREAD (thread_obj, iter);
75 mark_object (thread_obj);
76 mark_one_thread (iter);
80 void
81 mark_threads (void)
83 flush_stack_call_func (mark_threads_callback, NULL);
86 void
87 unmark_threads (void)
89 struct thread_state *iter;
91 for (iter = all_threads; iter; iter = iter->next_thread)
92 unmark_byte_stack (iter->m_byte_stack_list);
95 static void
96 thread_yield_callback (char *end, void *ignore)
98 current_thread->stack_top = end;
99 pthread_mutex_unlock (&global_lock);
100 sched_yield ();
101 pthread_mutex_lock (&global_lock);
104 void
105 thread_yield (void)
107 /* Note: currently it is safe to check this here, but eventually it
108 will require a lock to ensure non-racy operation. */
109 /* Only yield if there is another thread to yield to. */
110 if (all_threads->next_thread)
111 flush_stack_call_func (thread_yield_callback, NULL);
114 DEFUN ("yield", Fyield, Syield, 0, 0, 0,
115 doc: /* Yield to the next thread. */)
116 (void)
118 thread_yield ();
121 static Lisp_Object
122 invoke_thread_function (void)
124 Lisp_Object iter;
126 int count = SPECPDL_INDEX ();
128 /* Set up specpdl. */
129 for (iter = current_thread->initial_specpdl;
130 !EQ (iter, Qnil);
131 iter = XCDR (iter))
133 /* We may bind a variable twice -- but it doesn't matter because
134 there is no way to undo these bindings without exiting the
135 thread. */
136 specbind (XCAR (XCAR (iter)), XCDR (XCAR (iter)));
138 current_thread->initial_specpdl = Qnil;
140 Ffuncall (1, &current_thread->func);
141 return unbind_to (count, Qnil);
144 static Lisp_Object
145 do_nothing (Lisp_Object whatever)
147 return whatever;
150 static void *
151 run_thread (void *state)
153 char stack_pos;
154 struct thread_state *self = state;
155 struct thread_state **iter;
156 struct gcpro gcpro1;
157 Lisp_Object buffer;
159 self->stack_top = self->stack_bottom = &stack_pos;
161 self->m_specpdl_size = 50;
162 self->m_specpdl = xmalloc (self->m_specpdl_size
163 * sizeof (struct specbinding));
164 self->m_specpdl_ptr = self->m_specpdl;
166 /* Thread-local assignment. */
167 current_thread = self;
169 pthread_mutex_lock (&global_lock);
171 /* We need special handling to set the initial buffer. Our parent
172 thread is very likely to be using this same buffer so we will
173 typically wait for the parent thread to release it first. */
174 XSETBUFFER (buffer, self->m_current_buffer);
175 GCPRO1 (buffer);
176 self->m_current_buffer = 0;
177 set_buffer_internal (XBUFFER (buffer));
179 /* It might be nice to do something with errors here. */
180 internal_condition_case (invoke_thread_function, Qt, do_nothing);
182 /* Unlink this thread from the list of all threads. */
183 for (iter = &all_threads; *iter != self; iter = &(*iter)->next_thread)
185 *iter = (*iter)->next_thread;
187 release_buffer (self);
189 xfree (self->m_specpdl);
190 xfree (self);
192 pthread_mutex_unlock (&global_lock);
194 return NULL;
197 DEFUN ("run-in-thread", Frun_in_thread, Srun_in_thread, 1, 1, 0,
198 doc: /* Start a new thread and run FUNCTION in it.
199 When the function exits, the thread dies. */)
200 (function)
201 Lisp_Object function;
203 pthread_t thr;
204 struct thread_state *new_thread;
205 struct specbinding *p;
207 /* Can't start a thread in temacs. */
208 if (!initialized)
209 abort ();
211 new_thread = (struct thread_state *) allocate_pseudovector (VECSIZE (struct thread_state),
212 2, PVEC_THREAD);
213 memset ((char *) new_thread + OFFSETOF (struct thread_state, m_gcprolist),
214 0, sizeof (struct thread_state) - OFFSETOF (struct thread_state,
215 m_gcprolist));
217 new_thread->func = function;
218 new_thread->initial_specpdl = Qnil;
219 new_thread->m_current_buffer = current_thread->m_current_buffer;
221 for (p = specpdl; p != specpdl_ptr; ++p)
223 if (!p->func)
225 Lisp_Object sym = p->symbol;
226 if (!SYMBOLP (sym))
227 sym = XCAR (sym);
228 new_thread->initial_specpdl
229 = Fcons (Fcons (sym, find_symbol_value (sym)),
230 new_thread->initial_specpdl);
234 /* We'll need locking here. */
235 new_thread->next_thread = all_threads;
236 all_threads = new_thread;
238 /* FIXME check result */
239 pthread_create (&thr, NULL, run_thread, new_thread);
241 return Qnil;
244 /* Get the current thread as a lisp object. */
245 Lisp_Object
246 get_current_thread (void)
248 Lisp_Object result;
249 XSETTHREAD (result, current_thread);
250 return result;
253 /* Get the main thread as a lisp object. */
254 Lisp_Object
255 get_main_thread (void)
257 Lisp_Object result;
258 XSETTHREAD (result, &primary_thread);
259 return result;
263 other_threads_p (void)
265 return all_threads->next_thread != NULL;
268 void
269 init_threads (void)
271 pthread_mutex_init (&global_lock, NULL);
272 pthread_mutex_lock (&global_lock);
275 void
276 syms_of_threads (void)
278 defsubr (&Srun_in_thread);
279 defsubr (&Syield);