aboutsummaryrefslogtreecommitdiffstats
path: root/src/thread.c
diff options
context:
space:
mode:
authorTom Tromey2012-08-19 03:23:03 -0600
committerTom Tromey2012-08-19 03:23:03 -0600
commit5651640d578fa2efa40be4789d9fa61813ccb1fa (patch)
treea59e33464016e88cb3f0501d6c47baf8aa5e07aa /src/thread.c
parentee1464eab19311ab7708b135bdb6eb989909e4cc (diff)
downloademacs-5651640d578fa2efa40be4789d9fa61813ccb1fa.tar.gz
emacs-5651640d578fa2efa40be4789d9fa61813ccb1fa.zip
condition variables
This implements condition variables for elisp. This needs more tests.
Diffstat (limited to 'src/thread.c')
-rw-r--r--src/thread.c219
1 files changed, 201 insertions, 18 deletions
diff --git a/src/thread.c b/src/thread.c
index 9c39b84eb50..4657d6a797e 100644
--- a/src/thread.c
+++ b/src/thread.c
@@ -32,7 +32,7 @@ static struct thread_state *all_threads = &primary_thread;
32 32
33static sys_mutex_t global_lock; 33static sys_mutex_t global_lock;
34 34
35Lisp_Object Qthreadp, Qmutexp; 35Lisp_Object Qthreadp, Qmutexp, Qcondition_variablep;
36 36
37 37
38 38
@@ -89,36 +89,41 @@ lisp_mutex_init (lisp_mutex_t *mutex)
89 sys_cond_init (&mutex->condition); 89 sys_cond_init (&mutex->condition);
90} 90}
91 91
92static void 92static int
93lisp_mutex_lock (lisp_mutex_t *mutex) 93lisp_mutex_lock (lisp_mutex_t *mutex, int new_count)
94{ 94{
95 struct thread_state *self; 95 struct thread_state *self;
96 96
97 if (mutex->owner == NULL) 97 if (mutex->owner == NULL)
98 { 98 {
99 mutex->owner = current_thread; 99 mutex->owner = current_thread;
100 mutex->count = 1; 100 mutex->count = new_count == 0 ? 1 : new_count;
101 return; 101 return 0;
102 } 102 }
103 if (mutex->owner == current_thread) 103 if (mutex->owner == current_thread)
104 { 104 {
105 eassert (new_count == 0);
105 ++mutex->count; 106 ++mutex->count;
106 return; 107 return 0;
107 } 108 }
108 109
109 self = current_thread; 110 self = current_thread;
110 self->wait_condvar = &mutex->condition; 111 self->wait_condvar = &mutex->condition;
111 while (mutex->owner != NULL && EQ (self->error_symbol, Qnil)) 112 while (mutex->owner != NULL && (new_count != 0
113 || EQ (self->error_symbol, Qnil)))
112 sys_cond_wait (&mutex->condition, &global_lock); 114 sys_cond_wait (&mutex->condition, &global_lock);
113 self->wait_condvar = NULL; 115 self->wait_condvar = NULL;
114 116
115 post_acquire_global_lock (self); 117 if (new_count == 0 && !NILP (self->error_symbol))
118 return 1;
116 119
117 mutex->owner = self; 120 mutex->owner = self;
118 mutex->count = 1; 121 mutex->count = new_count == 0 ? 1 : new_count;
122
123 return 1;
119} 124}
120 125
121static void 126static int
122lisp_mutex_unlock (lisp_mutex_t *mutex) 127lisp_mutex_unlock (lisp_mutex_t *mutex)
123{ 128{
124 struct thread_state *self = current_thread; 129 struct thread_state *self = current_thread;
@@ -127,12 +132,28 @@ lisp_mutex_unlock (lisp_mutex_t *mutex)
127 error ("blah"); 132 error ("blah");
128 133
129 if (--mutex->count > 0) 134 if (--mutex->count > 0)
130 return; 135 return 0;
131 136
132 mutex->owner = NULL; 137 mutex->owner = NULL;
133 sys_cond_broadcast (&mutex->condition); 138 sys_cond_broadcast (&mutex->condition);
134 139
135 post_acquire_global_lock (self); 140 return 1;
141}
142
143static unsigned int
144lisp_mutex_unlock_for_wait (lisp_mutex_t *mutex)
145{
146 struct thread_state *self = current_thread;
147 unsigned int result = mutex->count;
148
149 /* Ensured by condvar code. */
150 eassert (mutex->owner == current_thread);
151
152 mutex->count = 0;
153 mutex->owner = NULL;
154 sys_cond_broadcast (&mutex->condition);
155
156 return result;
136} 157}
137 158
138static void 159static void
@@ -141,6 +162,12 @@ lisp_mutex_destroy (lisp_mutex_t *mutex)
141 sys_cond_destroy (&mutex->condition); 162 sys_cond_destroy (&mutex->condition);
142} 163}
143 164
165static int
166lisp_mutex_owned_p (lisp_mutex_t *mutex)
167{
168 return mutex->owner == current_thread;
169}
170
144 171
145 172
146DEFUN ("make-mutex", Fmake_mutex, Smake_mutex, 0, 1, 0, 173DEFUN ("make-mutex", Fmake_mutex, Smake_mutex, 0, 1, 0,
@@ -173,9 +200,10 @@ static void
173mutex_lock_callback (void *arg) 200mutex_lock_callback (void *arg)
174{ 201{
175 struct Lisp_Mutex *mutex = arg; 202 struct Lisp_Mutex *mutex = arg;
203 struct thread_state *self = current_thread;
176 204
177 /* This calls post_acquire_global_lock. */ 205 if (lisp_mutex_lock (&mutex->mutex, 0))
178 lisp_mutex_lock (&mutex->mutex); 206 post_acquire_global_lock (self);
179} 207}
180 208
181static Lisp_Object 209static Lisp_Object
@@ -211,9 +239,10 @@ static void
211mutex_unlock_callback (void *arg) 239mutex_unlock_callback (void *arg)
212{ 240{
213 struct Lisp_Mutex *mutex = arg; 241 struct Lisp_Mutex *mutex = arg;
242 struct thread_state *self = current_thread;
214 243
215 /* This calls post_acquire_global_lock. */ 244 if (lisp_mutex_unlock (&mutex->mutex))
216 lisp_mutex_unlock (&mutex->mutex); 245 post_acquire_global_lock (self);
217} 246}
218 247
219DEFUN ("mutex-unlock", Fmutex_unlock, Smutex_unlock, 1, 1, 0, 248DEFUN ("mutex-unlock", Fmutex_unlock, Smutex_unlock, 1, 1, 0,
@@ -253,6 +282,154 @@ finalize_one_mutex (struct Lisp_Mutex *mutex)
253 282
254 283
255 284
285DEFUN ("make-condition-variable",
286 Fmake_condition_variable, Smake_condition_variable,
287 1, 2, 0,
288 doc: /* Make a condition variable.
289A condition variable provides a way for a thread to sleep while
290waiting for a state change.
291
292MUTEX is the mutex associated with this condition variable.
293NAME, if given, is the name of this condition variable. The name is
294informational only. */)
295 (Lisp_Object mutex, Lisp_Object name)
296{
297 struct Lisp_CondVar *condvar;
298 Lisp_Object result;
299
300 CHECK_MUTEX (mutex);
301 if (!NILP (name))
302 CHECK_STRING (name);
303
304 condvar = ALLOCATE_PSEUDOVECTOR (struct Lisp_CondVar, cond, PVEC_CONDVAR);
305 memset ((char *) condvar + offsetof (struct Lisp_CondVar, cond),
306 0, sizeof (struct Lisp_CondVar) - offsetof (struct Lisp_CondVar,
307 cond));
308 condvar->mutex = mutex;
309 condvar->name = name;
310 sys_cond_init (&condvar->cond);
311
312 XSETCONDVAR (result, condvar);
313 return result;
314}
315
316static void
317condition_wait_callback (void *arg)
318{
319 struct Lisp_CondVar *cvar = arg;
320 struct Lisp_Mutex *mutex = XMUTEX (cvar->mutex);
321 struct thread_state *self = current_thread;
322 unsigned int saved_count;
323 Lisp_Object cond;
324
325 XSETCONDVAR (cond, cvar);
326 current_thread->event_object = cond;
327 saved_count = lisp_mutex_unlock_for_wait (&mutex->mutex);
328 /* If we were signalled while unlocking, we skip the wait, but we
329 still must reacquire our lock. */
330 if (NILP (self->error_symbol))
331 {
332 self->wait_condvar = &cvar->cond;
333 sys_cond_wait (&cvar->cond, &global_lock);
334 self->wait_condvar = NULL;
335 }
336 lisp_mutex_lock (&mutex->mutex, saved_count);
337 current_thread->event_object = Qnil;
338 post_acquire_global_lock (self);
339}
340
341DEFUN ("condition-wait", Fcondition_wait, Scondition_wait, 1, 1, 0,
342 doc: /* Wait for the condition variable to be notified.
343CONDITION is the condition variable to wait on.
344
345The mutex associated with CONDITION must be held when this is called.
346It is an error if it is not held.
347
348This atomically releases the mutex and waits for CONDITION to be
349notified. When `condition-wait' returns, the mutex will again be
350locked by this thread. */)
351 (Lisp_Object condition)
352{
353 struct Lisp_CondVar *cvar;
354 struct Lisp_Mutex *mutex;
355
356 CHECK_CONDVAR (condition);
357 cvar = XCONDVAR (condition);
358
359 mutex = XMUTEX (cvar->mutex);
360 if (!lisp_mutex_owned_p (&mutex->mutex))
361 error ("fixme");
362
363 flush_stack_call_func (condition_wait_callback, cvar);
364
365 return Qnil;
366}
367
368/* Used to communicate argumnets to condition_notify_callback. */
369struct notify_args
370{
371 struct Lisp_CondVar *cvar;
372 int all;
373};
374
375static void
376condition_notify_callback (void *arg)
377{
378 struct notify_args *na = arg;
379 struct Lisp_Mutex *mutex = XMUTEX (na->cvar->mutex);
380 struct thread_state *self = current_thread;
381 unsigned int saved_count;
382 Lisp_Object cond;
383
384 XSETCONDVAR (cond, na->cvar);
385 saved_count = lisp_mutex_unlock_for_wait (&mutex->mutex);
386 if (na->all)
387 sys_cond_broadcast (&na->cvar->cond);
388 else
389 sys_cond_signal (&na->cvar->cond);
390 lisp_mutex_lock (&mutex->mutex, saved_count);
391 post_acquire_global_lock (self);
392}
393
394DEFUN ("condition-notify", Fcondition_notify, Scondition_notify, 1, 2, 0,
395 doc: /* Notify a condition variable.
396This wakes a thread waiting on CONDITION.
397If ALL is non-nil, all waiting threads are awoken.
398
399The mutex associated with CONDITION must be held when this is called.
400It is an error if it is not held.
401
402This atomically releases the mutex when notifying CONDITION. When
403`condition-notify' returns, the mutex will again be locked by this
404thread. */)
405 (Lisp_Object condition, Lisp_Object all)
406{
407 struct Lisp_CondVar *cvar;
408 struct Lisp_Mutex *mutex;
409 struct notify_args args;
410
411 CHECK_CONDVAR (condition);
412 cvar = XCONDVAR (condition);
413
414 mutex = XMUTEX (cvar->mutex);
415 if (!lisp_mutex_owned_p (&mutex->mutex))
416 error ("fixme");
417
418 args.cvar = cvar;
419 args.all = !NILP (all);
420 flush_stack_call_func (condition_notify_callback, &args);
421
422 return Qnil;
423}
424
425void
426finalize_one_condvar (struct Lisp_CondVar *condvar)
427{
428 sys_cond_destroy (&condvar->cond);
429}
430
431
432
256struct select_args 433struct select_args
257{ 434{
258 select_func *func; 435 select_func *func;
@@ -555,8 +732,8 @@ DEFUN ("thread-signal", Fthread_signal, Sthread_signal, 3, 3, 0,
555 doc: /* Signal an error in a thread. 732 doc: /* Signal an error in a thread.
556This acts like `signal', but arranges for the signal to be raised 733This acts like `signal', but arranges for the signal to be raised
557in THREAD. If THREAD is the current thread, acts just like `signal'. 734in THREAD. If THREAD is the current thread, acts just like `signal'.
558This will interrupt a blocked call to `mutex-lock' or`thread-join' in 735This will interrupt a blocked call to `mutex-lock', `condition-wait',
559the target thread. */) 736or `thread-join' in the target thread. */)
560 (Lisp_Object thread, Lisp_Object error_symbol, Lisp_Object data) 737 (Lisp_Object thread, Lisp_Object error_symbol, Lisp_Object data)
561{ 738{
562 struct thread_state *tstate; 739 struct thread_state *tstate;
@@ -597,6 +774,7 @@ DEFUN ("thread-blocker", Fthread_blocker, Sthread_blocker, 1, 1, 0,
597If THREAD is blocked in `thread-join' on a second thread, return that 774If THREAD is blocked in `thread-join' on a second thread, return that
598thread. 775thread.
599If THREAD is blocked in `mutex-lock', return the mutex. 776If THREAD is blocked in `mutex-lock', return the mutex.
777If THREAD is blocked in `condition-wait', return the condition variable.
600Otherwise, if THREAD is not blocked, return nil. */) 778Otherwise, if THREAD is not blocked, return nil. */)
601 (Lisp_Object thread) 779 (Lisp_Object thread)
602{ 780{
@@ -711,9 +889,14 @@ syms_of_threads (void)
711 defsubr (&Smutex_lock); 889 defsubr (&Smutex_lock);
712 defsubr (&Smutex_unlock); 890 defsubr (&Smutex_unlock);
713 defsubr (&Smutex_name); 891 defsubr (&Smutex_name);
892 defsubr (&Smake_condition_variable);
893 defsubr (&Scondition_wait);
894 defsubr (&Scondition_notify);
714 895
715 Qthreadp = intern_c_string ("threadp"); 896 Qthreadp = intern_c_string ("threadp");
716 staticpro (&Qthreadp); 897 staticpro (&Qthreadp);
717 Qmutexp = intern_c_string ("mutexp"); 898 Qmutexp = intern_c_string ("mutexp");
718 staticpro (&Qmutexp); 899 staticpro (&Qmutexp);
900 Qcondition_variablep = intern_c_string ("condition-variablep");
901 staticpro (&Qcondition_variablep);
719} 902}