1 #define PY_SSIZE_T_CLEAN
2 #include "Python.h"
3 #include "structmember.h"
4
5 #include "common.h"
6 #include "semaphore.h"
7
8 #define ONE_BILLION 1000000000
9
10 // This enum has to start at zero because its values are used as an
11 // arry index in sem_perform_semop().
12 enum SEMOP_TYPE {
13 SEMOP_P = 0,
14 SEMOP_V,
15 SEMOP_Z
16 };
17
18 /* Struct to contain a timeout which can be None */
19 typedef struct {
20 int is_none;
21 int is_zero;
22 struct timespec timestamp;
23 } NoneableTimeout;
24
25
26 // It is recommended practice to define this union here in the .c module, but
27 // it's been common practice for platforms to define it themselves in header
28 // files. For instance, BSD and OS X do so (provisionally) in sem.h. As a
29 // result, I need to surround this with an #ifdef.
30 #ifdef _SEM_SEMUN_UNDEFINED
31 union semun {
32 int val; /* used for SETVAL only */
33 struct semid_ds *buf; /* for IPC_STAT and IPC_SET */
34 unsigned short *array; /* used for GETALL and SETALL */
35 #ifdef __linux__
36 struct seminfo *__buf; /* Buffer for IPC_INFO (Linux-specific) */
37 #endif
38 };
39 #endif
40
41
42 static int
convert_timeout(PyObject * py_timeout,void * converted_timeout)43 convert_timeout(PyObject *py_timeout, void *converted_timeout) {
44 // Converts a PyObject into a timeout if possible. The PyObject should
45 // be None or some sort of numeric value (e.g. int, float, etc.)
46 // converted_timeout should point to a NoneableTimeout. When this function
47 // returns, if the NoneableTimeout's is_none is true, then the rest of the
48 // struct is undefined. Otherwise, the rest of the struct is populated.
49 int rc = 0;
50 double simple_timeout = 0;
51 NoneableTimeout *p_timeout = (NoneableTimeout *)converted_timeout;
52
53 // The timeout can be None or any Python numeric type (float,
54 // int, long).
55 if (py_timeout == Py_None)
56 rc = 1;
57 else if (PyFloat_Check(py_timeout)) {
58 rc = 1;
59 simple_timeout = PyFloat_AsDouble(py_timeout);
60 }
61 else if (PyLong_Check(py_timeout)) {
62 rc = 1;
63 simple_timeout = (double)PyLong_AsLong(py_timeout);
64 }
65
66 // The timeout may not be negative.
67 if ((rc) && (simple_timeout < 0))
68 rc = 0;
69
70 if (!rc)
71 PyErr_SetString(PyExc_TypeError,
72 "The timeout must be None or a non-negative number");
73 else {
74 if (py_timeout == Py_None)
75 p_timeout->is_none = 1;
76 else {
77 p_timeout->is_none = 0;
78
79 p_timeout->is_zero = (!simple_timeout);
80
81 // Note the difference between this and POSIX timeouts. System V
82 // timeouts expect tv_sec to represent a delta from the current
83 // time whereas POSIX semaphores expect an absolute value.
84 p_timeout->timestamp.tv_sec = (time_t)floor(simple_timeout);
85 p_timeout->timestamp.tv_nsec = (long)((simple_timeout - floor(simple_timeout)) * ONE_BILLION);
86 }
87 }
88
89 return rc;
90 }
91
92
93 PyObject *
sem_str(Semaphore * self)94 sem_str(Semaphore *self) {
95 return PyUnicode_FromFormat("Key=%ld, id=%d", (long)self->key, self->id);
96 }
97
98
99 PyObject *
sem_repr(Semaphore * self)100 sem_repr(Semaphore *self) {
101 return PyUnicode_FromFormat("sysv_ipc.Semaphore(%ld)", (long)self->key);
102 }
103
104
105 static void
sem_set_error(void)106 sem_set_error(void) {
107 switch (errno) {
108 case ENOENT:
109 case EINVAL:
110 PyErr_SetString(pExistentialException,
111 "No semaphore exists with the specified key");
112 break;
113
114 case EEXIST:
115 PyErr_SetString(pExistentialException,
116 "A semaphore with the specified key already exists");
117 break;
118
119 case EACCES:
120 PyErr_SetString(pPermissionsException, "Permission denied");
121 break;
122
123 case ERANGE:
124 PyErr_Format(PyExc_ValueError,
125 "The semaphore's value must remain between 0 and %ld (SEMAPHORE_VALUE_MAX)",
126 (long)SEMAPHORE_VALUE_MAX);
127 break;
128
129 case EAGAIN:
130 PyErr_SetString(pBusyException, "The semaphore is busy");
131 break;
132
133 case EIDRM:
134 PyErr_SetString(pExistentialException, "The semaphore was removed");
135 break;
136
137 case EINTR:
138 PyErr_SetString(pBaseException, "Signaled while waiting");
139 break;
140
141 case ENOMEM:
142 PyErr_SetString(PyExc_MemoryError, "Not enough memory");
143 break;
144
145 default:
146 PyErr_SetFromErrno(PyExc_OSError);
147 break;
148 }
149 }
150
151
152 static PyObject *
sem_perform_semop(enum SEMOP_TYPE op_type,Semaphore * self,PyObject * args,PyObject * keywords)153 sem_perform_semop(enum SEMOP_TYPE op_type, Semaphore *self, PyObject *args, PyObject *keywords) {
154 int rc = 0;
155 NoneableTimeout timeout;
156 struct sembuf op[1];
157 /* delta (a.k.a. struct sembuf.sem_op) is a short
158 ref: http://www.opengroup.org/onlinepubs/000095399/functions/semop.html
159 */
160 short int delta;
161 char *keyword_list[3][3] = {
162 {"timeout", "delta", NULL}, // P == acquire
163 {"delta", NULL}, // V == release
164 {"timeout", NULL} // Z == zero test
165 };
166
167
168 /* Initialize this to the default value. If the user doesn't pass a
169 timeout, Python won't call convert_timeout() and so the timeout
170 will be otherwise uninitialized.
171 */
172 timeout.is_none = 1;
173
174 /* op_type is P, V or Z corresponding to the 3 Semaphore methods
175 that call that call semop(). */
176 switch (op_type) {
177 case SEMOP_P:
178 // P == acquire
179 delta = -1;
180 rc = PyArg_ParseTupleAndKeywords(args, keywords, "|O&h",
181 keyword_list[SEMOP_P],
182 convert_timeout, &timeout,
183 &delta);
184
185 if (rc && !delta) {
186 rc = 0;
187 PyErr_SetString(PyExc_ValueError, "The delta must be non-zero");
188 }
189 else
190 delta = -abs(delta);
191 break;
192
193 case SEMOP_V:
194 // V == release
195 delta = 1;
196 rc = PyArg_ParseTupleAndKeywords(args, keywords, "|h",
197 keyword_list[SEMOP_V],
198 &delta);
199
200 if (rc && !delta) {
201 rc = 0;
202 PyErr_SetString(PyExc_ValueError, "The delta must be non-zero");
203 }
204 else
205 delta = abs(delta);
206 break;
207
208 case SEMOP_Z:
209 // Z = Zero test
210 delta = 0;
211 rc = PyArg_ParseTupleAndKeywords(args, keywords, "|O&",
212 keyword_list[SEMOP_Z],
213 convert_timeout, &timeout);
214 break;
215
216 default:
217 PyErr_Format(pInternalException, "Bad op_type (%d)", op_type);
218 rc = 0;
219 break;
220 }
221
222 if (!rc)
223 goto error_return;
224
225 // Now that the caller's params have been vetted, I set up the op struct
226 // that I'm going to pass to semop().
227 op[0].sem_num = 0;
228 op[0].sem_op = delta;
229 op[0].sem_flg = self->op_flags;
230
231 Py_BEGIN_ALLOW_THREADS;
232 #ifdef SEMTIMEDOP_EXISTS
233 // Call semtimedop() if appropriate, otherwise call semop()
234 if (!timeout.is_none) {
235 DPRINTF("calling semtimedop on id %d, op.sem_op=%d, op.flags=0x%x\n",
236 self->id, op[0].sem_op, op[0].sem_flg);
237 DPRINTF("timeout tv_sec = %ld; timeout tv_nsec = %ld\n",
238 timeout.timestamp.tv_sec, timeout.timestamp.tv_nsec);
239 rc = semtimedop(self->id, op, 1, &timeout.timestamp);
240 }
241 else {
242 DPRINTF("calling semop on id %d, op.sem_op = %d, op.flags=%x\n",
243 self->id, op[0].sem_op, op[0].sem_flg);
244 rc = semop(self->id, op, 1);
245 }
246 #else
247 // no support for semtimedop(), always call semop() instead.
248 DPRINTF("calling semop on id %d, op.sem_op = %d, op.flags=%x\n",
249 self->id, op[0].sem_op, op[0].sem_flg);
250 rc = semop(self->id, op, 1);
251 #endif
252 Py_END_ALLOW_THREADS;
253
254 if (rc == -1) {
255 sem_set_error();
256 goto error_return;
257 }
258
259 Py_RETURN_NONE;
260
261 error_return:
262 return NULL;
263 }
264
265
266 // cmd can be any of the values defined in the documentation for semctl().
267 static PyObject *
sem_get_semctl_value(int semaphore_id,int cmd)268 sem_get_semctl_value(int semaphore_id, int cmd) {
269 int rc;
270
271 // semctl() returns an int
272 // ref: http://www.opengroup.org/onlinepubs/000095399/functions/semctl.html
273 rc = semctl(semaphore_id, 0, cmd);
274
275 if (-1 == rc) {
276 sem_set_error();
277 goto error_return;
278 }
279
280 return PyLong_FromLong(rc);
281
282 error_return:
283 return NULL;
284 }
285
286
287 static PyObject *
sem_get_ipc_perm_value(int id,enum GET_SET_IDENTIFIERS field)288 sem_get_ipc_perm_value(int id, enum GET_SET_IDENTIFIERS field) {
289 struct semid_ds sem_info;
290 union semun arg;
291 PyObject *py_value = NULL;
292
293 arg.buf = &sem_info;
294
295 // Here I get the values currently associated with the semaphore.
296 if (-1 == semctl(id, 0, IPC_STAT, arg)) {
297 sem_set_error();
298 goto error_return;
299 }
300
301 switch (field) {
302 case SVIFP_IPC_PERM_UID:
303 py_value = UID_T_TO_PY(sem_info.sem_perm.uid);
304 break;
305
306 case SVIFP_IPC_PERM_GID:
307 py_value = GID_T_TO_PY(sem_info.sem_perm.gid);
308 break;
309
310 case SVIFP_IPC_PERM_CUID:
311 py_value = UID_T_TO_PY(sem_info.sem_perm.cuid);
312 break;
313
314 case SVIFP_IPC_PERM_CGID:
315 py_value = GID_T_TO_PY(sem_info.sem_perm.cgid);
316 break;
317
318 case SVIFP_IPC_PERM_MODE:
319 py_value = MODE_T_TO_PY(sem_info.sem_perm.mode);
320 break;
321
322 // This isn't an ipc_perm value but it fits here anyway.
323 case SVIFP_SEM_OTIME:
324 py_value = TIME_T_TO_PY(sem_info.sem_otime);
325 break;
326
327 default:
328 PyErr_Format(pInternalException,
329 "Bad field %d passed to sem_get_ipc_perm_value", field);
330 goto error_return;
331 break;
332 }
333
334 return py_value;
335
336 error_return:
337 return NULL;
338 }
339
340
341 static int
sem_set_ipc_perm_value(int id,enum GET_SET_IDENTIFIERS field,PyObject * py_value)342 sem_set_ipc_perm_value(int id, enum GET_SET_IDENTIFIERS field, PyObject *py_value) {
343 struct semid_ds sem_info;
344 union semun arg;
345
346 arg.buf = &sem_info;
347
348 if (!PyLong_Check(py_value))
349 {
350 PyErr_Format(PyExc_TypeError, "The attribute must be an integer");
351 goto error_return;
352 }
353
354 arg.buf = &sem_info;
355
356 /* Here I get the current values associated with the semaphore. It's
357 critical to populate sem_info with current values here (rather than
358 just using the struct filled with whatever garbage it acquired from
359 being declared on the stack) because the call to semctl(...IPC_SET...)
360 below will copy uid, gid and mode to the kernel's data structure.
361 */
362 if (-1 == semctl(id, 0, IPC_STAT, arg)) {
363 sem_set_error();
364 goto error_return;
365 }
366
367 // Below I'm stuffing a Python int converted to a C long into a
368 // uid_t, gid_t or mode_t. A long might not fit, hence the explicit
369 // cast. If the user passes a value that's too big, tough cookies.
370 switch (field) {
371 case SVIFP_IPC_PERM_UID:
372 sem_info.sem_perm.uid = (uid_t)PyLong_AsLong(py_value);
373 break;
374
375 case SVIFP_IPC_PERM_GID:
376 sem_info.sem_perm.gid = (gid_t)PyLong_AsLong(py_value);
377 break;
378
379 case SVIFP_IPC_PERM_MODE:
380 sem_info.sem_perm.mode = (mode_t)PyLong_AsLong(py_value);
381 break;
382
383 default:
384 PyErr_Format(pInternalException,
385 "Bad field %d passed to sem_set_ipc_perm_value", field);
386 goto error_return;
387 break;
388 }
389
390 if (-1 == semctl(id, 0, IPC_SET, arg)) {
391 sem_set_error();
392 goto error_return;
393 }
394
395 return 0;
396
397 error_return:
398 return -1;
399 }
400
401
402 PyObject *
sem_remove(int id)403 sem_remove(int id) {
404 if (NULL == sem_get_semctl_value(id, IPC_RMID))
405 return NULL;
406 else
407 Py_RETURN_NONE;
408 }
409
410
411 void
Semaphore_dealloc(Semaphore * self)412 Semaphore_dealloc(Semaphore *self) {
413 Py_TYPE(self)->tp_free((PyObject*)self);
414 }
415
416 PyObject *
Semaphore_new(PyTypeObject * type,PyObject * args,PyObject * keywords)417 Semaphore_new(PyTypeObject *type, PyObject *args, PyObject *keywords) {
418 Semaphore *self;
419
420 self = (Semaphore *)type->tp_alloc(type, 0);
421
422 return (PyObject *)self;
423 }
424
425
426 int
Semaphore_init(Semaphore * self,PyObject * args,PyObject * keywords)427 Semaphore_init(Semaphore *self, PyObject *args, PyObject *keywords) {
428 int mode = 0600;
429 int initial_value = 0;
430 int flags = 0;
431 union semun arg;
432 char *keyword_list[ ] = {"key", "flags", "mode", "initial_value", NULL};
433 NoneableKey key;
434
435 //Semaphore(key, [flags = 0, [mode = 0600, [initial_value = 0]]])
436
437 if (!PyArg_ParseTupleAndKeywords(args, keywords, "O&|iii", keyword_list,
438 &convert_key_param, &key, &flags,
439 &mode, &initial_value))
440 goto error_return;
441
442 DPRINTF("key is none = %d, key value = %ld\n", key.is_none, (long)key.value);
443
444 if ( !(flags & IPC_CREAT) && (flags & IPC_EXCL) ) {
445 PyErr_SetString(PyExc_ValueError,
446 "IPC_EXCL must be combined with IPC_CREAT");
447 goto error_return;
448 }
449
450 if (key.is_none && ((flags & IPC_EXCL) != IPC_EXCL)) {
451 PyErr_SetString(PyExc_ValueError,
452 "Key can only be None if IPC_EXCL is set");
453 goto error_return;
454 }
455
456 self->op_flags = 0;
457
458 // I mask the caller's flags against the two IPC_* flags to ensure that
459 // nothing funky sneaks into the flags.
460 flags &= (IPC_CREAT | IPC_EXCL);
461
462 // Note that Sys V sems can be in "sets" (arrays) but I hardcode this
463 // to always be a set with just one member.
464 // Permissions and flags (i.e. IPC_CREAT | IPC_EXCL) are both crammed
465 // into the 3rd param.
466 if (key.is_none) {
467 // (key == None) ==> generate a key for the caller
468 do {
469 errno = 0;
470 self->key = get_random_key();
471
472 DPRINTF("Calling semget, key=%ld, mode=%o, flags=%x\n",
473 (long)self->key, mode, flags);
474 self->id = semget(self->key, 1, mode | flags);
475 } while ( (-1 == self->id) && (EEXIST == errno) );
476 }
477 else {
478 // (key != None) ==> use key supplied by the caller
479 self->key = key.value;
480
481 DPRINTF("Calling semget, key=%ld, mode=%o, flags=%x\n",
482 (long)self->key, mode, flags);
483 self->id = semget(self->key, 1, mode | flags);
484 }
485
486 DPRINTF("id == %d\n", self->id);
487
488 if (self->id == -1) {
489 sem_set_error();
490 goto error_return;
491 }
492
493 // Before attempting to set the initial value, I have to be sure that
494 // I created this semaphore and that I have write access to it.
495 if ((flags & IPC_CREX) && (mode & 0200)) {
496 DPRINTF("setting initial value to %d\n", initial_value);
497 arg.val = initial_value;
498
499 if (-1 == semctl(self->id, 0, SETVAL, arg)) {
500 sem_set_error();
501 goto error_return;
502 }
503 }
504
505 return 0;
506
507 error_return:
508 return -1;
509 }
510
511
512 PyObject *
Semaphore_P(Semaphore * self,PyObject * args,PyObject * keywords)513 Semaphore_P(Semaphore *self, PyObject *args, PyObject *keywords) {
514 return sem_perform_semop(SEMOP_P, self, args, keywords);
515 }
516
517
518 PyObject *
Semaphore_acquire(Semaphore * self,PyObject * args,PyObject * keywords)519 Semaphore_acquire(Semaphore *self, PyObject *args, PyObject *keywords) {
520 return Semaphore_P(self, args, keywords);
521 }
522
523
524 PyObject *
Semaphore_V(Semaphore * self,PyObject * args,PyObject * keywords)525 Semaphore_V(Semaphore *self, PyObject *args, PyObject *keywords) {
526 return sem_perform_semop(SEMOP_V, self, args, keywords);
527 }
528
529
530 PyObject *
Semaphore_release(Semaphore * self,PyObject * args,PyObject * keywords)531 Semaphore_release(Semaphore *self, PyObject *args, PyObject *keywords) {
532 return Semaphore_V(self, args, keywords);
533 }
534
535
536 PyObject *
Semaphore_Z(Semaphore * self,PyObject * args,PyObject * keywords)537 Semaphore_Z(Semaphore *self, PyObject *args, PyObject *keywords) {
538 return sem_perform_semop(SEMOP_Z, self, args, keywords);
539 }
540
541
542 PyObject *
Semaphore_remove(Semaphore * self)543 Semaphore_remove(Semaphore *self) {
544 return sem_remove(self->id);
545 }
546
547 PyObject *
Semaphore_enter(Semaphore * self)548 Semaphore_enter(Semaphore *self) {
549 PyObject *args = PyTuple_New(0);
550 PyObject *retval = NULL;
551
552 if (Semaphore_acquire(self, args, NULL)) {
553 retval = (PyObject *)self;
554 Py_INCREF(self);
555 }
556
557 Py_DECREF(args);
558
559 return retval;
560 }
561
562 PyObject *
Semaphore_exit(Semaphore * self,PyObject * args)563 Semaphore_exit(Semaphore *self, PyObject *args) {
564 PyObject *release_args = PyTuple_New(0);
565 PyObject *retval = NULL;
566
567 DPRINTF("exiting context and releasing semaphore %ld\n", (long)self->key);
568
569 retval = Semaphore_release(self, release_args, NULL);
570
571 Py_DECREF(release_args);
572
573 return retval;
574 }
575
576 PyObject *
sem_get_key(Semaphore * self)577 sem_get_key(Semaphore *self) {
578 return KEY_T_TO_PY(self->key);
579 }
580
581 PyObject *
sem_get_value(Semaphore * self)582 sem_get_value(Semaphore *self) {
583 return sem_get_semctl_value(self->id, GETVAL);
584 }
585
586
587 int
sem_set_value(Semaphore * self,PyObject * py_value)588 sem_set_value(Semaphore *self, PyObject *py_value)
589 {
590 union semun arg;
591 long value;
592
593 if (!PyLong_Check(py_value))
594 {
595 PyErr_Format(PyExc_TypeError, "Attribute 'value' must be an integer");
596 goto error_return;
597 }
598
599 value = PyLong_AsLong(py_value);
600
601 DPRINTF("C value is %ld\n", value);
602
603 if ((-1 == value) && PyErr_Occurred()) {
604 // No idea wht could cause this -- just raise it to the caller.
605 goto error_return;
606 }
607
608 if ((value < 0) || (value > SEMAPHORE_VALUE_MAX)) {
609 PyErr_Format(PyExc_ValueError,
610 "Attribute 'value' must be between 0 and %ld (SEMAPHORE_VALUE_MAX)",
611 (long)SEMAPHORE_VALUE_MAX);
612 goto error_return;
613 }
614
615 arg.val = value;
616
617 if (-1 == semctl(self->id, 0, SETVAL, arg)) {
618 sem_set_error();
619 goto error_return;
620 }
621
622 return 0;
623
624 error_return:
625 return -1;
626 }
627
628
629 PyObject *
sem_get_block(Semaphore * self)630 sem_get_block(Semaphore *self) {
631 DPRINTF("op_flags: %x\n", self->op_flags);
632 return PyBool_FromLong( (self->op_flags & IPC_NOWAIT) ? 0 : 1);
633 }
634
635
636 int
sem_set_block(Semaphore * self,PyObject * py_value)637 sem_set_block(Semaphore *self, PyObject *py_value)
638 {
639 DPRINTF("op_flags before: %x\n", self->op_flags);
640
641 if (PyObject_IsTrue(py_value))
642 self->op_flags &= ~IPC_NOWAIT;
643 else
644 self->op_flags |= IPC_NOWAIT;
645
646 DPRINTF("op_flags after: %x\n", self->op_flags);
647
648 return 0;
649 }
650
651
652 PyObject *
sem_get_mode(Semaphore * self)653 sem_get_mode(Semaphore *self) {
654 return sem_get_ipc_perm_value(self->id, SVIFP_IPC_PERM_MODE);
655 }
656
657
658 int
sem_set_mode(Semaphore * self,PyObject * py_value)659 sem_set_mode(Semaphore *self, PyObject *py_value) {
660 return sem_set_ipc_perm_value(self->id, SVIFP_IPC_PERM_MODE, py_value);
661 }
662
663
664 PyObject *
sem_get_undo(Semaphore * self)665 sem_get_undo(Semaphore *self) {
666 return PyBool_FromLong( (self->op_flags & SEM_UNDO) ? 1 : 0 );
667 }
668
669
670 int
sem_set_undo(Semaphore * self,PyObject * py_value)671 sem_set_undo(Semaphore *self, PyObject *py_value)
672 {
673 DPRINTF("op_flags before: %x\n", self->op_flags);
674
675 if (PyObject_IsTrue(py_value))
676 self->op_flags |= SEM_UNDO;
677 else
678 self->op_flags &= ~SEM_UNDO;
679
680 DPRINTF("op_flags after: %x\n", self->op_flags);
681
682 return 0;
683 }
684
685
686 PyObject *
sem_get_uid(Semaphore * self)687 sem_get_uid(Semaphore *self) {
688 return sem_get_ipc_perm_value(self->id, SVIFP_IPC_PERM_UID);
689 }
690
691
692 int
sem_set_uid(Semaphore * self,PyObject * py_value)693 sem_set_uid(Semaphore *self, PyObject *py_value) {
694 return sem_set_ipc_perm_value(self->id, SVIFP_IPC_PERM_UID, py_value);
695 }
696
697
698 PyObject *
sem_get_gid(Semaphore * self)699 sem_get_gid(Semaphore *self) {
700 return sem_get_ipc_perm_value(self->id, SVIFP_IPC_PERM_GID);
701 }
702
703
704 int
sem_set_gid(Semaphore * self,PyObject * py_value)705 sem_set_gid(Semaphore *self, PyObject *py_value) {
706 return sem_set_ipc_perm_value(self->id, SVIFP_IPC_PERM_GID, py_value);
707 }
708
709 PyObject *
sem_get_c_uid(Semaphore * self)710 sem_get_c_uid(Semaphore *self) {
711 return sem_get_ipc_perm_value(self->id, SVIFP_IPC_PERM_CUID);
712 }
713
714 PyObject *
sem_get_c_gid(Semaphore * self)715 sem_get_c_gid(Semaphore *self) {
716 return sem_get_ipc_perm_value(self->id, SVIFP_IPC_PERM_CGID);
717 }
718
719 PyObject *
sem_get_last_pid(Semaphore * self)720 sem_get_last_pid(Semaphore *self) {
721 return sem_get_semctl_value(self->id, GETPID);
722 }
723
724 PyObject *
sem_get_waiting_for_nonzero(Semaphore * self)725 sem_get_waiting_for_nonzero(Semaphore *self) {
726 return sem_get_semctl_value(self->id, GETNCNT);
727 }
728
729 PyObject *
sem_get_waiting_for_zero(Semaphore * self)730 sem_get_waiting_for_zero(Semaphore *self) {
731 return sem_get_semctl_value(self->id, GETZCNT);
732 }
733
734 PyObject *
sem_get_o_time(Semaphore * self)735 sem_get_o_time(Semaphore *self) {
736 return sem_get_ipc_perm_value(self->id, SVIFP_SEM_OTIME);
737 }
738
739