Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code | Sign in
(116)

Side by Side Diff: Modules/_threadmodule.c

Issue 15139: Speed up threading.Condition wakeup
Patch Set: Created 10 months, 2 weeks ago
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments. Please Sign in to add in-line comments.
Jump to:
View unified diff | Download patch
« no previous file with comments | « Lib/threading.py ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 1
2 /* Thread module */ 2 /* Thread module */
3 /* Interface to Sjoerd's portable C thread library */ 3 /* Interface to Sjoerd's portable C thread library */
4 4
5 #include "Python.h" 5 #include "Python.h"
6 #include "structmember.h" /* offsetof */ 6 #include "structmember.h" /* offsetof */
7 7
8 #ifndef WITH_THREAD 8 #ifndef WITH_THREAD
9 #error "Error! The rest of Python is not compiled with thread support." 9 #error "Error! The rest of Python is not compiled with thread support."
10 #error "Rerun configure, adding a --with-threads option." 10 #error "Rerun configure, adding a --with-threads option."
(...skipping 171 matching lines...) Expand 10 before | Expand all | Expand 10 after
182 lock_locked_lock(lockobject *self) 182 lock_locked_lock(lockobject *self)
183 { 183 {
184 return PyBool_FromLong((long)self->locked); 184 return PyBool_FromLong((long)self->locked);
185 } 185 }
186 186
187 PyDoc_STRVAR(locked_doc, 187 PyDoc_STRVAR(locked_doc,
188 "locked() -> bool\n\ 188 "locked() -> bool\n\
189 (locked_lock() is an obsolete synonym)\n\ 189 (locked_lock() is an obsolete synonym)\n\
190 \n\ 190 \n\
191 Return whether the lock is in the locked state."); 191 Return whether the lock is in the locked state.");
192
193 PyDoc_STRVAR(lock_release_save_doc,
194 "_release_save() -> state\n\
195 \n\
196 For internal use by `threading.Condition`.");
197
198 /* restore does nothing, since _acquire_condition was used which did it
199 * automatically
200 */
201 static PyObject *
202 lock_acquire_restore(lockobject *self, PyObject *args)
203 {
204 Py_RETURN_NONE;
205 }
206
207 PyDoc_STRVAR(lock_acquire_restore_doc,
208 "_acquire_restore(state) -> None\n\
209 \n\
210 For internal use by `threading.Condition`.");
211
212 /* forward decleration */
213 static PyObject *
214 lock_acquire_condition(lockobject *self, PyObject *args, PyObject *kwds);
215
216 PyDoc_STRVAR(lock_acquire_condition_doc,
217 "_acquire_condition(timeout, outer, state) -> None\n\
218 \n\
219 For internal use by `threading.Condition`.");
192 220
193 static PyMethodDef lock_methods[] = { 221 static PyMethodDef lock_methods[] = {
194 {"acquire_lock", (PyCFunction)lock_PyThread_acquire_lock, 222 {"acquire_lock", (PyCFunction)lock_PyThread_acquire_lock,
195 METH_VARARGS | METH_KEYWORDS, acquire_doc}, 223 METH_VARARGS | METH_KEYWORDS, acquire_doc},
196 {"acquire", (PyCFunction)lock_PyThread_acquire_lock, 224 {"acquire", (PyCFunction)lock_PyThread_acquire_lock,
197 METH_VARARGS | METH_KEYWORDS, acquire_doc}, 225 METH_VARARGS | METH_KEYWORDS, acquire_doc},
198 {"release_lock", (PyCFunction)lock_PyThread_release_lock, 226 {"release_lock", (PyCFunction)lock_PyThread_release_lock,
199 METH_NOARGS, release_doc}, 227 METH_NOARGS, release_doc},
200 {"release", (PyCFunction)lock_PyThread_release_lock, 228 {"release", (PyCFunction)lock_PyThread_release_lock,
201 METH_NOARGS, release_doc}, 229 METH_NOARGS, release_doc},
202 {"locked_lock", (PyCFunction)lock_locked_lock, 230 {"locked_lock", (PyCFunction)lock_locked_lock,
203 METH_NOARGS, locked_doc}, 231 METH_NOARGS, locked_doc},
204 {"locked", (PyCFunction)lock_locked_lock, 232 {"locked", (PyCFunction)lock_locked_lock,
205 METH_NOARGS, locked_doc}, 233 METH_NOARGS, locked_doc},
206 {"__enter__", (PyCFunction)lock_PyThread_acquire_lock, 234 {"__enter__", (PyCFunction)lock_PyThread_acquire_lock,
207 METH_VARARGS | METH_KEYWORDS, acquire_doc}, 235 METH_VARARGS | METH_KEYWORDS, acquire_doc},
208 {"__exit__", (PyCFunction)lock_PyThread_release_lock, 236 {"__exit__", (PyCFunction)lock_PyThread_release_lock,
209 METH_VARARGS, release_doc}, 237 METH_VARARGS, release_doc},
238 {"_release_save", (PyCFunction)lock_PyThread_release_lock,
239 METH_NOARGS, lock_release_save_doc},
240 {"_acquire_restore", (PyCFunction)lock_acquire_restore,
241 METH_VARARGS, lock_acquire_restore_doc},
242 {"_acquire_condition", (PyCFunction)lock_acquire_condition,
243 METH_VARARGS, lock_acquire_condition_doc},
210 {NULL, NULL} /* sentinel */ 244 {NULL, NULL} /* sentinel */
211 }; 245 };
212 246
213 static PyTypeObject Locktype = { 247 static PyTypeObject Locktype = {
214 PyVarObject_HEAD_INIT(&PyType_Type, 0) 248 PyVarObject_HEAD_INIT(&PyType_Type, 0)
215 "_thread.lock", /*tp_name*/ 249 "_thread.lock", /*tp_name*/
216 sizeof(lockobject), /*tp_size*/ 250 sizeof(lockobject), /*tp_size*/
217 0, /*tp_itemsize*/ 251 0, /*tp_itemsize*/
218 /* methods */ 252 /* methods */
219 (destructor)lock_dealloc, /*tp_dealloc*/ 253 (destructor)lock_dealloc, /*tp_dealloc*/
(...skipping 145 matching lines...) Expand 10 before | Expand all | Expand 10 after
365 "release()\n\ 399 "release()\n\
366 \n\ 400 \n\
367 Release the lock, allowing another thread that is blocked waiting for\n\ 401 Release the lock, allowing another thread that is blocked waiting for\n\
368 the lock to acquire the lock. The lock must be in the locked state,\n\ 402 the lock to acquire the lock. The lock must be in the locked state,\n\
369 and must be locked by the same thread that unlocks it; otherwise a\n\ 403 and must be locked by the same thread that unlocks it; otherwise a\n\
370 `RuntimeError` is raised.\n\ 404 `RuntimeError` is raised.\n\
371 \n\ 405 \n\
372 Do note that if the lock was acquire()d several times in a row by the\n\ 406 Do note that if the lock was acquire()d several times in a row by the\n\
373 current thread, release() needs to be called as many times for the lock\n\ 407 current thread, release() needs to be called as many times for the lock\n\
374 to be available for other threads."); 408 to be available for other threads.");
375
376 static PyObject *
377 rlock_acquire_restore(rlockobject *self, PyObject *arg)
378 {
379 long owner;
380 unsigned long count;
381 int r = 1;
382
383 if (!PyArg_ParseTuple(arg, "kl:_acquire_restore", &count, &owner))
384 return NULL;
385
386 if (!PyThread_acquire_lock(self->rlock_lock, 0)) {
387 Py_BEGIN_ALLOW_THREADS
388 r = PyThread_acquire_lock(self->rlock_lock, 1);
389 Py_END_ALLOW_THREADS
390 }
391 if (!r) {
392 PyErr_SetString(ThreadError, "couldn't acquire lock");
393 return NULL;
394 }
395 assert(self->rlock_count == 0);
396 self->rlock_owner = owner;
397 self->rlock_count = count;
398 Py_RETURN_NONE;
399 }
400
401 PyDoc_STRVAR(rlock_acquire_restore_doc,
402 "_acquire_restore(state) -> None\n\
403 \n\
404 For internal use by `threading.Condition`.");
405 409
406 static PyObject * 410 static PyObject *
407 rlock_release_save(rlockobject *self) 411 rlock_release_save(rlockobject *self)
408 { 412 {
409 long owner; 413 long owner;
410 unsigned long count; 414 unsigned long count;
411 415
412 if (self->rlock_count == 0) { 416 if (self->rlock_count == 0) {
413 PyErr_SetString(PyExc_RuntimeError, 417 PyErr_SetString(PyExc_RuntimeError,
414 "cannot release un-acquired lock"); 418 "cannot release un-acquired lock");
(...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after
474 } 478 }
475 479
476 480
477 static PyMethodDef rlock_methods[] = { 481 static PyMethodDef rlock_methods[] = {
478 {"acquire", (PyCFunction)rlock_acquire, 482 {"acquire", (PyCFunction)rlock_acquire,
479 METH_VARARGS | METH_KEYWORDS, rlock_acquire_doc}, 483 METH_VARARGS | METH_KEYWORDS, rlock_acquire_doc},
480 {"release", (PyCFunction)rlock_release, 484 {"release", (PyCFunction)rlock_release,
481 METH_NOARGS, rlock_release_doc}, 485 METH_NOARGS, rlock_release_doc},
482 {"_is_owned", (PyCFunction)rlock_is_owned, 486 {"_is_owned", (PyCFunction)rlock_is_owned,
483 METH_NOARGS, rlock_is_owned_doc}, 487 METH_NOARGS, rlock_is_owned_doc},
484 {"_acquire_restore", (PyCFunction)rlock_acquire_restore, 488 {"_acquire_restore", (PyCFunction)lock_acquire_restore,
485 METH_O, rlock_acquire_restore_doc}, 489 METH_O, lock_acquire_restore_doc},
486 {"_release_save", (PyCFunction)rlock_release_save, 490 {"_release_save", (PyCFunction)rlock_release_save,
487 METH_NOARGS, rlock_release_save_doc}, 491 METH_NOARGS, rlock_release_save_doc},
488 {"__enter__", (PyCFunction)rlock_acquire, 492 {"__enter__", (PyCFunction)rlock_acquire,
489 METH_VARARGS | METH_KEYWORDS, rlock_acquire_doc}, 493 METH_VARARGS | METH_KEYWORDS, rlock_acquire_doc},
490 {"__exit__", (PyCFunction)rlock_release, 494 {"__exit__", (PyCFunction)rlock_release,
491 METH_VARARGS, rlock_release_doc}, 495 METH_VARARGS, rlock_release_doc},
492 {NULL, NULL} /* sentinel */ 496 {NULL, NULL} /* sentinel */
493 }; 497 };
494 498
495 499
(...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after
544 return NULL; 548 return NULL;
545 self->lock_lock = PyThread_allocate_lock(); 549 self->lock_lock = PyThread_allocate_lock();
546 self->locked = 0; 550 self->locked = 0;
547 self->in_weakreflist = NULL; 551 self->in_weakreflist = NULL;
548 if (self->lock_lock == NULL) { 552 if (self->lock_lock == NULL) {
549 Py_DECREF(self); 553 Py_DECREF(self);
550 PyErr_SetString(ThreadError, "can't allocate lock"); 554 PyErr_SetString(ThreadError, "can't allocate lock");
551 return NULL; 555 return NULL;
552 } 556 }
553 return self; 557 return self;
558 }
559
560 static PyObject *
561 lock_acquire_condition(lockobject *self, PyObject *args, PyObject *kwds)
562 {
563 PyObject *timeout_obj;
564 PyObject *outer_lock;
565 PyObject *outer_arg;
566 double timeout;
567 unsigned long count;
568 long owner;
569 PY_TIMEOUT_T microseconds;
570 PyLockStatus r;
571
572 /* if we can't even parse the arguments, then we cannot re-acquire the outer lock. But that
573 * is the same if passing wrong arguments to a _aquire_restore method.
574 */
575 if (!PyArg_ParseTuple(args, "OOO:_acquire_condition", &timeout_obj, &outer_l ock, &outer_arg))
576 return NULL;
577 if (Py_TYPE(outer_lock) == &RLocktype)
578 if (!PyArg_ParseTuple(outer_arg, "kl", &count, &owner))
579 return NULL;
580
581 /* None is infinite, otherwise a float is the timeout in seconds, negative v alues
582 * are clipped to zero
583 */
584 if (timeout_obj == Py_None) {
585 microseconds = -1;
586 } else {
587 timeout = PyFloat_AsDouble(timeout_obj);
588 if (timeout == -1.0 && PyErr_Occurred())
589 goto ERR;
590 if (timeout > 0.0) {
591 timeout *= 1e6;
592 if (timeout >= (double) PY_TIMEOUT_MAX) {
593 PyErr_SetString(PyExc_OverflowError,
594 "timeout value is too large");
595 goto ERR;
596 }
597 microseconds = (PY_TIMEOUT_T) timeout;
598 } else
599 microseconds = 0;
600 }
601
602 r = PY_LOCK_ACQUIRED;
603 goto ACQUIRE;
604 ERR:
605 r = PY_LOCK_INTR; /* just acquire the outer lock and exit with error */
606 ACQUIRE:
607 /* acquire the condition lock _and_ the outer lock before reacquiring the GI L */
608 Py_BEGIN_ALLOW_THREADS
609 if (r == PY_LOCK_ACQUIRED)
610 r = PyThread_acquire_lock_timed(self->lock_lock, microseconds, 1);
611 /* acquire the outer lock if we know it */
612 if (Py_TYPE(outer_lock) == &Locktype) {
613 PyThread_acquire_lock_timed(((lockobject*)outer_lock)->lock_lock, -1, 0) ;
614 } else if(Py_TYPE(outer_lock) == &RLocktype) {
615 rlockobject *rlock = (rlockobject*)outer_lock;
616 PyThread_acquire_lock_timed(rlock->rlock_lock, -1, 0);
617 }
618 Py_END_ALLOW_THREADS
619 /* and restore the state of the locks */
620 if (Py_TYPE(outer_lock) == &Locktype) {
621 ((lockobject*)outer_lock)->locked = 1;
622 } else if (Py_TYPE(outer_lock) == &RLocktype) {
623 rlockobject *rlock = (rlockobject*)outer_lock;
624 rlock->rlock_count = count;
625 rlock->rlock_owner = owner;
626 }
627
628 if (r == PY_LOCK_INTR) {
629 return NULL;
630 }
631
632 return PyBool_FromLong(r == PY_LOCK_ACQUIRED);
554 } 633 }
555 634
556 /* Thread-local objects */ 635 /* Thread-local objects */
557 636
558 #include "structmember.h" 637 #include "structmember.h"
559 638
560 /* Quick overview: 639 /* Quick overview:
561 640
562 We need to be able to reclaim reference cycles as soon as possible 641 We need to be able to reclaim reference cycles as soon as possible
563 (both when a thread is being terminated, or a thread-local object 642 (both when a thread is being terminated, or a thread-local object
(...skipping 765 matching lines...) Expand 10 before | Expand all | Expand 10 after
1329 nb_threads = 0; 1408 nb_threads = 0;
1330 1409
1331 str_dict = PyUnicode_InternFromString("__dict__"); 1410 str_dict = PyUnicode_InternFromString("__dict__");
1332 if (str_dict == NULL) 1411 if (str_dict == NULL)
1333 return NULL; 1412 return NULL;
1334 1413
1335 /* Initialize the C thread library */ 1414 /* Initialize the C thread library */
1336 PyThread_init_thread(); 1415 PyThread_init_thread();
1337 return m; 1416 return m;
1338 } 1417 }
OLDNEW
« no previous file with comments | « Lib/threading.py ('k') | no next file » | no next file with comments »

RSS Feeds Recent Issues | This issue
This is Rietveld cbc36f91f3f7