/Modules/_multiprocessing/semaphore.c

http://unladen-swallow.googlecode.com/ · C · 625 lines · 488 code · 86 blank · 51 comment · 122 complexity · 0353ed0a1ed6e0e4478faf8bc497c7db MD5 · raw file

  1. /*
  2. * A type which wraps a semaphore
  3. *
  4. * semaphore.c
  5. *
  6. * Copyright (c) 2006-2008, R Oudkerk --- see COPYING.txt
  7. */
  8. #include "multiprocessing.h"
  9. enum { RECURSIVE_MUTEX, SEMAPHORE };
  10. typedef struct {
  11. PyObject_HEAD
  12. SEM_HANDLE handle;
  13. long last_tid;
  14. int count;
  15. int maxvalue;
  16. int kind;
  17. } SemLockObject;
  18. #define ISMINE(o) (o->count > 0 && PyThread_get_thread_ident() == o->last_tid)
  19. #ifdef MS_WINDOWS
  20. /*
  21. * Windows definitions
  22. */
  23. #define SEM_FAILED NULL
  24. #define SEM_CLEAR_ERROR() SetLastError(0)
  25. #define SEM_GET_LAST_ERROR() GetLastError()
  26. #define SEM_CREATE(name, val, max) CreateSemaphore(NULL, val, max, NULL)
  27. #define SEM_CLOSE(sem) (CloseHandle(sem) ? 0 : -1)
  28. #define SEM_GETVALUE(sem, pval) _GetSemaphoreValue(sem, pval)
  29. #define SEM_UNLINK(name) 0
  30. static int
  31. _GetSemaphoreValue(HANDLE handle, long *value)
  32. {
  33. long previous;
  34. switch (WaitForSingleObject(handle, 0)) {
  35. case WAIT_OBJECT_0:
  36. if (!ReleaseSemaphore(handle, 1, &previous))
  37. return MP_STANDARD_ERROR;
  38. *value = previous + 1;
  39. return 0;
  40. case WAIT_TIMEOUT:
  41. *value = 0;
  42. return 0;
  43. default:
  44. return MP_STANDARD_ERROR;
  45. }
  46. }
  47. static PyObject *
  48. semlock_acquire(SemLockObject *self, PyObject *args, PyObject *kwds)
  49. {
  50. int blocking = 1;
  51. double timeout;
  52. PyObject *timeout_obj = Py_None;
  53. DWORD res, full_msecs, msecs, start, ticks;
  54. static char *kwlist[] = {"block", "timeout", NULL};
  55. if (!PyArg_ParseTupleAndKeywords(args, kwds, "|iO", kwlist,
  56. &blocking, &timeout_obj))
  57. return NULL;
  58. /* calculate timeout */
  59. if (!blocking) {
  60. full_msecs = 0;
  61. } else if (timeout_obj == Py_None) {
  62. full_msecs = INFINITE;
  63. } else {
  64. timeout = PyFloat_AsDouble(timeout_obj);
  65. if (PyErr_Occurred())
  66. return NULL;
  67. timeout *= 1000.0; /* convert to millisecs */
  68. if (timeout < 0.0) {
  69. timeout = 0.0;
  70. } else if (timeout >= 0.5 * INFINITE) { /* 25 days */
  71. PyErr_SetString(PyExc_OverflowError,
  72. "timeout is too large");
  73. return NULL;
  74. }
  75. full_msecs = (DWORD)(timeout + 0.5);
  76. }
  77. /* check whether we already own the lock */
  78. if (self->kind == RECURSIVE_MUTEX && ISMINE(self)) {
  79. ++self->count;
  80. Py_RETURN_TRUE;
  81. }
  82. /* check whether we can acquire without blocking */
  83. if (WaitForSingleObject(self->handle, 0) == WAIT_OBJECT_0) {
  84. self->last_tid = GetCurrentThreadId();
  85. ++self->count;
  86. Py_RETURN_TRUE;
  87. }
  88. msecs = full_msecs;
  89. start = GetTickCount();
  90. for ( ; ; ) {
  91. HANDLE handles[2] = {self->handle, sigint_event};
  92. /* do the wait */
  93. Py_BEGIN_ALLOW_THREADS
  94. ResetEvent(sigint_event);
  95. res = WaitForMultipleObjects(2, handles, FALSE, msecs);
  96. Py_END_ALLOW_THREADS
  97. /* handle result */
  98. if (res != WAIT_OBJECT_0 + 1)
  99. break;
  100. /* got SIGINT so give signal handler a chance to run */
  101. Sleep(1);
  102. /* if this is main thread let KeyboardInterrupt be raised */
  103. if (PyErr_CheckSignals())
  104. return NULL;
  105. /* recalculate timeout */
  106. if (msecs != INFINITE) {
  107. ticks = GetTickCount();
  108. if ((DWORD)(ticks - start) >= full_msecs)
  109. Py_RETURN_FALSE;
  110. msecs = full_msecs - (ticks - start);
  111. }
  112. }
  113. /* handle result */
  114. switch (res) {
  115. case WAIT_TIMEOUT:
  116. Py_RETURN_FALSE;
  117. case WAIT_OBJECT_0:
  118. self->last_tid = GetCurrentThreadId();
  119. ++self->count;
  120. Py_RETURN_TRUE;
  121. case WAIT_FAILED:
  122. return PyErr_SetFromWindowsErr(0);
  123. default:
  124. PyErr_Format(PyExc_RuntimeError, "WaitForSingleObject() or "
  125. "WaitForMultipleObjects() gave unrecognized "
  126. "value %d", res);
  127. return NULL;
  128. }
  129. }
  130. static PyObject *
  131. semlock_release(SemLockObject *self, PyObject *args)
  132. {
  133. if (self->kind == RECURSIVE_MUTEX) {
  134. if (!ISMINE(self)) {
  135. PyErr_SetString(PyExc_AssertionError, "attempt to "
  136. "release recursive lock not owned "
  137. "by thread");
  138. return NULL;
  139. }
  140. if (self->count > 1) {
  141. --self->count;
  142. Py_RETURN_NONE;
  143. }
  144. assert(self->count == 1);
  145. }
  146. if (!ReleaseSemaphore(self->handle, 1, NULL)) {
  147. if (GetLastError() == ERROR_TOO_MANY_POSTS) {
  148. PyErr_SetString(PyExc_ValueError, "semaphore or lock "
  149. "released too many times");
  150. return NULL;
  151. } else {
  152. return PyErr_SetFromWindowsErr(0);
  153. }
  154. }
  155. --self->count;
  156. Py_RETURN_NONE;
  157. }
  158. #else /* !MS_WINDOWS */
  159. /*
  160. * Unix definitions
  161. */
  162. #define SEM_CLEAR_ERROR()
  163. #define SEM_GET_LAST_ERROR() 0
  164. #define SEM_CREATE(name, val, max) sem_open(name, O_CREAT | O_EXCL, 0600, val)
  165. #define SEM_CLOSE(sem) sem_close(sem)
  166. #define SEM_GETVALUE(sem, pval) sem_getvalue(sem, pval)
  167. #define SEM_UNLINK(name) sem_unlink(name)
  168. #if HAVE_BROKEN_SEM_UNLINK
  169. # define sem_unlink(name) 0
  170. #endif
  171. #if !HAVE_SEM_TIMEDWAIT
  172. # define sem_timedwait(sem,deadline) sem_timedwait_save(sem,deadline,_save)
  173. int
  174. sem_timedwait_save(sem_t *sem, struct timespec *deadline, PyThreadState *_save)
  175. {
  176. int res;
  177. unsigned long delay, difference;
  178. struct timeval now, tvdeadline, tvdelay;
  179. errno = 0;
  180. tvdeadline.tv_sec = deadline->tv_sec;
  181. tvdeadline.tv_usec = deadline->tv_nsec / 1000;
  182. for (delay = 0 ; ; delay += 1000) {
  183. /* poll */
  184. if (sem_trywait(sem) == 0)
  185. return 0;
  186. else if (errno != EAGAIN)
  187. return MP_STANDARD_ERROR;
  188. /* get current time */
  189. if (gettimeofday(&now, NULL) < 0)
  190. return MP_STANDARD_ERROR;
  191. /* check for timeout */
  192. if (tvdeadline.tv_sec < now.tv_sec ||
  193. (tvdeadline.tv_sec == now.tv_sec &&
  194. tvdeadline.tv_usec <= now.tv_usec)) {
  195. errno = ETIMEDOUT;
  196. return MP_STANDARD_ERROR;
  197. }
  198. /* calculate how much time is left */
  199. difference = (tvdeadline.tv_sec - now.tv_sec) * 1000000 +
  200. (tvdeadline.tv_usec - now.tv_usec);
  201. /* check delay not too long -- maximum is 20 msecs */
  202. if (delay > 20000)
  203. delay = 20000;
  204. if (delay > difference)
  205. delay = difference;
  206. /* sleep */
  207. tvdelay.tv_sec = delay / 1000000;
  208. tvdelay.tv_usec = delay % 1000000;
  209. if (select(0, NULL, NULL, NULL, &tvdelay) < 0)
  210. return MP_STANDARD_ERROR;
  211. /* check for signals */
  212. Py_BLOCK_THREADS
  213. res = PyErr_CheckSignals();
  214. Py_UNBLOCK_THREADS
  215. if (res) {
  216. errno = EINTR;
  217. return MP_EXCEPTION_HAS_BEEN_SET;
  218. }
  219. }
  220. }
  221. #endif /* !HAVE_SEM_TIMEDWAIT */
  222. static PyObject *
  223. semlock_acquire(SemLockObject *self, PyObject *args, PyObject *kwds)
  224. {
  225. int blocking = 1, res;
  226. double timeout;
  227. PyObject *timeout_obj = Py_None;
  228. struct timespec deadline = {0};
  229. struct timeval now;
  230. long sec, nsec;
  231. static char *kwlist[] = {"block", "timeout", NULL};
  232. if (!PyArg_ParseTupleAndKeywords(args, kwds, "|iO", kwlist,
  233. &blocking, &timeout_obj))
  234. return NULL;
  235. if (self->kind == RECURSIVE_MUTEX && ISMINE(self)) {
  236. ++self->count;
  237. Py_RETURN_TRUE;
  238. }
  239. if (timeout_obj != Py_None) {
  240. timeout = PyFloat_AsDouble(timeout_obj);
  241. if (PyErr_Occurred())
  242. return NULL;
  243. if (timeout < 0.0)
  244. timeout = 0.0;
  245. if (gettimeofday(&now, NULL) < 0) {
  246. PyErr_SetFromErrno(PyExc_OSError);
  247. return NULL;
  248. }
  249. sec = (long) timeout;
  250. nsec = (long) (1e9 * (timeout - sec) + 0.5);
  251. deadline.tv_sec = now.tv_sec + sec;
  252. deadline.tv_nsec = now.tv_usec * 1000 + nsec;
  253. deadline.tv_sec += (deadline.tv_nsec / 1000000000);
  254. deadline.tv_nsec %= 1000000000;
  255. }
  256. do {
  257. Py_BEGIN_ALLOW_THREADS
  258. if (blocking && timeout_obj == Py_None)
  259. res = sem_wait(self->handle);
  260. else if (!blocking)
  261. res = sem_trywait(self->handle);
  262. else
  263. res = sem_timedwait(self->handle, &deadline);
  264. Py_END_ALLOW_THREADS
  265. if (res == MP_EXCEPTION_HAS_BEEN_SET)
  266. break;
  267. } while (res < 0 && errno == EINTR && !PyErr_CheckSignals());
  268. if (res < 0) {
  269. if (errno == EAGAIN || errno == ETIMEDOUT)
  270. Py_RETURN_FALSE;
  271. else if (errno == EINTR)
  272. return NULL;
  273. else
  274. return PyErr_SetFromErrno(PyExc_OSError);
  275. }
  276. ++self->count;
  277. self->last_tid = PyThread_get_thread_ident();
  278. Py_RETURN_TRUE;
  279. }
  280. static PyObject *
  281. semlock_release(SemLockObject *self, PyObject *args)
  282. {
  283. if (self->kind == RECURSIVE_MUTEX) {
  284. if (!ISMINE(self)) {
  285. PyErr_SetString(PyExc_AssertionError, "attempt to "
  286. "release recursive lock not owned "
  287. "by thread");
  288. return NULL;
  289. }
  290. if (self->count > 1) {
  291. --self->count;
  292. Py_RETURN_NONE;
  293. }
  294. assert(self->count == 1);
  295. } else {
  296. #if HAVE_BROKEN_SEM_GETVALUE
  297. /* We will only check properly the maxvalue == 1 case */
  298. if (self->maxvalue == 1) {
  299. /* make sure that already locked */
  300. if (sem_trywait(self->handle) < 0) {
  301. if (errno != EAGAIN) {
  302. PyErr_SetFromErrno(PyExc_OSError);
  303. return NULL;
  304. }
  305. /* it is already locked as expected */
  306. } else {
  307. /* it was not locked so undo wait and raise */
  308. if (sem_post(self->handle) < 0) {
  309. PyErr_SetFromErrno(PyExc_OSError);
  310. return NULL;
  311. }
  312. PyErr_SetString(PyExc_ValueError, "semaphore "
  313. "or lock released too many "
  314. "times");
  315. return NULL;
  316. }
  317. }
  318. #else
  319. int sval;
  320. /* This check is not an absolute guarantee that the semaphore
  321. does not rise above maxvalue. */
  322. if (sem_getvalue(self->handle, &sval) < 0) {
  323. return PyErr_SetFromErrno(PyExc_OSError);
  324. } else if (sval >= self->maxvalue) {
  325. PyErr_SetString(PyExc_ValueError, "semaphore or lock "
  326. "released too many times");
  327. return NULL;
  328. }
  329. #endif
  330. }
  331. if (sem_post(self->handle) < 0)
  332. return PyErr_SetFromErrno(PyExc_OSError);
  333. --self->count;
  334. Py_RETURN_NONE;
  335. }
  336. #endif /* !MS_WINDOWS */
  337. /*
  338. * All platforms
  339. */
  340. static PyObject *
  341. newsemlockobject(PyTypeObject *type, SEM_HANDLE handle, int kind, int maxvalue)
  342. {
  343. SemLockObject *self;
  344. self = PyObject_New(SemLockObject, type);
  345. if (!self)
  346. return NULL;
  347. self->handle = handle;
  348. self->kind = kind;
  349. self->count = 0;
  350. self->last_tid = 0;
  351. self->maxvalue = maxvalue;
  352. return (PyObject*)self;
  353. }
  354. static PyObject *
  355. semlock_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
  356. {
  357. char buffer[256];
  358. SEM_HANDLE handle = SEM_FAILED;
  359. int kind, maxvalue, value;
  360. PyObject *result;
  361. static char *kwlist[] = {"kind", "value", "maxvalue", NULL};
  362. static int counter = 0;
  363. if (!PyArg_ParseTupleAndKeywords(args, kwds, "iii", kwlist,
  364. &kind, &value, &maxvalue))
  365. return NULL;
  366. if (kind != RECURSIVE_MUTEX && kind != SEMAPHORE) {
  367. PyErr_SetString(PyExc_ValueError, "unrecognized kind");
  368. return NULL;
  369. }
  370. PyOS_snprintf(buffer, sizeof(buffer), "/mp%d-%d", getpid(), counter++);
  371. SEM_CLEAR_ERROR();
  372. handle = SEM_CREATE(buffer, value, maxvalue);
  373. /* On Windows we should fail if GetLastError()==ERROR_ALREADY_EXISTS */
  374. if (handle == SEM_FAILED || SEM_GET_LAST_ERROR() != 0)
  375. goto failure;
  376. if (SEM_UNLINK(buffer) < 0)
  377. goto failure;
  378. result = newsemlockobject(type, handle, kind, maxvalue);
  379. if (!result)
  380. goto failure;
  381. return result;
  382. failure:
  383. if (handle != SEM_FAILED)
  384. SEM_CLOSE(handle);
  385. mp_SetError(NULL, MP_STANDARD_ERROR);
  386. return NULL;
  387. }
  388. static PyObject *
  389. semlock_rebuild(PyTypeObject *type, PyObject *args)
  390. {
  391. SEM_HANDLE handle;
  392. int kind, maxvalue;
  393. if (!PyArg_ParseTuple(args, F_SEM_HANDLE "ii",
  394. &handle, &kind, &maxvalue))
  395. return NULL;
  396. return newsemlockobject(type, handle, kind, maxvalue);
  397. }
  398. static void
  399. semlock_dealloc(SemLockObject* self)
  400. {
  401. if (self->handle != SEM_FAILED)
  402. SEM_CLOSE(self->handle);
  403. PyObject_Del(self);
  404. }
  405. static PyObject *
  406. semlock_count(SemLockObject *self)
  407. {
  408. return PyInt_FromLong((long)self->count);
  409. }
  410. static PyObject *
  411. semlock_ismine(SemLockObject *self)
  412. {
  413. /* only makes sense for a lock */
  414. return PyBool_FromLong(ISMINE(self));
  415. }
  416. static PyObject *
  417. semlock_getvalue(SemLockObject *self)
  418. {
  419. #if HAVE_BROKEN_SEM_GETVALUE
  420. PyErr_SetNone(PyExc_NotImplementedError);
  421. return NULL;
  422. #else
  423. int sval;
  424. if (SEM_GETVALUE(self->handle, &sval) < 0)
  425. return mp_SetError(NULL, MP_STANDARD_ERROR);
  426. /* some posix implementations use negative numbers to indicate
  427. the number of waiting threads */
  428. if (sval < 0)
  429. sval = 0;
  430. return PyInt_FromLong((long)sval);
  431. #endif
  432. }
  433. static PyObject *
  434. semlock_iszero(SemLockObject *self)
  435. {
  436. #if HAVE_BROKEN_SEM_GETVALUE
  437. if (sem_trywait(self->handle) < 0) {
  438. if (errno == EAGAIN)
  439. Py_RETURN_TRUE;
  440. return mp_SetError(NULL, MP_STANDARD_ERROR);
  441. } else {
  442. if (sem_post(self->handle) < 0)
  443. return mp_SetError(NULL, MP_STANDARD_ERROR);
  444. Py_RETURN_FALSE;
  445. }
  446. #else
  447. int sval;
  448. if (SEM_GETVALUE(self->handle, &sval) < 0)
  449. return mp_SetError(NULL, MP_STANDARD_ERROR);
  450. return PyBool_FromLong((long)sval == 0);
  451. #endif
  452. }
  453. static PyObject *
  454. semlock_afterfork(SemLockObject *self)
  455. {
  456. self->count = 0;
  457. Py_RETURN_NONE;
  458. }
  459. /*
  460. * Semaphore methods
  461. */
  462. static PyMethodDef semlock_methods[] = {
  463. {"acquire", (PyCFunction)semlock_acquire, METH_VARARGS | METH_KEYWORDS,
  464. "acquire the semaphore/lock"},
  465. {"release", (PyCFunction)semlock_release, METH_NOARGS,
  466. "release the semaphore/lock"},
  467. {"__enter__", (PyCFunction)semlock_acquire, METH_VARARGS | METH_KEYWORDS,
  468. "enter the semaphore/lock"},
  469. {"__exit__", (PyCFunction)semlock_release, METH_VARARGS,
  470. "exit the semaphore/lock"},
  471. {"_count", (PyCFunction)semlock_count, METH_NOARGS,
  472. "num of `acquire()`s minus num of `release()`s for this process"},
  473. {"_is_mine", (PyCFunction)semlock_ismine, METH_NOARGS,
  474. "whether the lock is owned by this thread"},
  475. {"_get_value", (PyCFunction)semlock_getvalue, METH_NOARGS,
  476. "get the value of the semaphore"},
  477. {"_is_zero", (PyCFunction)semlock_iszero, METH_NOARGS,
  478. "returns whether semaphore has value zero"},
  479. {"_rebuild", (PyCFunction)semlock_rebuild, METH_VARARGS | METH_CLASS,
  480. ""},
  481. {"_after_fork", (PyCFunction)semlock_afterfork, METH_NOARGS,
  482. "rezero the net acquisition count after fork()"},
  483. {NULL}
  484. };
  485. /*
  486. * Member table
  487. */
  488. static PyMemberDef semlock_members[] = {
  489. {"handle", T_SEM_HANDLE, offsetof(SemLockObject, handle), READONLY,
  490. ""},
  491. {"kind", T_INT, offsetof(SemLockObject, kind), READONLY,
  492. ""},
  493. {"maxvalue", T_INT, offsetof(SemLockObject, maxvalue), READONLY,
  494. ""},
  495. {NULL}
  496. };
  497. /*
  498. * Semaphore type
  499. */
  500. PyTypeObject SemLockType = {
  501. PyVarObject_HEAD_INIT(NULL, 0)
  502. /* tp_name */ "_multiprocessing.SemLock",
  503. /* tp_basicsize */ sizeof(SemLockObject),
  504. /* tp_itemsize */ 0,
  505. /* tp_dealloc */ (destructor)semlock_dealloc,
  506. /* tp_print */ 0,
  507. /* tp_getattr */ 0,
  508. /* tp_setattr */ 0,
  509. /* tp_compare */ 0,
  510. /* tp_repr */ 0,
  511. /* tp_as_number */ 0,
  512. /* tp_as_sequence */ 0,
  513. /* tp_as_mapping */ 0,
  514. /* tp_hash */ 0,
  515. /* tp_call */ 0,
  516. /* tp_str */ 0,
  517. /* tp_getattro */ 0,
  518. /* tp_setattro */ 0,
  519. /* tp_as_buffer */ 0,
  520. /* tp_flags */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
  521. /* tp_doc */ "Semaphore/Mutex type",
  522. /* tp_traverse */ 0,
  523. /* tp_clear */ 0,
  524. /* tp_richcompare */ 0,
  525. /* tp_weaklistoffset */ 0,
  526. /* tp_iter */ 0,
  527. /* tp_iternext */ 0,
  528. /* tp_methods */ semlock_methods,
  529. /* tp_members */ semlock_members,
  530. /* tp_getset */ 0,
  531. /* tp_base */ 0,
  532. /* tp_dict */ 0,
  533. /* tp_descr_get */ 0,
  534. /* tp_descr_set */ 0,
  535. /* tp_dictoffset */ 0,
  536. /* tp_init */ 0,
  537. /* tp_alloc */ 0,
  538. /* tp_new */ semlock_new,
  539. };