diff options
| author | Eli Zaretskii | 2016-12-04 19:59:17 +0200 |
|---|---|---|
| committer | Eli Zaretskii | 2016-12-04 19:59:17 +0200 |
| commit | de4624c99ea5bbe38ad5aff7b6461cc5c740d0be (patch) | |
| tree | 1b57de9e769cdb695cb2cecf157b50f7dea9cfe5 /src/thread.c | |
| parent | a486fabb41cdbaa5813c2687fd4008945297d71d (diff) | |
| parent | e7bde34e939451d87fb42a36195086bdbe48b5e1 (diff) | |
| download | emacs-de4624c99ea5bbe38ad5aff7b6461cc5c740d0be.tar.gz emacs-de4624c99ea5bbe38ad5aff7b6461cc5c740d0be.zip | |
Merge branch 'concurrency'
Conflicts (resolved):
configure.ac
src/Makefile.in
src/alloc.c
src/bytecode.c
src/emacs.c
src/eval.c
src/lisp.h
src/process.c
src/regex.c
src/regex.h
Diffstat (limited to 'src/thread.c')
| -rw-r--r-- | src/thread.c | 975 |
1 files changed, 975 insertions, 0 deletions
diff --git a/src/thread.c b/src/thread.c new file mode 100644 index 00000000000..f5b04e4b231 --- /dev/null +++ b/src/thread.c | |||
| @@ -0,0 +1,975 @@ | |||
| 1 | /* Threading code. | ||
| 2 | Copyright (C) 2012, 2013 Free Software Foundation, Inc. | ||
| 3 | |||
| 4 | This file is part of GNU Emacs. | ||
| 5 | |||
| 6 | GNU Emacs is free software: you can redistribute it and/or modify | ||
| 7 | it under the terms of the GNU General Public License as published by | ||
| 8 | the Free Software Foundation, either version 3 of the License, or | ||
| 9 | (at your option) any later version. | ||
| 10 | |||
| 11 | GNU Emacs is distributed in the hope that it will be useful, | ||
| 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 14 | GNU General Public License for more details. | ||
| 15 | |||
| 16 | You should have received a copy of the GNU General Public License | ||
| 17 | along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */ | ||
| 18 | |||
| 19 | |||
| 20 | #include <config.h> | ||
| 21 | #include <setjmp.h> | ||
| 22 | #include "lisp.h" | ||
| 23 | #include "character.h" | ||
| 24 | #include "buffer.h" | ||
| 25 | #include "process.h" | ||
| 26 | #include "coding.h" | ||
| 27 | |||
| 28 | static struct thread_state primary_thread; | ||
| 29 | |||
| 30 | struct thread_state *current_thread = &primary_thread; | ||
| 31 | |||
| 32 | static struct thread_state *all_threads = &primary_thread; | ||
| 33 | |||
| 34 | static sys_mutex_t global_lock; | ||
| 35 | |||
| 36 | extern int poll_suppress_count; | ||
| 37 | extern volatile int interrupt_input_blocked; | ||
| 38 | |||
| 39 | |||
| 40 | |||
| 41 | /* m_specpdl is set when the thread is created and cleared when the | ||
| 42 | thread dies. */ | ||
| 43 | #define thread_alive_p(STATE) ((STATE)->m_specpdl != NULL) | ||
| 44 | |||
| 45 | |||
| 46 | |||
| 47 | static void | ||
| 48 | release_global_lock (void) | ||
| 49 | { | ||
| 50 | sys_mutex_unlock (&global_lock); | ||
| 51 | } | ||
| 52 | |||
| 53 | /* You must call this after acquiring the global lock. | ||
| 54 | acquire_global_lock does it for you. */ | ||
| 55 | static void | ||
| 56 | post_acquire_global_lock (struct thread_state *self) | ||
| 57 | { | ||
| 58 | Lisp_Object buffer; | ||
| 59 | struct thread_state *prev_thread = current_thread; | ||
| 60 | |||
| 61 | /* Do this early on, so that code below could signal errors (e.g., | ||
| 62 | unbind_for_thread_switch might) correctly, because we are already | ||
| 63 | running in the context of the thread pointed by SELF. */ | ||
| 64 | current_thread = self; | ||
| 65 | |||
| 66 | if (prev_thread != current_thread) | ||
| 67 | { | ||
| 68 | /* PREV_THREAD is NULL if the previously current thread | ||
| 69 | exited. In this case, there is no reason to unbind, and | ||
| 70 | trying will crash. */ | ||
| 71 | if (prev_thread != NULL) | ||
| 72 | unbind_for_thread_switch (prev_thread); | ||
| 73 | rebind_for_thread_switch (); | ||
| 74 | } | ||
| 75 | |||
| 76 | /* We need special handling to re-set the buffer. */ | ||
| 77 | XSETBUFFER (buffer, self->m_current_buffer); | ||
| 78 | self->m_current_buffer = 0; | ||
| 79 | set_buffer_internal (XBUFFER (buffer)); | ||
| 80 | |||
| 81 | if (!NILP (current_thread->error_symbol)) | ||
| 82 | { | ||
| 83 | Lisp_Object sym = current_thread->error_symbol; | ||
| 84 | Lisp_Object data = current_thread->error_data; | ||
| 85 | |||
| 86 | current_thread->error_symbol = Qnil; | ||
| 87 | current_thread->error_data = Qnil; | ||
| 88 | Fsignal (sym, data); | ||
| 89 | } | ||
| 90 | } | ||
| 91 | |||
| 92 | static void | ||
| 93 | acquire_global_lock (struct thread_state *self) | ||
| 94 | { | ||
| 95 | sys_mutex_lock (&global_lock); | ||
| 96 | post_acquire_global_lock (self); | ||
| 97 | } | ||
| 98 | |||
| 99 | |||
| 100 | |||
| 101 | static void | ||
| 102 | lisp_mutex_init (lisp_mutex_t *mutex) | ||
| 103 | { | ||
| 104 | mutex->owner = NULL; | ||
| 105 | mutex->count = 0; | ||
| 106 | sys_cond_init (&mutex->condition); | ||
| 107 | } | ||
| 108 | |||
| 109 | static int | ||
| 110 | lisp_mutex_lock (lisp_mutex_t *mutex, int new_count) | ||
| 111 | { | ||
| 112 | struct thread_state *self; | ||
| 113 | |||
| 114 | if (mutex->owner == NULL) | ||
| 115 | { | ||
| 116 | mutex->owner = current_thread; | ||
| 117 | mutex->count = new_count == 0 ? 1 : new_count; | ||
| 118 | return 0; | ||
| 119 | } | ||
| 120 | if (mutex->owner == current_thread) | ||
| 121 | { | ||
| 122 | eassert (new_count == 0); | ||
| 123 | ++mutex->count; | ||
| 124 | return 0; | ||
| 125 | } | ||
| 126 | |||
| 127 | self = current_thread; | ||
| 128 | self->wait_condvar = &mutex->condition; | ||
| 129 | while (mutex->owner != NULL && (new_count != 0 | ||
| 130 | || NILP (self->error_symbol))) | ||
| 131 | sys_cond_wait (&mutex->condition, &global_lock); | ||
| 132 | self->wait_condvar = NULL; | ||
| 133 | |||
| 134 | if (new_count == 0 && !NILP (self->error_symbol)) | ||
| 135 | return 1; | ||
| 136 | |||
| 137 | mutex->owner = self; | ||
| 138 | mutex->count = new_count == 0 ? 1 : new_count; | ||
| 139 | |||
| 140 | return 1; | ||
| 141 | } | ||
| 142 | |||
| 143 | static int | ||
| 144 | lisp_mutex_unlock (lisp_mutex_t *mutex) | ||
| 145 | { | ||
| 146 | struct thread_state *self = current_thread; | ||
| 147 | |||
| 148 | if (mutex->owner != current_thread) | ||
| 149 | error ("blah"); | ||
| 150 | |||
| 151 | if (--mutex->count > 0) | ||
| 152 | return 0; | ||
| 153 | |||
| 154 | mutex->owner = NULL; | ||
| 155 | sys_cond_broadcast (&mutex->condition); | ||
| 156 | |||
| 157 | return 1; | ||
| 158 | } | ||
| 159 | |||
| 160 | static unsigned int | ||
| 161 | lisp_mutex_unlock_for_wait (lisp_mutex_t *mutex) | ||
| 162 | { | ||
| 163 | struct thread_state *self = current_thread; | ||
| 164 | unsigned int result = mutex->count; | ||
| 165 | |||
| 166 | /* Ensured by condvar code. */ | ||
| 167 | eassert (mutex->owner == current_thread); | ||
| 168 | |||
| 169 | mutex->count = 0; | ||
| 170 | mutex->owner = NULL; | ||
| 171 | sys_cond_broadcast (&mutex->condition); | ||
| 172 | |||
| 173 | return result; | ||
| 174 | } | ||
| 175 | |||
| 176 | static void | ||
| 177 | lisp_mutex_destroy (lisp_mutex_t *mutex) | ||
| 178 | { | ||
| 179 | sys_cond_destroy (&mutex->condition); | ||
| 180 | } | ||
| 181 | |||
| 182 | static int | ||
| 183 | lisp_mutex_owned_p (lisp_mutex_t *mutex) | ||
| 184 | { | ||
| 185 | return mutex->owner == current_thread; | ||
| 186 | } | ||
| 187 | |||
| 188 | |||
| 189 | |||
| 190 | DEFUN ("make-mutex", Fmake_mutex, Smake_mutex, 0, 1, 0, | ||
| 191 | doc: /* Create a mutex. | ||
| 192 | A mutex provides a synchronization point for threads. | ||
| 193 | Only one thread at a time can hold a mutex. Other threads attempting | ||
| 194 | to acquire it will block until the mutex is available. | ||
| 195 | |||
| 196 | A thread can acquire a mutex any number of times. | ||
| 197 | |||
| 198 | NAME, if given, is used as the name of the mutex. The name is | ||
| 199 | informational only. */) | ||
| 200 | (Lisp_Object name) | ||
| 201 | { | ||
| 202 | struct Lisp_Mutex *mutex; | ||
| 203 | Lisp_Object result; | ||
| 204 | |||
| 205 | if (!NILP (name)) | ||
| 206 | CHECK_STRING (name); | ||
| 207 | |||
| 208 | mutex = ALLOCATE_PSEUDOVECTOR (struct Lisp_Mutex, mutex, PVEC_MUTEX); | ||
| 209 | memset ((char *) mutex + offsetof (struct Lisp_Mutex, mutex), | ||
| 210 | 0, sizeof (struct Lisp_Mutex) - offsetof (struct Lisp_Mutex, | ||
| 211 | mutex)); | ||
| 212 | mutex->name = name; | ||
| 213 | lisp_mutex_init (&mutex->mutex); | ||
| 214 | |||
| 215 | XSETMUTEX (result, mutex); | ||
| 216 | return result; | ||
| 217 | } | ||
| 218 | |||
| 219 | static void | ||
| 220 | mutex_lock_callback (void *arg) | ||
| 221 | { | ||
| 222 | struct Lisp_Mutex *mutex = arg; | ||
| 223 | struct thread_state *self = current_thread; | ||
| 224 | |||
| 225 | if (lisp_mutex_lock (&mutex->mutex, 0)) | ||
| 226 | post_acquire_global_lock (self); | ||
| 227 | } | ||
| 228 | |||
| 229 | static void | ||
| 230 | do_unwind_mutex_lock (void) | ||
| 231 | { | ||
| 232 | current_thread->event_object = Qnil; | ||
| 233 | } | ||
| 234 | |||
| 235 | DEFUN ("mutex-lock", Fmutex_lock, Smutex_lock, 1, 1, 0, | ||
| 236 | doc: /* Acquire a mutex. | ||
| 237 | If the current thread already owns MUTEX, increment the count and | ||
| 238 | return. | ||
| 239 | Otherwise, if no thread owns MUTEX, make the current thread own it. | ||
| 240 | Otherwise, block until MUTEX is available, or until the current thread | ||
| 241 | is signalled using `thread-signal'. | ||
| 242 | Note that calls to `mutex-lock' and `mutex-unlock' must be paired. */) | ||
| 243 | (Lisp_Object mutex) | ||
| 244 | { | ||
| 245 | struct Lisp_Mutex *lmutex; | ||
| 246 | ptrdiff_t count = SPECPDL_INDEX (); | ||
| 247 | |||
| 248 | CHECK_MUTEX (mutex); | ||
| 249 | lmutex = XMUTEX (mutex); | ||
| 250 | |||
| 251 | current_thread->event_object = mutex; | ||
| 252 | record_unwind_protect_void (do_unwind_mutex_lock); | ||
| 253 | flush_stack_call_func (mutex_lock_callback, lmutex); | ||
| 254 | return unbind_to (count, Qnil); | ||
| 255 | } | ||
| 256 | |||
| 257 | static void | ||
| 258 | mutex_unlock_callback (void *arg) | ||
| 259 | { | ||
| 260 | struct Lisp_Mutex *mutex = arg; | ||
| 261 | struct thread_state *self = current_thread; | ||
| 262 | |||
| 263 | if (lisp_mutex_unlock (&mutex->mutex)) | ||
| 264 | post_acquire_global_lock (self); | ||
| 265 | } | ||
| 266 | |||
| 267 | DEFUN ("mutex-unlock", Fmutex_unlock, Smutex_unlock, 1, 1, 0, | ||
| 268 | doc: /* Release the mutex. | ||
| 269 | If this thread does not own MUTEX, signal an error. | ||
| 270 | Otherwise, decrement the mutex's count. If the count is zero, | ||
| 271 | release MUTEX. */) | ||
| 272 | (Lisp_Object mutex) | ||
| 273 | { | ||
| 274 | struct Lisp_Mutex *lmutex; | ||
| 275 | |||
| 276 | CHECK_MUTEX (mutex); | ||
| 277 | lmutex = XMUTEX (mutex); | ||
| 278 | |||
| 279 | flush_stack_call_func (mutex_unlock_callback, lmutex); | ||
| 280 | return Qnil; | ||
| 281 | } | ||
| 282 | |||
| 283 | DEFUN ("mutex-name", Fmutex_name, Smutex_name, 1, 1, 0, | ||
| 284 | doc: /* Return the name of MUTEX. | ||
| 285 | If no name was given when MUTEX was created, return nil. */) | ||
| 286 | (Lisp_Object mutex) | ||
| 287 | { | ||
| 288 | struct Lisp_Mutex *lmutex; | ||
| 289 | |||
| 290 | CHECK_MUTEX (mutex); | ||
| 291 | lmutex = XMUTEX (mutex); | ||
| 292 | |||
| 293 | return lmutex->name; | ||
| 294 | } | ||
| 295 | |||
| 296 | void | ||
| 297 | finalize_one_mutex (struct Lisp_Mutex *mutex) | ||
| 298 | { | ||
| 299 | lisp_mutex_destroy (&mutex->mutex); | ||
| 300 | } | ||
| 301 | |||
| 302 | |||
| 303 | |||
| 304 | DEFUN ("make-condition-variable", | ||
| 305 | Fmake_condition_variable, Smake_condition_variable, | ||
| 306 | 1, 2, 0, | ||
| 307 | doc: /* Make a condition variable. | ||
| 308 | A condition variable provides a way for a thread to sleep while | ||
| 309 | waiting for a state change. | ||
| 310 | |||
| 311 | MUTEX is the mutex associated with this condition variable. | ||
| 312 | NAME, if given, is the name of this condition variable. The name is | ||
| 313 | informational only. */) | ||
| 314 | (Lisp_Object mutex, Lisp_Object name) | ||
| 315 | { | ||
| 316 | struct Lisp_CondVar *condvar; | ||
| 317 | Lisp_Object result; | ||
| 318 | |||
| 319 | CHECK_MUTEX (mutex); | ||
| 320 | if (!NILP (name)) | ||
| 321 | CHECK_STRING (name); | ||
| 322 | |||
| 323 | condvar = ALLOCATE_PSEUDOVECTOR (struct Lisp_CondVar, cond, PVEC_CONDVAR); | ||
| 324 | memset ((char *) condvar + offsetof (struct Lisp_CondVar, cond), | ||
| 325 | 0, sizeof (struct Lisp_CondVar) - offsetof (struct Lisp_CondVar, | ||
| 326 | cond)); | ||
| 327 | condvar->mutex = mutex; | ||
| 328 | condvar->name = name; | ||
| 329 | sys_cond_init (&condvar->cond); | ||
| 330 | |||
| 331 | XSETCONDVAR (result, condvar); | ||
| 332 | return result; | ||
| 333 | } | ||
| 334 | |||
| 335 | static void | ||
| 336 | condition_wait_callback (void *arg) | ||
| 337 | { | ||
| 338 | struct Lisp_CondVar *cvar = arg; | ||
| 339 | struct Lisp_Mutex *mutex = XMUTEX (cvar->mutex); | ||
| 340 | struct thread_state *self = current_thread; | ||
| 341 | unsigned int saved_count; | ||
| 342 | Lisp_Object cond; | ||
| 343 | |||
| 344 | XSETCONDVAR (cond, cvar); | ||
| 345 | self->event_object = cond; | ||
| 346 | saved_count = lisp_mutex_unlock_for_wait (&mutex->mutex); | ||
| 347 | /* If we were signalled while unlocking, we skip the wait, but we | ||
| 348 | still must reacquire our lock. */ | ||
| 349 | if (NILP (self->error_symbol)) | ||
| 350 | { | ||
| 351 | self->wait_condvar = &cvar->cond; | ||
| 352 | sys_cond_wait (&cvar->cond, &global_lock); | ||
| 353 | self->wait_condvar = NULL; | ||
| 354 | } | ||
| 355 | lisp_mutex_lock (&mutex->mutex, saved_count); | ||
| 356 | self->event_object = Qnil; | ||
| 357 | post_acquire_global_lock (self); | ||
| 358 | } | ||
| 359 | |||
| 360 | DEFUN ("condition-wait", Fcondition_wait, Scondition_wait, 1, 1, 0, | ||
| 361 | doc: /* Wait for the condition variable to be notified. | ||
| 362 | CONDITION is the condition variable to wait on. | ||
| 363 | |||
| 364 | The mutex associated with CONDITION must be held when this is called. | ||
| 365 | It is an error if it is not held. | ||
| 366 | |||
| 367 | This releases the mutex and waits for CONDITION to be notified or for | ||
| 368 | this thread to be signalled with `thread-signal'. When | ||
| 369 | `condition-wait' returns, the mutex will again be locked by this | ||
| 370 | thread. */) | ||
| 371 | (Lisp_Object condition) | ||
| 372 | { | ||
| 373 | struct Lisp_CondVar *cvar; | ||
| 374 | struct Lisp_Mutex *mutex; | ||
| 375 | |||
| 376 | CHECK_CONDVAR (condition); | ||
| 377 | cvar = XCONDVAR (condition); | ||
| 378 | |||
| 379 | mutex = XMUTEX (cvar->mutex); | ||
| 380 | if (!lisp_mutex_owned_p (&mutex->mutex)) | ||
| 381 | error ("fixme"); | ||
| 382 | |||
| 383 | flush_stack_call_func (condition_wait_callback, cvar); | ||
| 384 | |||
| 385 | return Qnil; | ||
| 386 | } | ||
| 387 | |||
| 388 | /* Used to communicate argumnets to condition_notify_callback. */ | ||
| 389 | struct notify_args | ||
| 390 | { | ||
| 391 | struct Lisp_CondVar *cvar; | ||
| 392 | int all; | ||
| 393 | }; | ||
| 394 | |||
| 395 | static void | ||
| 396 | condition_notify_callback (void *arg) | ||
| 397 | { | ||
| 398 | struct notify_args *na = arg; | ||
| 399 | struct Lisp_Mutex *mutex = XMUTEX (na->cvar->mutex); | ||
| 400 | struct thread_state *self = current_thread; | ||
| 401 | unsigned int saved_count; | ||
| 402 | Lisp_Object cond; | ||
| 403 | |||
| 404 | XSETCONDVAR (cond, na->cvar); | ||
| 405 | saved_count = lisp_mutex_unlock_for_wait (&mutex->mutex); | ||
| 406 | if (na->all) | ||
| 407 | sys_cond_broadcast (&na->cvar->cond); | ||
| 408 | else | ||
| 409 | sys_cond_signal (&na->cvar->cond); | ||
| 410 | lisp_mutex_lock (&mutex->mutex, saved_count); | ||
| 411 | post_acquire_global_lock (self); | ||
| 412 | } | ||
| 413 | |||
| 414 | DEFUN ("condition-notify", Fcondition_notify, Scondition_notify, 1, 2, 0, | ||
| 415 | doc: /* Notify a condition variable. | ||
| 416 | This wakes a thread waiting on CONDITION. | ||
| 417 | If ALL is non-nil, all waiting threads are awoken. | ||
| 418 | |||
| 419 | The mutex associated with CONDITION must be held when this is called. | ||
| 420 | It is an error if it is not held. | ||
| 421 | |||
| 422 | This releases the mutex when notifying CONDITION. When | ||
| 423 | `condition-notify' returns, the mutex will again be locked by this | ||
| 424 | thread. */) | ||
| 425 | (Lisp_Object condition, Lisp_Object all) | ||
| 426 | { | ||
| 427 | struct Lisp_CondVar *cvar; | ||
| 428 | struct Lisp_Mutex *mutex; | ||
| 429 | struct notify_args args; | ||
| 430 | |||
| 431 | CHECK_CONDVAR (condition); | ||
| 432 | cvar = XCONDVAR (condition); | ||
| 433 | |||
| 434 | mutex = XMUTEX (cvar->mutex); | ||
| 435 | if (!lisp_mutex_owned_p (&mutex->mutex)) | ||
| 436 | error ("fixme"); | ||
| 437 | |||
| 438 | args.cvar = cvar; | ||
| 439 | args.all = !NILP (all); | ||
| 440 | flush_stack_call_func (condition_notify_callback, &args); | ||
| 441 | |||
| 442 | return Qnil; | ||
| 443 | } | ||
| 444 | |||
| 445 | DEFUN ("condition-mutex", Fcondition_mutex, Scondition_mutex, 1, 1, 0, | ||
| 446 | doc: /* Return the mutex associated with CONDITION. */) | ||
| 447 | (Lisp_Object condition) | ||
| 448 | { | ||
| 449 | struct Lisp_CondVar *cvar; | ||
| 450 | |||
| 451 | CHECK_CONDVAR (condition); | ||
| 452 | cvar = XCONDVAR (condition); | ||
| 453 | |||
| 454 | return cvar->mutex; | ||
| 455 | } | ||
| 456 | |||
| 457 | DEFUN ("condition-name", Fcondition_name, Scondition_name, 1, 1, 0, | ||
| 458 | doc: /* Return the name of CONDITION. | ||
| 459 | If no name was given when CONDITION was created, return nil. */) | ||
| 460 | (Lisp_Object condition) | ||
| 461 | { | ||
| 462 | struct Lisp_CondVar *cvar; | ||
| 463 | |||
| 464 | CHECK_CONDVAR (condition); | ||
| 465 | cvar = XCONDVAR (condition); | ||
| 466 | |||
| 467 | return cvar->name; | ||
| 468 | } | ||
| 469 | |||
| 470 | void | ||
| 471 | finalize_one_condvar (struct Lisp_CondVar *condvar) | ||
| 472 | { | ||
| 473 | sys_cond_destroy (&condvar->cond); | ||
| 474 | } | ||
| 475 | |||
| 476 | |||
| 477 | |||
| 478 | struct select_args | ||
| 479 | { | ||
| 480 | select_func *func; | ||
| 481 | int max_fds; | ||
| 482 | fd_set *rfds; | ||
| 483 | fd_set *wfds; | ||
| 484 | fd_set *efds; | ||
| 485 | struct timespec *timeout; | ||
| 486 | sigset_t *sigmask; | ||
| 487 | int result; | ||
| 488 | }; | ||
| 489 | |||
| 490 | static void | ||
| 491 | really_call_select (void *arg) | ||
| 492 | { | ||
| 493 | struct select_args *sa = arg; | ||
| 494 | struct thread_state *self = current_thread; | ||
| 495 | |||
| 496 | release_global_lock (); | ||
| 497 | sa->result = (sa->func) (sa->max_fds, sa->rfds, sa->wfds, sa->efds, | ||
| 498 | sa->timeout, sa->sigmask); | ||
| 499 | acquire_global_lock (self); | ||
| 500 | } | ||
| 501 | |||
| 502 | int | ||
| 503 | thread_select (select_func *func, int max_fds, fd_set *rfds, | ||
| 504 | fd_set *wfds, fd_set *efds, struct timespec *timeout, | ||
| 505 | sigset_t *sigmask) | ||
| 506 | { | ||
| 507 | struct select_args sa; | ||
| 508 | |||
| 509 | sa.func = func; | ||
| 510 | sa.max_fds = max_fds; | ||
| 511 | sa.rfds = rfds; | ||
| 512 | sa.wfds = wfds; | ||
| 513 | sa.efds = efds; | ||
| 514 | sa.timeout = timeout; | ||
| 515 | sa.sigmask = sigmask; | ||
| 516 | flush_stack_call_func (really_call_select, &sa); | ||
| 517 | return sa.result; | ||
| 518 | } | ||
| 519 | |||
| 520 | |||
| 521 | |||
| 522 | static void | ||
| 523 | mark_one_thread (struct thread_state *thread) | ||
| 524 | { | ||
| 525 | struct handler *handler; | ||
| 526 | Lisp_Object tem; | ||
| 527 | |||
| 528 | mark_specpdl (thread->m_specpdl, thread->m_specpdl_ptr); | ||
| 529 | |||
| 530 | mark_stack (thread->m_stack_bottom, thread->stack_top); | ||
| 531 | |||
| 532 | for (handler = thread->m_handlerlist; handler; handler = handler->next) | ||
| 533 | { | ||
| 534 | mark_object (handler->tag_or_ch); | ||
| 535 | mark_object (handler->val); | ||
| 536 | } | ||
| 537 | |||
| 538 | if (thread->m_current_buffer) | ||
| 539 | { | ||
| 540 | XSETBUFFER (tem, thread->m_current_buffer); | ||
| 541 | mark_object (tem); | ||
| 542 | } | ||
| 543 | |||
| 544 | mark_object (thread->m_last_thing_searched); | ||
| 545 | |||
| 546 | if (thread->m_saved_last_thing_searched) | ||
| 547 | mark_object (thread->m_saved_last_thing_searched); | ||
| 548 | } | ||
| 549 | |||
| 550 | static void | ||
| 551 | mark_threads_callback (void *ignore) | ||
| 552 | { | ||
| 553 | struct thread_state *iter; | ||
| 554 | |||
| 555 | for (iter = all_threads; iter; iter = iter->next_thread) | ||
| 556 | { | ||
| 557 | Lisp_Object thread_obj; | ||
| 558 | |||
| 559 | XSETTHREAD (thread_obj, iter); | ||
| 560 | mark_object (thread_obj); | ||
| 561 | mark_one_thread (iter); | ||
| 562 | } | ||
| 563 | } | ||
| 564 | |||
| 565 | void | ||
| 566 | mark_threads (void) | ||
| 567 | { | ||
| 568 | flush_stack_call_func (mark_threads_callback, NULL); | ||
| 569 | } | ||
| 570 | |||
| 571 | void | ||
| 572 | unmark_threads (void) | ||
| 573 | { | ||
| 574 | struct thread_state *iter; | ||
| 575 | |||
| 576 | for (iter = all_threads; iter; iter = iter->next_thread) | ||
| 577 | if (iter->m_byte_stack_list) | ||
| 578 | relocate_byte_stack (iter->m_byte_stack_list); | ||
| 579 | } | ||
| 580 | |||
| 581 | |||
| 582 | |||
| 583 | static void | ||
| 584 | yield_callback (void *ignore) | ||
| 585 | { | ||
| 586 | struct thread_state *self = current_thread; | ||
| 587 | |||
| 588 | release_global_lock (); | ||
| 589 | sys_thread_yield (); | ||
| 590 | acquire_global_lock (self); | ||
| 591 | } | ||
| 592 | |||
| 593 | DEFUN ("thread-yield", Fthread_yield, Sthread_yield, 0, 0, 0, | ||
| 594 | doc: /* Yield the CPU to another thread. */) | ||
| 595 | (void) | ||
| 596 | { | ||
| 597 | flush_stack_call_func (yield_callback, NULL); | ||
| 598 | return Qnil; | ||
| 599 | } | ||
| 600 | |||
| 601 | static Lisp_Object | ||
| 602 | invoke_thread_function (void) | ||
| 603 | { | ||
| 604 | Lisp_Object iter; | ||
| 605 | volatile struct thread_state *self = current_thread; | ||
| 606 | |||
| 607 | int count = SPECPDL_INDEX (); | ||
| 608 | |||
| 609 | Ffuncall (1, ¤t_thread->function); | ||
| 610 | return unbind_to (count, Qnil); | ||
| 611 | } | ||
| 612 | |||
| 613 | static Lisp_Object | ||
| 614 | do_nothing (Lisp_Object whatever) | ||
| 615 | { | ||
| 616 | return whatever; | ||
| 617 | } | ||
| 618 | |||
| 619 | static void * | ||
| 620 | run_thread (void *state) | ||
| 621 | { | ||
| 622 | char stack_pos; | ||
| 623 | struct thread_state *self = state; | ||
| 624 | struct thread_state **iter; | ||
| 625 | |||
| 626 | self->m_stack_bottom = &stack_pos; | ||
| 627 | self->stack_top = &stack_pos; | ||
| 628 | self->thread_id = sys_thread_self (); | ||
| 629 | |||
| 630 | acquire_global_lock (self); | ||
| 631 | |||
| 632 | { /* Put a dummy catcher at top-level so that handlerlist is never NULL. | ||
| 633 | This is important since handlerlist->nextfree holds the freelist | ||
| 634 | which would otherwise leak every time we unwind back to top-level. */ | ||
| 635 | handlerlist_sentinel = xzalloc (sizeof (struct handler)); | ||
| 636 | handlerlist = handlerlist_sentinel->nextfree = handlerlist_sentinel; | ||
| 637 | struct handler *c = push_handler (Qunbound, CATCHER); | ||
| 638 | eassert (c == handlerlist_sentinel); | ||
| 639 | handlerlist_sentinel->nextfree = NULL; | ||
| 640 | handlerlist_sentinel->next = NULL; | ||
| 641 | } | ||
| 642 | |||
| 643 | /* It might be nice to do something with errors here. */ | ||
| 644 | internal_condition_case (invoke_thread_function, Qt, do_nothing); | ||
| 645 | |||
| 646 | update_processes_for_thread_death (Fcurrent_thread ()); | ||
| 647 | |||
| 648 | xfree (self->m_specpdl - 1); | ||
| 649 | self->m_specpdl = NULL; | ||
| 650 | self->m_specpdl_ptr = NULL; | ||
| 651 | self->m_specpdl_size = 0; | ||
| 652 | |||
| 653 | { | ||
| 654 | struct handler *c, *c_next; | ||
| 655 | for (c = handlerlist_sentinel; c; c = c_next) | ||
| 656 | { | ||
| 657 | c_next = c->nextfree; | ||
| 658 | xfree (c); | ||
| 659 | } | ||
| 660 | } | ||
| 661 | |||
| 662 | current_thread = NULL; | ||
| 663 | sys_cond_broadcast (&self->thread_condvar); | ||
| 664 | |||
| 665 | /* Unlink this thread from the list of all threads. Note that we | ||
| 666 | have to do this very late, after broadcasting our death. | ||
| 667 | Otherwise the GC may decide to reap the thread_state object, | ||
| 668 | leading to crashes. */ | ||
| 669 | for (iter = &all_threads; *iter != self; iter = &(*iter)->next_thread) | ||
| 670 | ; | ||
| 671 | *iter = (*iter)->next_thread; | ||
| 672 | |||
| 673 | release_global_lock (); | ||
| 674 | |||
| 675 | return NULL; | ||
| 676 | } | ||
| 677 | |||
| 678 | void | ||
| 679 | finalize_one_thread (struct thread_state *state) | ||
| 680 | { | ||
| 681 | sys_cond_destroy (&state->thread_condvar); | ||
| 682 | } | ||
| 683 | |||
| 684 | DEFUN ("make-thread", Fmake_thread, Smake_thread, 1, 2, 0, | ||
| 685 | doc: /* Start a new thread and run FUNCTION in it. | ||
| 686 | When the function exits, the thread dies. | ||
| 687 | If NAME is given, it names the new thread. */) | ||
| 688 | (Lisp_Object function, Lisp_Object name) | ||
| 689 | { | ||
| 690 | sys_thread_t thr; | ||
| 691 | struct thread_state *new_thread; | ||
| 692 | Lisp_Object result; | ||
| 693 | const char *c_name = NULL; | ||
| 694 | size_t offset = offsetof (struct thread_state, m_byte_stack_list); | ||
| 695 | |||
| 696 | /* Can't start a thread in temacs. */ | ||
| 697 | if (!initialized) | ||
| 698 | abort (); | ||
| 699 | |||
| 700 | if (!NILP (name)) | ||
| 701 | CHECK_STRING (name); | ||
| 702 | |||
| 703 | new_thread = ALLOCATE_PSEUDOVECTOR (struct thread_state, m_byte_stack_list, | ||
| 704 | PVEC_THREAD); | ||
| 705 | memset ((char *) new_thread + offset, 0, | ||
| 706 | sizeof (struct thread_state) - offset); | ||
| 707 | |||
| 708 | new_thread->function = function; | ||
| 709 | new_thread->name = name; | ||
| 710 | new_thread->m_last_thing_searched = Qnil; /* copy from parent? */ | ||
| 711 | new_thread->m_saved_last_thing_searched = Qnil; | ||
| 712 | new_thread->m_current_buffer = current_thread->m_current_buffer; | ||
| 713 | new_thread->error_symbol = Qnil; | ||
| 714 | new_thread->error_data = Qnil; | ||
| 715 | new_thread->event_object = Qnil; | ||
| 716 | |||
| 717 | new_thread->m_specpdl_size = 50; | ||
| 718 | new_thread->m_specpdl = xmalloc ((1 + new_thread->m_specpdl_size) | ||
| 719 | * sizeof (union specbinding)); | ||
| 720 | /* Skip the dummy entry. */ | ||
| 721 | ++new_thread->m_specpdl; | ||
| 722 | new_thread->m_specpdl_ptr = new_thread->m_specpdl; | ||
| 723 | |||
| 724 | sys_cond_init (&new_thread->thread_condvar); | ||
| 725 | |||
| 726 | /* We'll need locking here eventually. */ | ||
| 727 | new_thread->next_thread = all_threads; | ||
| 728 | all_threads = new_thread; | ||
| 729 | |||
| 730 | if (!NILP (name)) | ||
| 731 | c_name = SSDATA (ENCODE_UTF_8 (name)); | ||
| 732 | |||
| 733 | if (! sys_thread_create (&thr, c_name, run_thread, new_thread)) | ||
| 734 | { | ||
| 735 | /* Restore the previous situation. */ | ||
| 736 | all_threads = all_threads->next_thread; | ||
| 737 | error ("Could not start a new thread"); | ||
| 738 | } | ||
| 739 | |||
| 740 | /* FIXME: race here where new thread might not be filled in? */ | ||
| 741 | XSETTHREAD (result, new_thread); | ||
| 742 | return result; | ||
| 743 | } | ||
| 744 | |||
| 745 | DEFUN ("current-thread", Fcurrent_thread, Scurrent_thread, 0, 0, 0, | ||
| 746 | doc: /* Return the current thread. */) | ||
| 747 | (void) | ||
| 748 | { | ||
| 749 | Lisp_Object result; | ||
| 750 | XSETTHREAD (result, current_thread); | ||
| 751 | return result; | ||
| 752 | } | ||
| 753 | |||
| 754 | DEFUN ("thread-name", Fthread_name, Sthread_name, 1, 1, 0, | ||
| 755 | doc: /* Return the name of the THREAD. | ||
| 756 | The name is the same object that was passed to `make-thread'. */) | ||
| 757 | (Lisp_Object thread) | ||
| 758 | { | ||
| 759 | struct thread_state *tstate; | ||
| 760 | |||
| 761 | CHECK_THREAD (thread); | ||
| 762 | tstate = XTHREAD (thread); | ||
| 763 | |||
| 764 | return tstate->name; | ||
| 765 | } | ||
| 766 | |||
| 767 | static void | ||
| 768 | thread_signal_callback (void *arg) | ||
| 769 | { | ||
| 770 | struct thread_state *tstate = arg; | ||
| 771 | struct thread_state *self = current_thread; | ||
| 772 | |||
| 773 | sys_cond_broadcast (tstate->wait_condvar); | ||
| 774 | post_acquire_global_lock (self); | ||
| 775 | } | ||
| 776 | |||
| 777 | DEFUN ("thread-signal", Fthread_signal, Sthread_signal, 3, 3, 0, | ||
| 778 | doc: /* Signal an error in a thread. | ||
| 779 | This acts like `signal', but arranges for the signal to be raised | ||
| 780 | in THREAD. If THREAD is the current thread, acts just like `signal'. | ||
| 781 | This will interrupt a blocked call to `mutex-lock', `condition-wait', | ||
| 782 | or `thread-join' in the target thread. */) | ||
| 783 | (Lisp_Object thread, Lisp_Object error_symbol, Lisp_Object data) | ||
| 784 | { | ||
| 785 | struct thread_state *tstate; | ||
| 786 | |||
| 787 | CHECK_THREAD (thread); | ||
| 788 | tstate = XTHREAD (thread); | ||
| 789 | |||
| 790 | if (tstate == current_thread) | ||
| 791 | Fsignal (error_symbol, data); | ||
| 792 | |||
| 793 | /* What to do if thread is already signalled? */ | ||
| 794 | /* What if error_symbol is Qnil? */ | ||
| 795 | tstate->error_symbol = error_symbol; | ||
| 796 | tstate->error_data = data; | ||
| 797 | |||
| 798 | if (tstate->wait_condvar) | ||
| 799 | flush_stack_call_func (thread_signal_callback, tstate); | ||
| 800 | |||
| 801 | return Qnil; | ||
| 802 | } | ||
| 803 | |||
| 804 | DEFUN ("thread-alive-p", Fthread_alive_p, Sthread_alive_p, 1, 1, 0, | ||
| 805 | doc: /* Return t if THREAD is alive, or nil if it has exited. */) | ||
| 806 | (Lisp_Object thread) | ||
| 807 | { | ||
| 808 | struct thread_state *tstate; | ||
| 809 | |||
| 810 | CHECK_THREAD (thread); | ||
| 811 | tstate = XTHREAD (thread); | ||
| 812 | |||
| 813 | return thread_alive_p (tstate) ? Qt : Qnil; | ||
| 814 | } | ||
| 815 | |||
| 816 | DEFUN ("thread--blocker", Fthread_blocker, Sthread_blocker, 1, 1, 0, | ||
| 817 | doc: /* Return the object that THREAD is blocking on. | ||
| 818 | If THREAD is blocked in `thread-join' on a second thread, return that | ||
| 819 | thread. | ||
| 820 | If THREAD is blocked in `mutex-lock', return the mutex. | ||
| 821 | If THREAD is blocked in `condition-wait', return the condition variable. | ||
| 822 | Otherwise, if THREAD is not blocked, return nil. */) | ||
| 823 | (Lisp_Object thread) | ||
| 824 | { | ||
| 825 | struct thread_state *tstate; | ||
| 826 | |||
| 827 | CHECK_THREAD (thread); | ||
| 828 | tstate = XTHREAD (thread); | ||
| 829 | |||
| 830 | return tstate->event_object; | ||
| 831 | } | ||
| 832 | |||
| 833 | static void | ||
| 834 | thread_join_callback (void *arg) | ||
| 835 | { | ||
| 836 | struct thread_state *tstate = arg; | ||
| 837 | struct thread_state *self = current_thread; | ||
| 838 | Lisp_Object thread; | ||
| 839 | |||
| 840 | XSETTHREAD (thread, tstate); | ||
| 841 | self->event_object = thread; | ||
| 842 | self->wait_condvar = &tstate->thread_condvar; | ||
| 843 | while (thread_alive_p (tstate) && NILP (self->error_symbol)) | ||
| 844 | sys_cond_wait (self->wait_condvar, &global_lock); | ||
| 845 | |||
| 846 | self->wait_condvar = NULL; | ||
| 847 | self->event_object = Qnil; | ||
| 848 | post_acquire_global_lock (self); | ||
| 849 | } | ||
| 850 | |||
| 851 | DEFUN ("thread-join", Fthread_join, Sthread_join, 1, 1, 0, | ||
| 852 | doc: /* Wait for a thread to exit. | ||
| 853 | This blocks the current thread until THREAD exits. | ||
| 854 | It is an error for a thread to try to join itself. */) | ||
| 855 | (Lisp_Object thread) | ||
| 856 | { | ||
| 857 | struct thread_state *tstate; | ||
| 858 | |||
| 859 | CHECK_THREAD (thread); | ||
| 860 | tstate = XTHREAD (thread); | ||
| 861 | |||
| 862 | if (tstate == current_thread) | ||
| 863 | error ("cannot join current thread"); | ||
| 864 | |||
| 865 | if (thread_alive_p (tstate)) | ||
| 866 | flush_stack_call_func (thread_join_callback, tstate); | ||
| 867 | |||
| 868 | return Qnil; | ||
| 869 | } | ||
| 870 | |||
| 871 | DEFUN ("all-threads", Fall_threads, Sall_threads, 0, 0, 0, | ||
| 872 | doc: /* Return a list of all threads. */) | ||
| 873 | (void) | ||
| 874 | { | ||
| 875 | Lisp_Object result = Qnil; | ||
| 876 | struct thread_state *iter; | ||
| 877 | |||
| 878 | for (iter = all_threads; iter; iter = iter->next_thread) | ||
| 879 | { | ||
| 880 | if (thread_alive_p (iter)) | ||
| 881 | { | ||
| 882 | Lisp_Object thread; | ||
| 883 | |||
| 884 | XSETTHREAD (thread, iter); | ||
| 885 | result = Fcons (thread, result); | ||
| 886 | } | ||
| 887 | } | ||
| 888 | |||
| 889 | return result; | ||
| 890 | } | ||
| 891 | |||
| 892 | |||
| 893 | |||
| 894 | bool | ||
| 895 | thread_check_current_buffer (struct buffer *buffer) | ||
| 896 | { | ||
| 897 | struct thread_state *iter; | ||
| 898 | |||
| 899 | for (iter = all_threads; iter; iter = iter->next_thread) | ||
| 900 | { | ||
| 901 | if (iter == current_thread) | ||
| 902 | continue; | ||
| 903 | |||
| 904 | if (iter->m_current_buffer == buffer) | ||
| 905 | return true; | ||
| 906 | } | ||
| 907 | |||
| 908 | return false; | ||
| 909 | } | ||
| 910 | |||
| 911 | |||
| 912 | |||
| 913 | static void | ||
| 914 | init_primary_thread (void) | ||
| 915 | { | ||
| 916 | primary_thread.header.size | ||
| 917 | = PSEUDOVECSIZE (struct thread_state, m_byte_stack_list); | ||
| 918 | XSETPVECTYPE (&primary_thread, PVEC_THREAD); | ||
| 919 | primary_thread.m_last_thing_searched = Qnil; | ||
| 920 | primary_thread.m_saved_last_thing_searched = Qnil; | ||
| 921 | primary_thread.name = Qnil; | ||
| 922 | primary_thread.function = Qnil; | ||
| 923 | primary_thread.error_symbol = Qnil; | ||
| 924 | primary_thread.error_data = Qnil; | ||
| 925 | primary_thread.event_object = Qnil; | ||
| 926 | } | ||
| 927 | |||
| 928 | void | ||
| 929 | init_threads_once (void) | ||
| 930 | { | ||
| 931 | init_primary_thread (); | ||
| 932 | } | ||
| 933 | |||
| 934 | void | ||
| 935 | init_threads (void) | ||
| 936 | { | ||
| 937 | init_primary_thread (); | ||
| 938 | sys_cond_init (&primary_thread.thread_condvar); | ||
| 939 | sys_mutex_init (&global_lock); | ||
| 940 | sys_mutex_lock (&global_lock); | ||
| 941 | current_thread = &primary_thread; | ||
| 942 | primary_thread.thread_id = sys_thread_self (); | ||
| 943 | } | ||
| 944 | |||
| 945 | void | ||
| 946 | syms_of_threads (void) | ||
| 947 | { | ||
| 948 | #ifndef THREADS_ENABLED | ||
| 949 | if (0) | ||
| 950 | #endif | ||
| 951 | { | ||
| 952 | defsubr (&Sthread_yield); | ||
| 953 | defsubr (&Smake_thread); | ||
| 954 | defsubr (&Scurrent_thread); | ||
| 955 | defsubr (&Sthread_name); | ||
| 956 | defsubr (&Sthread_signal); | ||
| 957 | defsubr (&Sthread_alive_p); | ||
| 958 | defsubr (&Sthread_join); | ||
| 959 | defsubr (&Sthread_blocker); | ||
| 960 | defsubr (&Sall_threads); | ||
| 961 | defsubr (&Smake_mutex); | ||
| 962 | defsubr (&Smutex_lock); | ||
| 963 | defsubr (&Smutex_unlock); | ||
| 964 | defsubr (&Smutex_name); | ||
| 965 | defsubr (&Smake_condition_variable); | ||
| 966 | defsubr (&Scondition_wait); | ||
| 967 | defsubr (&Scondition_notify); | ||
| 968 | defsubr (&Scondition_mutex); | ||
| 969 | defsubr (&Scondition_name); | ||
| 970 | } | ||
| 971 | |||
| 972 | DEFSYM (Qthreadp, "threadp"); | ||
| 973 | DEFSYM (Qmutexp, "mutexp"); | ||
| 974 | DEFSYM (Qcondition_variable_p, "condition-variable-p"); | ||
| 975 | } | ||