Showing the changes to sys/src/cmd/python/Modules/threadmodule.c (1 of 3706 files). Show all
Unified · Split
| @@ -0,0 +1,720 @@ | |||
| 1 | |||
| 2 | /* Thread module */ | ||
| 3 | /* Interface to Sjoerd's portable C thread library */ | ||
| 4 | |||
| 5 | #include "Python.h" | ||
| 6 | |||
| 7 | #ifndef WITH_THREAD | ||
| 8 | #error "Error! The rest of Python is not compiled with thread support." | ||
| 9 | #error "Rerun configure, adding a --with-threads option." | ||
| 10 | #error "Then run `make clean' followed by `make'." | ||
| 11 | #endif | ||
| 12 | |||
| 13 | #include "pythread.h" | ||
| 14 | |||
| 15 | static PyObject *ThreadError; | ||
| 16 | |||
| 17 | |||
| 18 | /* Lock objects */ | ||
| 19 | |||
| 20 | typedef struct { | ||
| 21 | PyObject_HEAD | ||
| 22 | PyThread_type_lock lock_lock; | ||
| 23 | } lockobject; | ||
| 24 | |||
| 25 | static void | ||
| 26 | lock_dealloc(lockobject *self) | ||
| 27 | { | ||
| 28 | assert(self->lock_lock); | ||
| 29 | /* Unlock the lock so it's safe to free it */ | ||
| 30 | PyThread_acquire_lock(self->lock_lock, 0); | ||
| 31 | PyThread_release_lock(self->lock_lock); | ||
| 32 | |||
| 33 | PyThread_free_lock(self->lock_lock); | ||
| 34 | PyObject_Del(self); | ||
| 35 | } | ||
| 36 | |||
| 37 | static PyObject * | ||
| 38 | lock_PyThread_acquire_lock(lockobject *self, PyObject *args) | ||
| 39 | { | ||
| 40 | int i = 1; | ||
| 41 | |||
| 42 | if (!PyArg_ParseTuple(args, "|i:acquire", &i)) | ||
| 43 | return NULL; | ||
| 44 | |||
| 45 | Py_BEGIN_ALLOW_THREADS | ||
| 46 | i = PyThread_acquire_lock(self->lock_lock, i); | ||
| 47 | Py_END_ALLOW_THREADS | ||
| 48 | |||
| 49 | return PyBool_FromLong((long)i); | ||
| 50 | } | ||
| 51 | |||
| 52 | PyDoc_STRVAR(acquire_doc, | ||
| 53 | "acquire([wait]) -> None or bool\n\ | ||
| 54 | (acquire_lock() is an obsolete synonym)\n\ | ||
| 55 | \n\ | ||
| 56 | Lock the lock. Without argument, this blocks if the lock is already\n\ | ||
| 57 | locked (even by the same thread), waiting for another thread to release\n\ | ||
| 58 | the lock, and return None once the lock is acquired.\n\ | ||
| 59 | With an argument, this will only block if the argument is true,\n\ | ||
| 60 | and the return value reflects whether the lock is acquired.\n\ | ||
| 61 | The blocking operation is not interruptible."); | ||
| 62 | |||
| 63 | static PyObject * | ||
| 64 | lock_PyThread_release_lock(lockobject *self) | ||
| 65 | { | ||
| 66 | /* Sanity check: the lock must be locked */ | ||
| 67 | if (PyThread_acquire_lock(self->lock_lock, 0)) { | ||
| 68 | PyThread_release_lock(self->lock_lock); | ||
| 69 | PyErr_SetString(ThreadError, "release unlocked lock"); | ||
| 70 | return NULL; | ||
| 71 | } | ||
| 72 | |||
| 73 | PyThread_release_lock(self->lock_lock); | ||
| 74 | Py_INCREF(Py_None); | ||
| 75 | return Py_None; | ||
| 76 | } | ||
| 77 | |||
| 78 | PyDoc_STRVAR(release_doc, | ||
| 79 | "release()\n\ | ||
| 80 | (release_lock() is an obsolete synonym)\n\ | ||
| 81 | \n\ | ||
| 82 | Release the lock, allowing another thread that is blocked waiting for\n\ | ||
| 83 | the lock to acquire the lock. The lock must be in the locked state,\n\ | ||
| 84 | but it needn't be locked by the same thread that unlocks it."); | ||
| 85 | |||
| 86 | static PyObject * | ||
| 87 | lock_locked_lock(lockobject *self) | ||
| 88 | { | ||
| 89 | if (PyThread_acquire_lock(self->lock_lock, 0)) { | ||
| 90 | PyThread_release_lock(self->lock_lock); | ||
| 91 | return PyBool_FromLong(0L); | ||
| 92 | } | ||
| 93 | return PyBool_FromLong(1L); | ||
| 94 | } | ||
| 95 | |||
| 96 | PyDoc_STRVAR(locked_doc, | ||
| 97 | "locked() -> bool\n\ | ||
| 98 | (locked_lock() is an obsolete synonym)\n\ | ||
| 99 | \n\ | ||
| 100 | Return whether the lock is in the locked state."); | ||
| 101 | |||
| 102 | static PyMethodDef lock_methods[] = { | ||
| 103 | {"acquire_lock", (PyCFunction)lock_PyThread_acquire_lock, | ||
| 104 | METH_VARARGS, acquire_doc}, | ||
| 105 | {"acquire", (PyCFunction)lock_PyThread_acquire_lock, | ||
| 106 | METH_VARARGS, acquire_doc}, | ||
| 107 | {"release_lock", (PyCFunction)lock_PyThread_release_lock, | ||
| 108 | METH_NOARGS, release_doc}, | ||
| 109 | {"release", (PyCFunction)lock_PyThread_release_lock, | ||
| 110 | METH_NOARGS, release_doc}, | ||
| 111 | {"locked_lock", (PyCFunction)lock_locked_lock, | ||
| 112 | METH_NOARGS, locked_doc}, | ||
| 113 | {"locked", (PyCFunction)lock_locked_lock, | ||
| 114 | METH_NOARGS, locked_doc}, | ||
| 115 | {"__enter__", (PyCFunction)lock_PyThread_acquire_lock, | ||
| 116 | METH_VARARGS, acquire_doc}, | ||
| 117 | {"__exit__", (PyCFunction)lock_PyThread_release_lock, | ||
| 118 | METH_VARARGS, release_doc}, | ||
| 119 | {NULL, NULL} /* sentinel */ | ||
| 120 | }; | ||
| 121 | |||
| 122 | static PyObject * | ||
| 123 | lock_getattr(lockobject *self, char *name) | ||
| 124 | { | ||
| 125 | return Py_FindMethod(lock_methods, (PyObject *)self, name); | ||
| 126 | } | ||
| 127 | |||
| 128 | static PyTypeObject Locktype = { | ||
| 129 | PyObject_HEAD_INIT(&PyType_Type) | ||
| 130 | 0, /*ob_size*/ | ||
| 131 | "thread.lock", /*tp_name*/ | ||
| 132 | sizeof(lockobject), /*tp_size*/ | ||
| 133 | 0, /*tp_itemsize*/ | ||
| 134 | /* methods */ | ||
| 135 | (destructor)lock_dealloc, /*tp_dealloc*/ | ||
| 136 | 0, /*tp_print*/ | ||
| 137 | (getattrfunc)lock_getattr, /*tp_getattr*/ | ||
| 138 | 0, /*tp_setattr*/ | ||
| 139 | 0, /*tp_compare*/ | ||
| 140 | 0, /*tp_repr*/ | ||
| 141 | }; | ||
| 142 | |||
| 143 | static lockobject * | ||
| 144 | newlockobject(void) | ||
| 145 | { | ||
| 146 | lockobject *self; | ||
| 147 | self = PyObject_New(lockobject, &Locktype); | ||
| 148 | if (self == NULL) | ||
| 149 | return NULL; | ||
| 150 | self->lock_lock = PyThread_allocate_lock(); | ||
| 151 | if (self->lock_lock == NULL) { | ||
| 152 | PyObject_Del(self); | ||
| 153 | self = NULL; | ||
| 154 | PyErr_SetString(ThreadError, "can't allocate lock"); | ||
| 155 | } | ||
| 156 | return self; | ||
| 157 | } | ||
| 158 | |||
| 159 | /* Thread-local objects */ | ||
| 160 | |||
| 161 | #include "structmember.h" | ||
| 162 | |||
| 163 | typedef struct { | ||
| 164 | PyObject_HEAD | ||
| 165 | PyObject *key; | ||
| 166 | PyObject *args; | ||
| 167 | PyObject *kw; | ||
| 168 | PyObject *dict; | ||
| 169 | } localobject; | ||
| 170 | |||
| 171 | static PyObject * | ||
| 172 | local_new(PyTypeObject *type, PyObject *args, PyObject *kw) | ||
| 173 | { | ||
| 174 | localobject *self; | ||
| 175 | PyObject *tdict; | ||
| 176 | |||
| 177 | if (type->tp_init == PyBaseObject_Type.tp_init | ||
| 178 | && ((args && PyObject_IsTrue(args)) | ||
| 179 | || (kw && PyObject_IsTrue(kw)))) { | ||
| 180 | PyErr_SetString(PyExc_TypeError, | ||
| 181 | "Initialization arguments are not supported"); | ||
| 182 | return NULL; | ||
| 183 | } | ||
| 184 | |||
| 185 | self = (localobject *)type->tp_alloc(type, 0); | ||
| 186 | if (self == NULL) | ||
| 187 | return NULL; | ||
| 188 | |||
| 189 | Py_XINCREF(args); | ||
| 190 | self->args = args; | ||
| 191 | Py_XINCREF(kw); | ||
| 192 | self->kw = kw; | ||
| 193 | self->dict = NULL; /* making sure */ | ||
| 194 | self->key = PyString_FromFormat("thread.local.%p", self); | ||
| 195 | if (self->key == NULL) | ||
| 196 | goto err; | ||
| 197 | |||
| 198 | self->dict = PyDict_New(); | ||
| 199 | if (self->dict == NULL) | ||
| 200 | goto err; | ||
| 201 | |||
| 202 | tdict = PyThreadState_GetDict(); | ||
| 203 | if (tdict == NULL) { | ||
| 204 | PyErr_SetString(PyExc_SystemError, | ||
| 205 | "Couldn't get thread-state dictionary"); | ||
| 206 | goto err; | ||
| 207 | } | ||
| 208 | |||
| 209 | if (PyDict_SetItem(tdict, self->key, self->dict) < 0) | ||
| 210 | goto err; | ||
| 211 | |||
| 212 | return (PyObject *)self; | ||
| 213 | |||
| 214 | err: | ||
| 215 | Py_DECREF(self); | ||
| 216 | return NULL; | ||
| 217 | } | ||
| 218 | |||
| 219 | static int | ||
| 220 | local_traverse(localobject *self, visitproc visit, void *arg) | ||
| 221 | { | ||
| 222 | Py_VISIT(self->args); | ||
| 223 | Py_VISIT(self->kw); | ||
| 224 | Py_VISIT(self->dict); | ||
| 225 | return 0; | ||
| 226 | } | ||
| 227 | |||
| 228 | static int | ||
| 229 | local_clear(localobject *self) | ||
| 230 | { | ||
| 231 | Py_CLEAR(self->key); | ||
| 232 | Py_CLEAR(self->args); | ||
| 233 | Py_CLEAR(self->kw); | ||
| 234 | Py_CLEAR(self->dict); | ||
| 235 | return 0; | ||
| 236 | } | ||
| 237 | |||
| 238 | static void | ||
| 239 | local_dealloc(localobject *self) | ||
| 240 | { | ||
| 241 | PyThreadState *tstate; | ||
| 242 | if (self->key | ||
| 243 | && (tstate = PyThreadState_Get()) | ||
| 244 | && tstate->interp) { | ||
| 245 | for(tstate = PyInterpreterState_ThreadHead(tstate->interp); | ||
| 246 | tstate; | ||
| 247 | tstate = PyThreadState_Next(tstate)) | ||
| 248 | if (tstate->dict && | ||
| 249 | PyDict_GetItem(tstate->dict, self->key)) | ||
| 250 | PyDict_DelItem(tstate->dict, self->key); | ||
| 251 | } | ||
| 252 | |||
| 253 | local_clear(self); | ||
| 254 | self->ob_type->tp_free((PyObject*)self); | ||
| 255 | } | ||
| 256 | |||
| 257 | static PyObject * | ||
| 258 | _ldict(localobject *self) | ||
| 259 | { | ||
| 260 | PyObject *tdict, *ldict; | ||
| 261 | |||
| 262 | tdict = PyThreadState_GetDict(); | ||
| 263 | if (tdict == NULL) { | ||
| 264 | PyErr_SetString(PyExc_SystemError, | ||
| 265 | "Couldn't get thread-state dictionary"); | ||
| 266 | return NULL; | ||
| 267 | } | ||
| 268 | |||
| 269 | ldict = PyDict_GetItem(tdict, self->key); | ||
| 270 | if (ldict == NULL) { | ||
| 271 | ldict = PyDict_New(); /* we own ldict */ | ||
| 272 | |||
| 273 | if (ldict == NULL) | ||
| 274 | return NULL; | ||
| 275 | else { | ||
| 276 | int i = PyDict_SetItem(tdict, self->key, ldict); | ||
| 277 | Py_DECREF(ldict); /* now ldict is borrowed */ | ||
| 278 | if (i < 0) | ||
| 279 | return NULL; | ||
| 280 | } | ||
| 281 | |||
| 282 | Py_CLEAR(self->dict); | ||
| 283 | Py_INCREF(ldict); | ||
| 284 | self->dict = ldict; /* still borrowed */ | ||
| 285 | |||
| 286 | if (self->ob_type->tp_init != PyBaseObject_Type.tp_init && | ||
| 287 | self->ob_type->tp_init((PyObject*)self, | ||
| 288 | self->args, self->kw) < 0) { | ||
| 289 | /* we need to get rid of ldict from thread so | ||
| 290 | we create a new one the next time we do an attr | ||
| 291 | acces */ | ||
| 292 | PyDict_DelItem(tdict, self->key); | ||
| 293 | return NULL; | ||
| 294 | } | ||
| 295 | |||
| 296 | } | ||
| 297 | else if (self->dict != ldict) { | ||
| 298 | Py_CLEAR(self->dict); | ||
| 299 | Py_INCREF(ldict); | ||
| 300 | self->dict = ldict; | ||
| 301 | } | ||
| 302 | |||
| 303 | return ldict; | ||
| 304 | } | ||
| 305 | |||
| 306 | static int | ||
| 307 | local_setattro(localobject *self, PyObject *name, PyObject *v) | ||
| 308 | { | ||
| 309 | PyObject *ldict; | ||
| 310 | |||
| 311 | ldict = _ldict(self); | ||
| 312 | if (ldict == NULL) | ||
| 313 | return -1; | ||
| 314 | |||
| 315 | return PyObject_GenericSetAttr((PyObject *)self, name, v); | ||
| 316 | } | ||
| 317 | |||
| 318 | static PyObject * | ||
| 319 | local_getdict(localobject *self, void *closure) | ||
| 320 | { | ||
| 321 | if (self->dict == NULL) { | ||
| 322 | PyErr_SetString(PyExc_AttributeError, "__dict__"); | ||
| 323 | return NULL; | ||
| 324 | } | ||
| 325 | |||
| 326 | Py_INCREF(self->dict); | ||
| 327 | return self->dict; | ||
| 328 | } | ||
| 329 | |||
| 330 | static PyGetSetDef local_getset[] = { | ||
| 331 | {"__dict__", (getter)local_getdict, (setter)NULL, | ||
| 332 | "Local-data dictionary", NULL}, | ||
| 333 | {NULL} /* Sentinel */ | ||
| 334 | }; | ||
| 335 | |||
| 336 | static PyObject *local_getattro(localobject *, PyObject *); | ||
| 337 | |||
| 338 | static PyTypeObject localtype = { | ||
| 339 | PyObject_HEAD_INIT(NULL) | ||
| 340 | /* ob_size */ 0, | ||
| 341 | /* tp_name */ "thread._local", | ||
| 342 | /* tp_basicsize */ sizeof(localobject), | ||
| 343 | /* tp_itemsize */ 0, | ||
| 344 | /* tp_dealloc */ (destructor)local_dealloc, | ||
| 345 | /* tp_print */ 0, | ||
| 346 | /* tp_getattr */ 0, | ||
| 347 | /* tp_setattr */ 0, | ||
| 348 | /* tp_compare */ 0, | ||
| 349 | /* tp_repr */ 0, | ||
| 350 | /* tp_as_number */ 0, | ||
| 351 | /* tp_as_sequence */ 0, | ||
| 352 | /* tp_as_mapping */ 0, | ||
| 353 | /* tp_hash */ 0, | ||
| 354 | /* tp_call */ 0, | ||
| 355 | /* tp_str */ 0, | ||
| 356 | /* tp_getattro */ (getattrofunc)local_getattro, | ||
| 357 | /* tp_setattro */ (setattrofunc)local_setattro, | ||
| 358 | /* tp_as_buffer */ 0, | ||
| 359 | /* tp_flags */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, | ||
| 360 | /* tp_doc */ "Thread-local data", | ||
| 361 | /* tp_traverse */ (traverseproc)local_traverse, | ||
| 362 | /* tp_clear */ (inquiry)local_clear, | ||
| 363 | /* tp_richcompare */ 0, | ||
| 364 | /* tp_weaklistoffset */ 0, | ||
| 365 | /* tp_iter */ 0, | ||
| 366 | /* tp_iternext */ 0, | ||
| 367 | /* tp_methods */ 0, | ||
| 368 | /* tp_members */ 0, | ||
| 369 | /* tp_getset */ local_getset, | ||
| 370 | /* tp_base */ 0, | ||
| 371 | /* tp_dict */ 0, /* internal use */ | ||
| 372 | /* tp_descr_get */ 0, | ||
| 373 | /* tp_descr_set */ 0, | ||
| 374 | /* tp_dictoffset */ offsetof(localobject, dict), | ||
| 375 | /* tp_init */ 0, | ||
| 376 | /* tp_alloc */ 0, | ||
| 377 | /* tp_new */ local_new, | ||
| 378 | /* tp_free */ 0, /* Low-level free-mem routine */ | ||
| 379 | /* tp_is_gc */ 0, /* For PyObject_IS_GC */ | ||
| 380 | }; | ||
| 381 | |||
| 382 | static PyObject * | ||
| 383 | local_getattro(localobject *self, PyObject *name) | ||
| 384 | { | ||
| 385 | PyObject *ldict, *value; | ||
| 386 | |||
| 387 | ldict = _ldict(self); | ||
| 388 | if (ldict == NULL) | ||
| 389 | return NULL; | ||
| 390 | |||
| 391 | if (self->ob_type != &localtype) | ||
| 392 | /* use generic lookup for subtypes */ | ||
| 393 | return PyObject_GenericGetAttr((PyObject *)self, name); | ||
| 394 | |||
| 395 | /* Optimization: just look in dict ourselves */ | ||
| 396 | value = PyDict_GetItem(ldict, name); | ||
| 397 | if (value == NULL) | ||
| 398 | /* Fall back on generic to get __class__ and __dict__ */ | ||
| 399 | return PyObject_GenericGetAttr((PyObject *)self, name); | ||
| 400 | |||
| 401 | Py_INCREF(value); | ||
| 402 | return value; | ||
| 403 | } | ||
| 404 | |||
| 405 | /* Module functions */ | ||
| 406 | |||
| 407 | struct bootstate { | ||
| 408 | PyInterpreterState *interp; | ||
| 409 | PyObject *func; | ||
| 410 | PyObject *args; | ||
| 411 | PyObject *keyw; | ||
| 412 | }; | ||
| 413 | |||
| 414 | static void | ||
| 415 | t_bootstrap(void *boot_raw) | ||
| 416 | { | ||
| 417 | struct bootstate *boot = (struct bootstate *) boot_raw; | ||
| 418 | PyThreadState *tstate; | ||
| 419 | PyObject *res; | ||
| 420 | |||
| 421 | tstate = PyThreadState_New(boot->interp); | ||
| 422 | |||
| 423 | PyEval_AcquireThread(tstate); | ||
| 424 | res = PyEval_CallObjectWithKeywords( | ||
| 425 | boot->func, boot->args, boot->keyw); | ||
| 426 | if (res == NULL) { | ||
| 427 | if (PyErr_ExceptionMatches(PyExc_SystemExit)) | ||
| 428 | PyErr_Clear(); | ||
| 429 | else { | ||
| 430 | PyObject *file; | ||
| 431 | PySys_WriteStderr( | ||
| 432 | "Unhandled exception in thread started by "); | ||
| 433 | file = PySys_GetObject("stderr"); | ||
| 434 | if (file) | ||
| 435 | PyFile_WriteObject(boot->func, file, 0); | ||
| 436 | else | ||
| 437 | PyObject_Print(boot->func, stderr, 0); | ||
| 438 | PySys_WriteStderr("\n"); | ||
| 439 | PyErr_PrintEx(0); | ||
| 440 | } | ||
| 441 | } | ||
| 442 | else | ||
| 443 | Py_DECREF(res); | ||
| 444 | Py_DECREF(boot->func); | ||
| 445 | Py_DECREF(boot->args); | ||
| 446 | Py_XDECREF(boot->keyw); | ||
| 447 | PyMem_DEL(boot_raw); | ||
| 448 | PyThreadState_Clear(tstate); | ||
| 449 | PyThreadState_DeleteCurrent(); | ||
| 450 | PyThread_exit_thread(); | ||
| 451 | } | ||
| 452 | |||
| 453 | static PyObject * | ||
| 454 | thread_PyThread_start_new_thread(PyObject *self, PyObject *fargs) | ||
| 455 | { | ||
| 456 | PyObject *func, *args, *keyw = NULL; | ||
| 457 | struct bootstate *boot; | ||
| 458 | long ident; | ||
| 459 | |||
| 460 | if (!PyArg_UnpackTuple(fargs, "start_new_thread", 2, 3, | ||
| 461 | &func, &args, &keyw)) | ||
| 462 | return NULL; | ||
| 463 | if (!PyCallable_Check(func)) { | ||
| 464 | PyErr_SetString(PyExc_TypeError, | ||
| 465 | "first arg must be callable"); | ||
| 466 | return NULL; | ||
| 467 | } | ||
| 468 | if (!PyTuple_Check(args)) { | ||
| 469 | PyErr_SetString(PyExc_TypeError, | ||
| 470 | "2nd arg must be a tuple"); | ||
| 471 | return NULL; | ||
| 472 | } | ||
| 473 | if (keyw != NULL && !PyDict_Check(keyw)) { | ||
| 474 | PyErr_SetString(PyExc_TypeError, | ||
| 475 | "optional 3rd arg must be a dictionary"); | ||
| 476 | return NULL; | ||
| 477 | } | ||
| 478 | boot = PyMem_NEW(struct bootstate, 1); | ||
| 479 | if (boot == NULL) | ||
| 480 | return PyErr_NoMemory(); | ||
| 481 | boot->interp = PyThreadState_GET()->interp; | ||
| 482 | boot->func = func; | ||
| 483 | boot->args = args; | ||
| 484 | boot->keyw = keyw; | ||
| 485 | Py_INCREF(func); | ||
| 486 | Py_INCREF(args); | ||
| 487 | Py_XINCREF(keyw); | ||
| 488 | PyEval_InitThreads(); /* Start the interpreter's thread-awareness */ | ||
| 489 | ident = PyThread_start_new_thread(t_bootstrap, (void*) boot); | ||
| 490 | if (ident == -1) { | ||
| 491 | PyErr_SetString(ThreadError, "can't start new thread"); | ||
| 492 | Py_DECREF(func); | ||
| 493 | Py_DECREF(args); | ||
| 494 | Py_XDECREF(keyw); | ||
| 495 | PyMem_DEL(boot); | ||
| 496 | return NULL; | ||
| 497 | } | ||
| 498 | return PyInt_FromLong(ident); | ||
| 499 | } | ||
| 500 | |||
| 501 | PyDoc_STRVAR(start_new_doc, | ||
| 502 | "start_new_thread(function, args[, kwargs])\n\ | ||
| 503 | (start_new() is an obsolete synonym)\n\ | ||
| 504 | \n\ | ||
| 505 | Start a new thread and return its identifier. The thread will call the\n\ | ||
| 506 | function with positional arguments from the tuple args and keyword arguments\n\ | ||
| 507 | taken from the optional dictionary kwargs. The thread exits when the\n\ | ||
| 508 | function returns; the return value is ignored. The thread will also exit\n\ | ||
| 509 | when the function raises an unhandled exception; a stack trace will be\n\ | ||
| 510 | printed unless the exception is SystemExit.\n"); | ||
| 511 | |||
| 512 | static PyObject * | ||
| 513 | thread_PyThread_exit_thread(PyObject *self) | ||
| 514 | { | ||
| 515 | PyErr_SetNone(PyExc_SystemExit); | ||
| 516 | return NULL; | ||
| 517 | } | ||
| 518 | |||
| 519 | PyDoc_STRVAR(exit_doc, | ||
| 520 | "exit()\n\ | ||
| 521 | (PyThread_exit_thread() is an obsolete synonym)\n\ | ||
| 522 | \n\ | ||
| 523 | This is synonymous to ``raise SystemExit''. It will cause the current\n\ | ||
| 524 | thread to exit silently unless the exception is caught."); | ||
| 525 | |||
| 526 | static PyObject * | ||
| 527 | thread_PyThread_interrupt_main(PyObject * self) | ||
| 528 | { | ||
| 529 | PyErr_SetInterrupt(); | ||
| 530 | Py_INCREF(Py_None); | ||
| 531 | return Py_None; | ||
| 532 | } | ||
| 533 | |||
| 534 | PyDoc_STRVAR(interrupt_doc, | ||
| 535 | "interrupt_main()\n\ | ||
| 536 | \n\ | ||
| 537 | Raise a KeyboardInterrupt in the main thread.\n\ | ||
| 538 | A subthread can use this function to interrupt the main thread." | ||
| 539 | ); | ||
| 540 | |||
| 541 | #ifndef NO_EXIT_PROG | ||
| 542 | static PyObject * | ||
| 543 | thread_PyThread_exit_prog(PyObject *self, PyObject *args) | ||
| 544 | { | ||
| 545 | int sts; | ||
| 546 | if (!PyArg_ParseTuple(args, "i:exit_prog", &sts)) | ||
| 547 | return NULL; | ||
| 548 | Py_Exit(sts); /* Calls PyThread_exit_prog(sts) or _PyThread_exit_prog(sts) */ | ||
| 549 | for (;;) { } /* Should not be reached */ | ||
| 550 | } | ||
| 551 | #endif | ||
| 552 | |||
| 553 | static lockobject *newlockobject(void); | ||
| 554 | |||
| 555 | static PyObject * | ||
| 556 | thread_PyThread_allocate_lock(PyObject *self) | ||
| 557 | { | ||
| 558 | return (PyObject *) newlockobject(); | ||
| 559 | } | ||
| 560 | |||
| 561 | PyDoc_STRVAR(allocate_doc, | ||
| 562 | "allocate_lock() -> lock object\n\ | ||
| 563 | (allocate() is an obsolete synonym)\n\ | ||
| 564 | \n\ | ||
| 565 | Create a new lock object. See LockType.__doc__ for information about locks."); | ||
| 566 | |||
| 567 | static PyObject * | ||
| 568 | thread_get_ident(PyObject *self) | ||
| 569 | { | ||
| 570 | long ident; | ||
| 571 | ident = PyThread_get_thread_ident(); | ||
| 572 | if (ident == -1) { | ||
| 573 | PyErr_SetString(ThreadError, "no current thread ident"); | ||
| 574 | return NULL; | ||
| 575 | } | ||
| 576 | return PyInt_FromLong(ident); | ||
| 577 | } | ||
| 578 | |||
| 579 | PyDoc_STRVAR(get_ident_doc, | ||
| 580 | "get_ident() -> integer\n\ | ||
| 581 | \n\ | ||
| 582 | Return a non-zero integer that uniquely identifies the current thread\n\ | ||
| 583 | amongst other threads that exist simultaneously.\n\ | ||
| 584 | This may be used to identify per-thread resources.\n\ | ||
| 585 | Even though on some platforms threads identities may appear to be\n\ | ||
| 586 | allocated consecutive numbers starting at 1, this behavior should not\n\ | ||
| 587 | be relied upon, and the number should be seen purely as a magic cookie.\n\ | ||
| 588 | A thread's identity may be reused for another thread after it exits."); | ||
| 589 | |||
| 590 | static PyObject * | ||
| 591 | thread_stack_size(PyObject *self, PyObject *args) | ||
| 592 | { | ||
| 593 | size_t old_size; | ||
| 594 | Py_ssize_t new_size = 0; | ||
| 595 | int rc; | ||
| 596 | |||
| 597 | if (!PyArg_ParseTuple(args, "|n:stack_size", &new_size)) | ||
| 598 | return NULL; | ||
| 599 | |||
| 600 | if (new_size < 0) { | ||
| 601 | PyErr_SetString(PyExc_ValueError, | ||
| 602 | "size must be 0 or a positive value"); | ||
| 603 | return NULL; | ||
| 604 | } | ||
| 605 | |||
| 606 | old_size = PyThread_get_stacksize(); | ||
| 607 | |||
| 608 | rc = PyThread_set_stacksize((size_t) new_size); | ||
| 609 | if (rc == -1) { | ||
| 610 | PyErr_Format(PyExc_ValueError, | ||
| 611 | "size not valid: %zd bytes", | ||
| 612 | new_size); | ||
| 613 | return NULL; | ||
| 614 | } | ||
| 615 | if (rc == -2) { | ||
| 616 | PyErr_SetString(ThreadError, | ||
| 617 | "setting stack size not supported"); | ||
| 618 | return NULL; | ||
| 619 | } | ||
| 620 | |||
| 621 | return PyInt_FromSsize_t((Py_ssize_t) old_size); | ||
| 622 | } | ||
| 623 | |||
| 624 | PyDoc_STRVAR(stack_size_doc, | ||
| 625 | "stack_size([size]) -> size\n\ | ||
| 626 | \n\ | ||
| 627 | Return the thread stack size used when creating new threads. The\n\ | ||
| 628 | optional size argument specifies the stack size (in bytes) to be used\n\ | ||
| 629 | for subsequently created threads, and must be 0 (use platform or\n\ | ||
| 630 | configured default) or a positive integer value of at least 32,768 (32k).\n\ | ||
| 631 | If changing the thread stack size is unsupported, a ThreadError\n\ | ||
| 632 | exception is raised. If the specified size is invalid, a ValueError\n\ | ||
| 633 | exception is raised, and the stack size is unmodified. 32k bytes\n\ | ||
| 634 | currently the minimum supported stack size value to guarantee\n\ | ||
| 635 | sufficient stack space for the interpreter itself.\n\ | ||
| 636 | \n\ | ||
| 637 | Note that some platforms may have particular restrictions on values for\n\ | ||
| 638 | the stack size, such as requiring a minimum stack size larger than 32kB or\n\ | ||
| 639 | requiring allocation in multiples of the system memory page size\n\ | ||
| 640 | - platform documentation should be referred to for more information\n\ | ||
| 641 | (4kB pages are common; using multiples of 4096 for the stack size is\n\ | ||
| 642 | the suggested approach in the absence of more specific information)."); | ||
| 643 | |||
| 644 | static PyMethodDef thread_methods[] = { | ||
| 645 | {"start_new_thread", (PyCFunction)thread_PyThread_start_new_thread, | ||
| 646 | METH_VARARGS, | ||
| 647 | start_new_doc}, | ||
| 648 | {"start_new", (PyCFunction)thread_PyThread_start_new_thread, | ||
| 649 | METH_VARARGS, | ||
| 650 | start_new_doc}, | ||
| 651 | {"allocate_lock", (PyCFunction)thread_PyThread_allocate_lock, | ||
| 652 | METH_NOARGS, allocate_doc}, | ||
| 653 | {"allocate", (PyCFunction)thread_PyThread_allocate_lock, | ||
| 654 | METH_NOARGS, allocate_doc}, | ||
| 655 | {"exit_thread", (PyCFunction)thread_PyThread_exit_thread, | ||
| 656 | METH_NOARGS, exit_doc}, | ||
| 657 | {"exit", (PyCFunction)thread_PyThread_exit_thread, | ||
| 658 | METH_NOARGS, exit_doc}, | ||
| 659 | {"interrupt_main", (PyCFunction)thread_PyThread_interrupt_main, | ||
| 660 | METH_NOARGS, interrupt_doc}, | ||
| 661 | {"get_ident", (PyCFunction)thread_get_ident, | ||
| 662 | METH_NOARGS, get_ident_doc}, | ||
| 663 | {"stack_size", (PyCFunction)thread_stack_size, | ||
| 664 | METH_VARARGS, | ||
| 665 | stack_size_doc}, | ||
| 666 | #ifndef NO_EXIT_PROG | ||
| 667 | {"exit_prog", (PyCFunction)thread_PyThread_exit_prog, | ||
| 668 | METH_VARARGS}, | ||
| 669 | #endif | ||
| 670 | {NULL, NULL} /* sentinel */ | ||
| 671 | }; | ||
| 672 | |||
| 673 | |||
| 674 | /* Initialization function */ | ||
| 675 | |||
| 676 | PyDoc_STRVAR(thread_doc, | ||
| 677 | "This module provides primitive operations to write multi-threaded programs.\n\ | ||
| 678 | The 'threading' module provides a more convenient interface."); | ||
| 679 | |||
| 680 | PyDoc_STRVAR(lock_doc, | ||
| 681 | "A lock object is a synchronization primitive. To create a lock,\n\ | ||
| 682 | call the PyThread_allocate_lock() function. Methods are:\n\ | ||
| 683 | \n\ | ||
| 684 | acquire() -- lock the lock, possibly blocking until it can be obtained\n\ | ||
| 685 | release() -- unlock of the lock\n\ | ||
| 686 | locked() -- test whether the lock is currently locked\n\ | ||
| 687 | \n\ | ||
| 688 | A lock is not owned by the thread that locked it; another thread may\n\ | ||
| 689 | unlock it. A thread attempting to lock a lock that it has already locked\n\ | ||
| 690 | will block until another thread unlocks it. Deadlocks may ensue."); | ||
| 691 | |||
| 692 | PyMODINIT_FUNC | ||
| 693 | initthread(void) | ||
| 694 | { | ||
| 695 | PyObject *m, *d; | ||
| 696 | |||
| 697 | /* Initialize types: */ | ||
| 698 | if (PyType_Ready(&localtype) < 0) | ||
| 699 | return; | ||
| 700 | |||
| 701 | /* Create the module and add the functions */ | ||
| 702 | m = Py_InitModule3("thread", thread_methods, thread_doc); | ||
| 703 | if (m == NULL) | ||
| 704 | return; | ||
| 705 | |||
| 706 | /* Add a symbolic constant */ | ||
| 707 | d = PyModule_GetDict(m); | ||
| 708 | ThreadError = PyErr_NewException("thread.error", NULL, NULL); | ||
| 709 | PyDict_SetItemString(d, "error", ThreadError); | ||
| 710 | Locktype.tp_doc = lock_doc; | ||
| 711 | Py_INCREF(&Locktype); | ||
| 712 | PyDict_SetItemString(d, "LockType", (PyObject *)&Locktype); | ||
| 713 | |||
| 714 | Py_INCREF(&localtype); | ||
| 715 | if (PyModule_AddObject(m, "_local", (PyObject *)&localtype) < 0) | ||
| 716 | return; | ||
| 717 | |||
| 718 | /* Initialize the C thread library */ | ||
| 719 | PyThread_init_thread(); | ||
| 720 | } | ||