Solve some problems with memory cleanup.
[emacs.git] / src / thread.c
blobb5c1aaca1a4f084467846a3c7fc6a16d86e8ed55
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 struct thread_state *self = state;
154 struct gcpro gcpro1;
155 Lisp_Object buffer;
156 char stack_pos;
158 self->stack_top = self->stack_bottom = &stack_pos;
160 self->m_specpdl_size = 50;
161 self->m_specpdl = xmalloc (self->m_specpdl_size
162 * sizeof (struct specbinding));
163 self->m_specpdl_ptr = self->m_specpdl;
165 /* Thread-local assignment. */
166 current_thread = self;
168 pthread_mutex_lock (&global_lock);
170 /* We need special handling to set the initial buffer. Our parent
171 thread is very likely to be using this same buffer so we will
172 typically wait for the parent thread to release it first. */
173 XSETBUFFER (buffer, self->m_current_buffer);
174 GCPRO1 (buffer);
175 self->m_current_buffer = 0;
176 set_buffer_internal (XBUFFER (buffer));
178 /* It might be nice to do something with errors here. */
179 internal_condition_case (invoke_thread_function, Qt, do_nothing);
181 /* Unlink this thread from the list of all threads. */
182 if (all_threads == self)
183 all_threads = all_threads->next_thread;
184 else
186 struct thread_state *prev;
187 for (prev = all_threads; prev->next_thread != self;
188 prev = prev->next_thread)
190 prev->next_thread = self->next_thread;
193 release_buffer (self);
195 pthread_mutex_unlock (&global_lock);
197 return NULL;
200 DEFUN ("run-in-thread", Frun_in_thread, Srun_in_thread, 1, 1, 0,
201 doc: /* Start a new thread and run FUNCTION in it.
202 When the function exits, the thread dies. */)
203 (function)
204 Lisp_Object function;
206 char stack_pos;
207 pthread_t thr;
208 struct thread_state *new_thread;
209 struct specbinding *p;
211 /* Can't start a thread in temacs. */
212 if (!initialized)
213 abort ();
215 new_thread = (struct thread_state *) allocate_pseudovector (VECSIZE (struct thread_state),
216 2, PVEC_THREAD);
217 memset ((char *) new_thread + OFFSETOF (struct thread_state, m_gcprolist),
218 0, sizeof (struct thread_state) - OFFSETOF (struct thread_state,
219 m_gcprolist));
221 new_thread->func = function;
222 new_thread->initial_specpdl = Qnil;
223 new_thread->m_current_buffer = current_thread->m_current_buffer;
224 new_thread->stack_bottom = &stack_pos;
226 for (p = specpdl; p != specpdl_ptr; ++p)
228 if (!p->func)
230 Lisp_Object sym = p->symbol;
231 if (!SYMBOLP (sym))
232 sym = XCAR (sym);
233 new_thread->initial_specpdl
234 = Fcons (Fcons (sym, find_symbol_value (sym)),
235 new_thread->initial_specpdl);
239 /* We'll need locking here. */
240 new_thread->next_thread = all_threads;
241 all_threads = new_thread;
243 if (pthread_create (&thr, NULL, run_thread, new_thread))
245 /* Restore the previous situation. */
246 all_threads = all_threads->next_thread;
249 return Qnil;
252 /* Get the current thread as a lisp object. */
253 Lisp_Object
254 get_current_thread (void)
256 Lisp_Object result;
257 XSETTHREAD (result, current_thread);
258 return result;
261 /* Get the main thread as a lisp object. */
262 Lisp_Object
263 get_main_thread (void)
265 Lisp_Object result;
266 XSETTHREAD (result, &primary_thread);
267 return result;
271 other_threads_p (void)
273 return all_threads->next_thread != NULL;
276 void
277 init_threads (void)
279 pthread_mutex_init (&global_lock, NULL);
280 pthread_mutex_lock (&global_lock);
283 void
284 syms_of_threads (void)
286 defsubr (&Srun_in_thread);
287 defsubr (&Syield);