Home | History | Annotate | Download | only in Modules
      1 
      2 /* Thread module */
      3 /* Interface to Sjoerd's portable C thread library */
      4 
      5 #include "Python.h"
      6 #include "structmember.h" /* offsetof */
      7 
      8 #ifndef WITH_THREAD
      9 #error "Error!  The rest of Python is not compiled with thread support."
     10 #error "Rerun configure, adding a --with-threads option."
     11 #error "Then run `make clean' followed by `make'."
     12 #endif
     13 
     14 #include "pythread.h"
     15 
     16 static PyObject *ThreadError;
     17 static PyObject *str_dict;
     18 static long nb_threads = 0;
     19 
     20 /* Lock objects */
     21 
     22 typedef struct {
     23     PyObject_HEAD
     24     PyThread_type_lock lock_lock;
     25     PyObject *in_weakreflist;
     26 } lockobject;
     27 
     28 static void
     29 lock_dealloc(lockobject *self)
     30 {
     31     if (self->in_weakreflist != NULL)
     32         PyObject_ClearWeakRefs((PyObject *) self);
     33     if (self->lock_lock != NULL) {
     34         /* Unlock the lock so it's safe to free it */
     35         PyThread_acquire_lock(self->lock_lock, 0);
     36         PyThread_release_lock(self->lock_lock);
     37 
     38         PyThread_free_lock(self->lock_lock);
     39     }
     40     PyObject_Del(self);
     41 }
     42 
     43 static PyObject *
     44 lock_PyThread_acquire_lock(lockobject *self, PyObject *args)
     45 {
     46     int i = 1;
     47 
     48     if (!PyArg_ParseTuple(args, "|i:acquire", &i))
     49         return NULL;
     50 
     51     Py_BEGIN_ALLOW_THREADS
     52     i = PyThread_acquire_lock(self->lock_lock, i);
     53     Py_END_ALLOW_THREADS
     54 
     55     return PyBool_FromLong((long)i);
     56 }
     57 
     58 PyDoc_STRVAR(acquire_doc,
     59 "acquire([wait]) -> None or bool\n\
     60 (acquire_lock() is an obsolete synonym)\n\
     61 \n\
     62 Lock the lock.  Without argument, this blocks if the lock is already\n\
     63 locked (even by the same thread), waiting for another thread to release\n\
     64 the lock, and return None once the lock is acquired.\n\
     65 With an argument, this will only block if the argument is true,\n\
     66 and the return value reflects whether the lock is acquired.\n\
     67 The blocking operation is not interruptible.");
     68 
     69 static PyObject *
     70 lock_PyThread_release_lock(lockobject *self)
     71 {
     72     /* Sanity check: the lock must be locked */
     73     if (PyThread_acquire_lock(self->lock_lock, 0)) {
     74         PyThread_release_lock(self->lock_lock);
     75         PyErr_SetString(ThreadError, "release unlocked lock");
     76         return NULL;
     77     }
     78 
     79     PyThread_release_lock(self->lock_lock);
     80     Py_INCREF(Py_None);
     81     return Py_None;
     82 }
     83 
     84 PyDoc_STRVAR(release_doc,
     85 "release()\n\
     86 (release_lock() is an obsolete synonym)\n\
     87 \n\
     88 Release the lock, allowing another thread that is blocked waiting for\n\
     89 the lock to acquire the lock.  The lock must be in the locked state,\n\
     90 but it needn't be locked by the same thread that unlocks it.");
     91 
     92 static PyObject *
     93 lock_locked_lock(lockobject *self)
     94 {
     95     if (PyThread_acquire_lock(self->lock_lock, 0)) {
     96         PyThread_release_lock(self->lock_lock);
     97         return PyBool_FromLong(0L);
     98     }
     99     return PyBool_FromLong(1L);
    100 }
    101 
    102 PyDoc_STRVAR(locked_doc,
    103 "locked() -> bool\n\
    104 (locked_lock() is an obsolete synonym)\n\
    105 \n\
    106 Return whether the lock is in the locked state.");
    107 
    108 static PyMethodDef lock_methods[] = {
    109     {"acquire_lock", (PyCFunction)lock_PyThread_acquire_lock,
    110      METH_VARARGS, acquire_doc},
    111     {"acquire",      (PyCFunction)lock_PyThread_acquire_lock,
    112      METH_VARARGS, acquire_doc},
    113     {"release_lock", (PyCFunction)lock_PyThread_release_lock,
    114      METH_NOARGS, release_doc},
    115     {"release",      (PyCFunction)lock_PyThread_release_lock,
    116      METH_NOARGS, release_doc},
    117     {"locked_lock",  (PyCFunction)lock_locked_lock,
    118      METH_NOARGS, locked_doc},
    119     {"locked",       (PyCFunction)lock_locked_lock,
    120      METH_NOARGS, locked_doc},
    121     {"__enter__",    (PyCFunction)lock_PyThread_acquire_lock,
    122      METH_VARARGS, acquire_doc},
    123     {"__exit__",    (PyCFunction)lock_PyThread_release_lock,
    124      METH_VARARGS, release_doc},
    125     {NULL}              /* sentinel */
    126 };
    127 
    128 static PyTypeObject Locktype = {
    129     PyVarObject_HEAD_INIT(&PyType_Type, 0)
    130     "thread.lock",                      /*tp_name*/
    131     sizeof(lockobject),                 /*tp_size*/
    132     0,                                  /*tp_itemsize*/
    133     /* methods */
    134     (destructor)lock_dealloc,           /*tp_dealloc*/
    135     0,                                  /*tp_print*/
    136     0,                                  /*tp_getattr*/
    137     0,                                  /*tp_setattr*/
    138     0,                                  /*tp_compare*/
    139     0,                                  /*tp_repr*/
    140     0,                                  /* tp_as_number */
    141     0,                                  /* tp_as_sequence */
    142     0,                                  /* tp_as_mapping */
    143     0,                                  /* tp_hash */
    144     0,                                  /* tp_call */
    145     0,                                  /* tp_str */
    146     0,                                  /* tp_getattro */
    147     0,                                  /* tp_setattro */
    148     0,                                  /* tp_as_buffer */
    149     Py_TPFLAGS_HAVE_WEAKREFS,       /* tp_flags */
    150     0,                                  /* tp_doc */
    151     0,                                  /* tp_traverse */
    152     0,                                  /* tp_clear */
    153     0,                                  /* tp_richcompare */
    154     offsetof(lockobject, in_weakreflist),       /* tp_weaklistoffset */
    155     0,                                  /* tp_iter */
    156     0,                                  /* tp_iternext */
    157     lock_methods,                       /* tp_methods */
    158 };
    159 
    160 static lockobject *
    161 newlockobject(void)
    162 {
    163     lockobject *self;
    164     self = PyObject_New(lockobject, &Locktype);
    165     if (self == NULL)
    166         return NULL;
    167     self->lock_lock = PyThread_allocate_lock();
    168     self->in_weakreflist = NULL;
    169     if (self->lock_lock == NULL) {
    170         Py_DECREF(self);
    171         PyErr_SetString(ThreadError, "can't allocate lock");
    172         return NULL;
    173     }
    174     return self;
    175 }
    176 
    177 /* Thread-local objects */
    178 
    179 #include "structmember.h"
    180 
    181 /* Quick overview:
    182 
    183    We need to be able to reclaim reference cycles as soon as possible
    184    (both when a thread is being terminated, or a thread-local object
    185     becomes unreachable from user data).  Constraints:
    186    - it must not be possible for thread-state dicts to be involved in
    187      reference cycles (otherwise the cyclic GC will refuse to consider
    188      objects referenced from a reachable thread-state dict, even though
    189      local_dealloc would clear them)
    190    - the death of a thread-state dict must still imply destruction of the
    191      corresponding local dicts in all thread-local objects.
    192 
    193    Our implementation uses small "localdummy" objects in order to break
    194    the reference chain. These trivial objects are hashable (using the
    195    default scheme of identity hashing) and weakrefable.
    196    Each thread-state holds a separate localdummy for each local object
    197    (as a /strong reference/),
    198    and each thread-local object holds a dict mapping /weak references/
    199    of localdummies to local dicts.
    200 
    201    Therefore:
    202    - only the thread-state dict holds a strong reference to the dummies
    203    - only the thread-local object holds a strong reference to the local dicts
    204    - only outside objects (application- or library-level) hold strong
    205      references to the thread-local objects
    206    - as soon as a thread-state dict is destroyed, the weakref callbacks of all
    207      dummies attached to that thread are called, and destroy the corresponding
    208      local dicts from thread-local objects
    209    - as soon as a thread-local object is destroyed, its local dicts are
    210      destroyed and its dummies are manually removed from all thread states
    211    - the GC can do its work correctly when a thread-local object is dangling,
    212      without any interference from the thread-state dicts
    213 
    214    As an additional optimization, each localdummy holds a borrowed reference
    215    to the corresponding localdict.  This borrowed reference is only used
    216    by the thread-local object which has created the localdummy, which should
    217    guarantee that the localdict still exists when accessed.
    218 */
    219 
    220 typedef struct {
    221     PyObject_HEAD
    222     PyObject *localdict;        /* Borrowed reference! */
    223     PyObject *weakreflist;      /* List of weak references to self */
    224 } localdummyobject;
    225 
    226 static void
    227 localdummy_dealloc(localdummyobject *self)
    228 {
    229     if (self->weakreflist != NULL)
    230         PyObject_ClearWeakRefs((PyObject *) self);
    231     Py_TYPE(self)->tp_free((PyObject*)self);
    232 }
    233 
    234 static PyTypeObject localdummytype = {
    235     PyVarObject_HEAD_INIT(NULL, 0)
    236     /* tp_name           */ "_thread._localdummy",
    237     /* tp_basicsize      */ sizeof(localdummyobject),
    238     /* tp_itemsize       */ 0,
    239     /* tp_dealloc        */ (destructor)localdummy_dealloc,
    240     /* tp_print          */ 0,
    241     /* tp_getattr        */ 0,
    242     /* tp_setattr        */ 0,
    243     /* tp_reserved       */ 0,
    244     /* tp_repr           */ 0,
    245     /* tp_as_number      */ 0,
    246     /* tp_as_sequence    */ 0,
    247     /* tp_as_mapping     */ 0,
    248     /* tp_hash           */ 0,
    249     /* tp_call           */ 0,
    250     /* tp_str            */ 0,
    251     /* tp_getattro       */ 0,
    252     /* tp_setattro       */ 0,
    253     /* tp_as_buffer      */ 0,
    254     /* tp_flags          */ Py_TPFLAGS_DEFAULT,
    255     /* tp_doc            */ "Thread-local dummy",
    256     /* tp_traverse       */ 0,
    257     /* tp_clear          */ 0,
    258     /* tp_richcompare    */ 0,
    259     /* tp_weaklistoffset */ offsetof(localdummyobject, weakreflist)
    260 };
    261 
    262 
    263 typedef struct {
    264     PyObject_HEAD
    265     PyObject *key;
    266     PyObject *args;
    267     PyObject *kw;
    268     PyObject *weakreflist;      /* List of weak references to self */
    269     /* A {localdummy weakref -> localdict} dict */
    270     PyObject *dummies;
    271     /* The callback for weakrefs to localdummies */
    272     PyObject *wr_callback;
    273 } localobject;
    274 
    275 /* Forward declaration */
    276 static PyObject *_ldict(localobject *self);
    277 static PyObject *_localdummy_destroyed(PyObject *meth_self, PyObject *dummyweakref);
    278 
    279 /* Create and register the dummy for the current thread.
    280    Returns a borrowed reference of the corresponding local dict */
    281 static PyObject *
    282 _local_create_dummy(localobject *self)
    283 {
    284     PyObject *tdict, *ldict = NULL, *wr = NULL;
    285     localdummyobject *dummy = NULL;
    286     int r;
    287 
    288     tdict = PyThreadState_GetDict();
    289     if (tdict == NULL) {
    290         PyErr_SetString(PyExc_SystemError,
    291                         "Couldn't get thread-state dictionary");
    292         goto err;
    293     }
    294 
    295     ldict = PyDict_New();
    296     if (ldict == NULL)
    297         goto err;
    298     dummy = (localdummyobject *) localdummytype.tp_alloc(&localdummytype, 0);
    299     if (dummy == NULL)
    300         goto err;
    301     dummy->localdict = ldict;
    302     wr = PyWeakref_NewRef((PyObject *) dummy, self->wr_callback);
    303     if (wr == NULL)
    304         goto err;
    305 
    306     /* As a side-effect, this will cache the weakref's hash before the
    307        dummy gets deleted */
    308     r = PyDict_SetItem(self->dummies, wr, ldict);
    309     if (r < 0)
    310         goto err;
    311     Py_CLEAR(wr);
    312     r = PyDict_SetItem(tdict, self->key, (PyObject *) dummy);
    313     if (r < 0)
    314         goto err;
    315     Py_CLEAR(dummy);
    316 
    317     Py_DECREF(ldict);
    318     return ldict;
    319 
    320 err:
    321     Py_XDECREF(ldict);
    322     Py_XDECREF(wr);
    323     Py_XDECREF(dummy);
    324     return NULL;
    325 }
    326 
    327 static PyObject *
    328 local_new(PyTypeObject *type, PyObject *args, PyObject *kw)
    329 {
    330     localobject *self;
    331     PyObject *wr;
    332     static PyMethodDef wr_callback_def = {
    333         "_localdummy_destroyed", (PyCFunction) _localdummy_destroyed, METH_O
    334     };
    335 
    336     if (type->tp_init == PyBaseObject_Type.tp_init
    337         && ((args && PyObject_IsTrue(args))
    338         || (kw && PyObject_IsTrue(kw)))) {
    339         PyErr_SetString(PyExc_TypeError,
    340                   "Initialization arguments are not supported");
    341         return NULL;
    342     }
    343 
    344     self = (localobject *)type->tp_alloc(type, 0);
    345     if (self == NULL)
    346         return NULL;
    347 
    348     Py_XINCREF(args);
    349     self->args = args;
    350     Py_XINCREF(kw);
    351     self->kw = kw;
    352     self->key = PyString_FromFormat("thread.local.%p", self);
    353     if (self->key == NULL)
    354         goto err;
    355 
    356     self->dummies = PyDict_New();
    357     if (self->dummies == NULL)
    358         goto err;
    359 
    360     /* We use a weak reference to self in the callback closure
    361        in order to avoid spurious reference cycles */
    362     wr = PyWeakref_NewRef((PyObject *) self, NULL);
    363     if (wr == NULL)
    364         goto err;
    365     self->wr_callback = PyCFunction_New(&wr_callback_def, wr);
    366     Py_DECREF(wr);
    367     if (self->wr_callback == NULL)
    368         goto err;
    369 
    370     if (_local_create_dummy(self) == NULL)
    371         goto err;
    372 
    373     return (PyObject *)self;
    374 
    375   err:
    376     Py_DECREF(self);
    377     return NULL;
    378 }
    379 
    380 static int
    381 local_traverse(localobject *self, visitproc visit, void *arg)
    382 {
    383     Py_VISIT(self->args);
    384     Py_VISIT(self->kw);
    385     Py_VISIT(self->dummies);
    386     return 0;
    387 }
    388 
    389 static int
    390 local_clear(localobject *self)
    391 {
    392     PyThreadState *tstate;
    393     Py_CLEAR(self->args);
    394     Py_CLEAR(self->kw);
    395     Py_CLEAR(self->dummies);
    396     Py_CLEAR(self->wr_callback);
    397     /* Remove all strong references to dummies from the thread states */
    398     if (self->key
    399         && (tstate = PyThreadState_Get())
    400         && tstate->interp) {
    401         for(tstate = PyInterpreterState_ThreadHead(tstate->interp);
    402             tstate;
    403             tstate = PyThreadState_Next(tstate))
    404             if (tstate->dict &&
    405                 PyDict_GetItem(tstate->dict, self->key))
    406                 PyDict_DelItem(tstate->dict, self->key);
    407     }
    408     return 0;
    409 }
    410 
    411 static void
    412 local_dealloc(localobject *self)
    413 {
    414     /* Weakrefs must be invalidated right now, otherwise they can be used
    415        from code called below, which is very dangerous since Py_REFCNT(self) == 0 */
    416     if (self->weakreflist != NULL)
    417         PyObject_ClearWeakRefs((PyObject *) self);
    418 
    419     PyObject_GC_UnTrack(self);
    420 
    421     local_clear(self);
    422     Py_XDECREF(self->key);
    423     Py_TYPE(self)->tp_free((PyObject*)self);
    424 }
    425 
    426 /* Returns a borrowed reference to the local dict, creating it if necessary */
    427 static PyObject *
    428 _ldict(localobject *self)
    429 {
    430     PyObject *tdict, *ldict, *dummy;
    431 
    432     tdict = PyThreadState_GetDict();
    433     if (tdict == NULL) {
    434         PyErr_SetString(PyExc_SystemError,
    435                         "Couldn't get thread-state dictionary");
    436         return NULL;
    437     }
    438 
    439     dummy = PyDict_GetItem(tdict, self->key);
    440     if (dummy == NULL) {
    441         ldict = _local_create_dummy(self);
    442         if (ldict == NULL)
    443             return NULL;
    444 
    445         if (Py_TYPE(self)->tp_init != PyBaseObject_Type.tp_init &&
    446             Py_TYPE(self)->tp_init((PyObject*)self,
    447                                    self->args, self->kw) < 0) {
    448             /* we need to get rid of ldict from thread so
    449                we create a new one the next time we do an attr
    450                access */
    451             PyDict_DelItem(tdict, self->key);
    452             return NULL;
    453         }
    454     }
    455     else {
    456         assert(Py_TYPE(dummy) == &localdummytype);
    457         ldict = ((localdummyobject *) dummy)->localdict;
    458     }
    459 
    460     return ldict;
    461 }
    462 
    463 static int
    464 local_setattro(localobject *self, PyObject *name, PyObject *v)
    465 {
    466     PyObject *ldict;
    467     int r;
    468 
    469     ldict = _ldict(self);
    470     if (ldict == NULL)
    471         return -1;
    472 
    473     r = PyObject_RichCompareBool(name, str_dict, Py_EQ);
    474     if (r == 1) {
    475         PyErr_Format(PyExc_AttributeError,
    476                      "'%.50s' object attribute '__dict__' is read-only",
    477                      Py_TYPE(self)->tp_name);
    478         return -1;
    479     }
    480     if (r == -1)
    481         return -1;
    482 
    483     return _PyObject_GenericSetAttrWithDict((PyObject *)self, name, v, ldict);
    484 }
    485 
    486 static PyObject *local_getattro(localobject *, PyObject *);
    487 
    488 static PyTypeObject localtype = {
    489     PyVarObject_HEAD_INIT(NULL, 0)
    490     /* tp_name           */ "thread._local",
    491     /* tp_basicsize      */ sizeof(localobject),
    492     /* tp_itemsize       */ 0,
    493     /* tp_dealloc        */ (destructor)local_dealloc,
    494     /* tp_print          */ 0,
    495     /* tp_getattr        */ 0,
    496     /* tp_setattr        */ 0,
    497     /* tp_compare        */ 0,
    498     /* tp_repr           */ 0,
    499     /* tp_as_number      */ 0,
    500     /* tp_as_sequence    */ 0,
    501     /* tp_as_mapping     */ 0,
    502     /* tp_hash           */ 0,
    503     /* tp_call           */ 0,
    504     /* tp_str            */ 0,
    505     /* tp_getattro       */ (getattrofunc)local_getattro,
    506     /* tp_setattro       */ (setattrofunc)local_setattro,
    507     /* tp_as_buffer      */ 0,
    508     /* tp_flags          */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE
    509                                                | Py_TPFLAGS_HAVE_GC,
    510     /* tp_doc            */ "Thread-local data",
    511     /* tp_traverse       */ (traverseproc)local_traverse,
    512     /* tp_clear          */ (inquiry)local_clear,
    513     /* tp_richcompare    */ 0,
    514     /* tp_weaklistoffset */ offsetof(localobject, weakreflist),
    515     /* tp_iter           */ 0,
    516     /* tp_iternext       */ 0,
    517     /* tp_methods        */ 0,
    518     /* tp_members        */ 0,
    519     /* tp_getset         */ 0,
    520     /* tp_base           */ 0,
    521     /* tp_dict           */ 0, /* internal use */
    522     /* tp_descr_get      */ 0,
    523     /* tp_descr_set      */ 0,
    524     /* tp_dictoffset     */ 0,
    525     /* tp_init           */ 0,
    526     /* tp_alloc          */ 0,
    527     /* tp_new            */ local_new,
    528     /* tp_free           */ 0, /* Low-level free-mem routine */
    529     /* tp_is_gc          */ 0, /* For PyObject_IS_GC */
    530 };
    531 
    532 static PyObject *
    533 local_getattro(localobject *self, PyObject *name)
    534 {
    535     PyObject *ldict, *value;
    536     int r;
    537 
    538     ldict = _ldict(self);
    539     if (ldict == NULL)
    540         return NULL;
    541 
    542     r = PyObject_RichCompareBool(name, str_dict, Py_EQ);
    543     if (r == 1) {
    544         Py_INCREF(ldict);
    545         return ldict;
    546     }
    547     if (r == -1)
    548         return NULL;
    549 
    550     if (Py_TYPE(self) != &localtype)
    551         /* use generic lookup for subtypes */
    552         return _PyObject_GenericGetAttrWithDict((PyObject *)self, name, ldict);
    553 
    554     /* Optimization: just look in dict ourselves */
    555     value = PyDict_GetItem(ldict, name);
    556     if (value == NULL)
    557         /* Fall back on generic to get __class__ and __dict__ */
    558         return _PyObject_GenericGetAttrWithDict((PyObject *)self, name, ldict);
    559 
    560     Py_INCREF(value);
    561     return value;
    562 }
    563 
    564 /* Called when a dummy is destroyed. */
    565 static PyObject *
    566 _localdummy_destroyed(PyObject *localweakref, PyObject *dummyweakref)
    567 {
    568     PyObject *obj;
    569     localobject *self;
    570     assert(PyWeakref_CheckRef(localweakref));
    571     obj = PyWeakref_GET_OBJECT(localweakref);
    572     if (obj == Py_None)
    573         Py_RETURN_NONE;
    574     Py_INCREF(obj);
    575     assert(PyObject_TypeCheck(obj, &localtype));
    576     /* If the thread-local object is still alive and not being cleared,
    577        remove the corresponding local dict */
    578     self = (localobject *) obj;
    579     if (self->dummies != NULL) {
    580         PyObject *ldict;
    581         ldict = PyDict_GetItem(self->dummies, dummyweakref);
    582         if (ldict != NULL) {
    583             PyDict_DelItem(self->dummies, dummyweakref);
    584         }
    585         if (PyErr_Occurred())
    586             PyErr_WriteUnraisable(obj);
    587     }
    588     Py_DECREF(obj);
    589     Py_RETURN_NONE;
    590 }
    591 
    592 /* Module functions */
    593 
    594 struct bootstate {
    595     PyInterpreterState *interp;
    596     PyObject *func;
    597     PyObject *args;
    598     PyObject *keyw;
    599     PyThreadState *tstate;
    600 };
    601 
    602 static void
    603 t_bootstrap(void *boot_raw)
    604 {
    605     struct bootstate *boot = (struct bootstate *) boot_raw;
    606     PyThreadState *tstate;
    607     PyObject *res;
    608 
    609     tstate = boot->tstate;
    610     tstate->thread_id = PyThread_get_thread_ident();
    611     _PyThreadState_Init(tstate);
    612     PyEval_AcquireThread(tstate);
    613     nb_threads++;
    614     res = PyEval_CallObjectWithKeywords(
    615         boot->func, boot->args, boot->keyw);
    616     if (res == NULL) {
    617         if (PyErr_ExceptionMatches(PyExc_SystemExit))
    618             PyErr_Clear();
    619         else {
    620             PyObject *file;
    621             PySys_WriteStderr(
    622                 "Unhandled exception in thread started by ");
    623             file = PySys_GetObject("stderr");
    624             if (file)
    625                 PyFile_WriteObject(boot->func, file, 0);
    626             else
    627                 PyObject_Print(boot->func, stderr, 0);
    628             PySys_WriteStderr("\n");
    629             PyErr_PrintEx(0);
    630         }
    631     }
    632     else
    633         Py_DECREF(res);
    634     Py_DECREF(boot->func);
    635     Py_DECREF(boot->args);
    636     Py_XDECREF(boot->keyw);
    637     PyMem_DEL(boot_raw);
    638     nb_threads--;
    639     PyThreadState_Clear(tstate);
    640     PyThreadState_DeleteCurrent();
    641     PyThread_exit_thread();
    642 }
    643 
    644 static PyObject *
    645 thread_PyThread_start_new_thread(PyObject *self, PyObject *fargs)
    646 {
    647     PyObject *func, *args, *keyw = NULL;
    648     struct bootstate *boot;
    649     long ident;
    650 
    651     if (!PyArg_UnpackTuple(fargs, "start_new_thread", 2, 3,
    652                            &func, &args, &keyw))
    653         return NULL;
    654     if (!PyCallable_Check(func)) {
    655         PyErr_SetString(PyExc_TypeError,
    656                         "first arg must be callable");
    657         return NULL;
    658     }
    659     if (!PyTuple_Check(args)) {
    660         PyErr_SetString(PyExc_TypeError,
    661                         "2nd arg must be a tuple");
    662         return NULL;
    663     }
    664     if (keyw != NULL && !PyDict_Check(keyw)) {
    665         PyErr_SetString(PyExc_TypeError,
    666                         "optional 3rd arg must be a dictionary");
    667         return NULL;
    668     }
    669     boot = PyMem_NEW(struct bootstate, 1);
    670     if (boot == NULL)
    671         return PyErr_NoMemory();
    672     boot->interp = PyThreadState_GET()->interp;
    673     boot->func = func;
    674     boot->args = args;
    675     boot->keyw = keyw;
    676     boot->tstate = _PyThreadState_Prealloc(boot->interp);
    677     if (boot->tstate == NULL) {
    678         PyMem_DEL(boot);
    679         return PyErr_NoMemory();
    680     }
    681     Py_INCREF(func);
    682     Py_INCREF(args);
    683     Py_XINCREF(keyw);
    684     PyEval_InitThreads(); /* Start the interpreter's thread-awareness */
    685     ident = PyThread_start_new_thread(t_bootstrap, (void*) boot);
    686     if (ident == -1) {
    687         PyErr_SetString(ThreadError, "can't start new thread");
    688         Py_DECREF(func);
    689         Py_DECREF(args);
    690         Py_XDECREF(keyw);
    691         PyThreadState_Clear(boot->tstate);
    692         PyMem_DEL(boot);
    693         return NULL;
    694     }
    695     return PyInt_FromLong(ident);
    696 }
    697 
    698 PyDoc_STRVAR(start_new_doc,
    699 "start_new_thread(function, args[, kwargs])\n\
    700 (start_new() is an obsolete synonym)\n\
    701 \n\
    702 Start a new thread and return its identifier.  The thread will call the\n\
    703 function with positional arguments from the tuple args and keyword arguments\n\
    704 taken from the optional dictionary kwargs.  The thread exits when the\n\
    705 function returns; the return value is ignored.  The thread will also exit\n\
    706 when the function raises an unhandled exception; a stack trace will be\n\
    707 printed unless the exception is SystemExit.\n");
    708 
    709 static PyObject *
    710 thread_PyThread_exit_thread(PyObject *self)
    711 {
    712     PyErr_SetNone(PyExc_SystemExit);
    713     return NULL;
    714 }
    715 
    716 PyDoc_STRVAR(exit_doc,
    717 "exit()\n\
    718 (PyThread_exit_thread() is an obsolete synonym)\n\
    719 \n\
    720 This is synonymous to ``raise SystemExit''.  It will cause the current\n\
    721 thread to exit silently unless the exception is caught.");
    722 
    723 static PyObject *
    724 thread_PyThread_interrupt_main(PyObject * self)
    725 {
    726     PyErr_SetInterrupt();
    727     Py_INCREF(Py_None);
    728     return Py_None;
    729 }
    730 
    731 PyDoc_STRVAR(interrupt_doc,
    732 "interrupt_main()\n\
    733 \n\
    734 Raise a KeyboardInterrupt in the main thread.\n\
    735 A subthread can use this function to interrupt the main thread."
    736 );
    737 
    738 static lockobject *newlockobject(void);
    739 
    740 static PyObject *
    741 thread_PyThread_allocate_lock(PyObject *self)
    742 {
    743     return (PyObject *) newlockobject();
    744 }
    745 
    746 PyDoc_STRVAR(allocate_doc,
    747 "allocate_lock() -> lock object\n\
    748 (allocate() is an obsolete synonym)\n\
    749 \n\
    750 Create a new lock object.  See help(LockType) for information about locks.");
    751 
    752 static PyObject *
    753 thread_get_ident(PyObject *self)
    754 {
    755     long ident;
    756     ident = PyThread_get_thread_ident();
    757     if (ident == -1) {
    758         PyErr_SetString(ThreadError, "no current thread ident");
    759         return NULL;
    760     }
    761     return PyInt_FromLong(ident);
    762 }
    763 
    764 PyDoc_STRVAR(get_ident_doc,
    765 "get_ident() -> integer\n\
    766 \n\
    767 Return a non-zero integer that uniquely identifies the current thread\n\
    768 amongst other threads that exist simultaneously.\n\
    769 This may be used to identify per-thread resources.\n\
    770 Even though on some platforms threads identities may appear to be\n\
    771 allocated consecutive numbers starting at 1, this behavior should not\n\
    772 be relied upon, and the number should be seen purely as a magic cookie.\n\
    773 A thread's identity may be reused for another thread after it exits.");
    774 
    775 static PyObject *
    776 thread__count(PyObject *self)
    777 {
    778     return PyInt_FromLong(nb_threads);
    779 }
    780 
    781 PyDoc_STRVAR(_count_doc,
    782 "_count() -> integer\n\
    783 \n\
    784 \
    785 Return the number of currently running Python threads, excluding \n\
    786 the main thread. The returned number comprises all threads created\n\
    787 through `start_new_thread()` as well as `threading.Thread`, and not\n\
    788 yet finished.\n\
    789 \n\
    790 This function is meant for internal and specialized purposes only.\n\
    791 In most applications `threading.enumerate()` should be used instead.");
    792 
    793 static PyObject *
    794 thread_stack_size(PyObject *self, PyObject *args)
    795 {
    796     size_t old_size;
    797     Py_ssize_t new_size = 0;
    798     int rc;
    799 
    800     if (!PyArg_ParseTuple(args, "|n:stack_size", &new_size))
    801         return NULL;
    802 
    803     if (new_size < 0) {
    804         PyErr_SetString(PyExc_ValueError,
    805                         "size must be 0 or a positive value");
    806         return NULL;
    807     }
    808 
    809     old_size = PyThread_get_stacksize();
    810 
    811     rc = PyThread_set_stacksize((size_t) new_size);
    812     if (rc == -1) {
    813         PyErr_Format(PyExc_ValueError,
    814                      "size not valid: %zd bytes",
    815                      new_size);
    816         return NULL;
    817     }
    818     if (rc == -2) {
    819         PyErr_SetString(ThreadError,
    820                         "setting stack size not supported");
    821         return NULL;
    822     }
    823 
    824     return PyInt_FromSsize_t((Py_ssize_t) old_size);
    825 }
    826 
    827 PyDoc_STRVAR(stack_size_doc,
    828 "stack_size([size]) -> size\n\
    829 \n\
    830 Return the thread stack size used when creating new threads.  The\n\
    831 optional size argument specifies the stack size (in bytes) to be used\n\
    832 for subsequently created threads, and must be 0 (use platform or\n\
    833 configured default) or a positive integer value of at least 32,768 (32k).\n\
    834 If changing the thread stack size is unsupported, a ThreadError\n\
    835 exception is raised.  If the specified size is invalid, a ValueError\n\
    836 exception is raised, and the stack size is unmodified.  32k bytes\n\
    837  currently the minimum supported stack size value to guarantee\n\
    838 sufficient stack space for the interpreter itself.\n\
    839 \n\
    840 Note that some platforms may have particular restrictions on values for\n\
    841 the stack size, such as requiring a minimum stack size larger than 32kB or\n\
    842 requiring allocation in multiples of the system memory page size\n\
    843 - platform documentation should be referred to for more information\n\
    844 (4kB pages are common; using multiples of 4096 for the stack size is\n\
    845 the suggested approach in the absence of more specific information).");
    846 
    847 static PyMethodDef thread_methods[] = {
    848     {"start_new_thread",        (PyCFunction)thread_PyThread_start_new_thread,
    849                             METH_VARARGS,
    850                             start_new_doc},
    851     {"start_new",               (PyCFunction)thread_PyThread_start_new_thread,
    852                             METH_VARARGS,
    853                             start_new_doc},
    854     {"allocate_lock",           (PyCFunction)thread_PyThread_allocate_lock,
    855      METH_NOARGS, allocate_doc},
    856     {"allocate",                (PyCFunction)thread_PyThread_allocate_lock,
    857      METH_NOARGS, allocate_doc},
    858     {"exit_thread",             (PyCFunction)thread_PyThread_exit_thread,
    859      METH_NOARGS, exit_doc},
    860     {"exit",                    (PyCFunction)thread_PyThread_exit_thread,
    861      METH_NOARGS, exit_doc},
    862     {"interrupt_main",          (PyCFunction)thread_PyThread_interrupt_main,
    863      METH_NOARGS, interrupt_doc},
    864     {"get_ident",               (PyCFunction)thread_get_ident,
    865      METH_NOARGS, get_ident_doc},
    866     {"_count",                  (PyCFunction)thread__count,
    867      METH_NOARGS, _count_doc},
    868     {"stack_size",              (PyCFunction)thread_stack_size,
    869                             METH_VARARGS,
    870                             stack_size_doc},
    871     {NULL,                      NULL}           /* sentinel */
    872 };
    873 
    874 
    875 /* Initialization function */
    876 
    877 PyDoc_STRVAR(thread_doc,
    878 "This module provides primitive operations to write multi-threaded programs.\n\
    879 The 'threading' module provides a more convenient interface.");
    880 
    881 PyDoc_STRVAR(lock_doc,
    882 "A lock object is a synchronization primitive.  To create a lock,\n\
    883 call the PyThread_allocate_lock() function.  Methods are:\n\
    884 \n\
    885 acquire() -- lock the lock, possibly blocking until it can be obtained\n\
    886 release() -- unlock of the lock\n\
    887 locked() -- test whether the lock is currently locked\n\
    888 \n\
    889 A lock is not owned by the thread that locked it; another thread may\n\
    890 unlock it.  A thread attempting to lock a lock that it has already locked\n\
    891 will block until another thread unlocks it.  Deadlocks may ensue.");
    892 
    893 PyMODINIT_FUNC
    894 initthread(void)
    895 {
    896     PyObject *m, *d;
    897 
    898     /* Initialize types: */
    899     if (PyType_Ready(&localdummytype) < 0)
    900         return;
    901     if (PyType_Ready(&localtype) < 0)
    902         return;
    903 
    904     /* Create the module and add the functions */
    905     m = Py_InitModule3("thread", thread_methods, thread_doc);
    906     if (m == NULL)
    907         return;
    908 
    909     /* Add a symbolic constant */
    910     d = PyModule_GetDict(m);
    911     ThreadError = PyErr_NewException("thread.error", NULL, NULL);
    912     PyDict_SetItemString(d, "error", ThreadError);
    913     Locktype.tp_doc = lock_doc;
    914     if (PyType_Ready(&Locktype) < 0)
    915         return;
    916     Py_INCREF(&Locktype);
    917     PyDict_SetItemString(d, "LockType", (PyObject *)&Locktype);
    918 
    919     Py_INCREF(&localtype);
    920     if (PyModule_AddObject(m, "_local", (PyObject *)&localtype) < 0)
    921         return;
    922 
    923     nb_threads = 0;
    924 
    925     str_dict = PyString_InternFromString("__dict__");
    926     if (str_dict == NULL)
    927         return;
    928 
    929     /* Initialize the C thread library */
    930     PyThread_init_thread();
    931 }
    932