From a77e3a63f004e6ee789fa05e4a5bbc333b1529f1 Mon Sep 17 00:00:00 2001 From: Lorry Tar Creator Date: Fri, 12 Sep 2014 14:16:22 +0000 Subject: Imported from /home/lorry/working-area/delta_python-packages_sysv-ipc-tarball/sysv_ipc-0.6.8.tar.gz. --- semaphore.c | 776 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 776 insertions(+) create mode 100644 semaphore.c (limited to 'semaphore.c') diff --git a/semaphore.c b/semaphore.c new file mode 100644 index 0000000..57cf2aa --- /dev/null +++ b/semaphore.c @@ -0,0 +1,776 @@ +#include "Python.h" +#include "structmember.h" + +#include "common.h" +#include "semaphore.h" + +#define ONE_BILLION 1000000000 + +// This enum has to start at zero because its values are used as an +// arry index in sem_perform_semop(). +enum SEMOP_TYPE { + SEMOP_P = 0, + SEMOP_V, + SEMOP_Z +}; + +/* Struct to contain a timeout which can be None */ +typedef struct { + int is_none; + int is_zero; + struct timespec timestamp; +} NoneableTimeout; + + +// It is recommended practice to define this union here in the .c module, but +// it's been common practice for platforms to define it themselves in header +// files. For instance, BSD and OS X do so (provisionally) in sem.h. As a +// result, I need to surround this with an #ifdef. +#ifdef _SEM_SEMUN_UNDEFINED +union semun { + int val; /* used for SETVAL only */ + struct semid_ds *buf; /* for IPC_STAT and IPC_SET */ + unsigned short *array; /* used for GETALL and SETALL */ +}; +#endif + + +static int +convert_timeout(PyObject *py_timeout, void *converted_timeout) { + // Converts a PyObject into a timeout if possible. The PyObject should + // be None or some sort of numeric value (e.g. int, float, etc.) + // converted_timeout should point to a NoneableTimeout. When this function + // returns, if the NoneableTimeout's is_none is true, then the rest of the + // struct is undefined. Otherwise, the rest of the struct is populated. + int rc = 0; + double simple_timeout = 0; + NoneableTimeout *p_timeout = (NoneableTimeout *)converted_timeout; + + // The timeout can be None or any Python numeric type (float, + // int, long). + if (py_timeout == Py_None) + rc = 1; + else if (PyFloat_Check(py_timeout)) { + rc = 1; + simple_timeout = PyFloat_AsDouble(py_timeout); + } +#if PY_MAJOR_VERSION < 3 + else if (PyInt_Check(py_timeout)) { + rc = 1; + simple_timeout = (double)PyInt_AsLong(py_timeout); + } +#endif + else if (PyLong_Check(py_timeout)) { + rc = 1; + simple_timeout = (double)PyLong_AsLong(py_timeout); + } + + // The timeout may not be negative. + if ((rc) && (simple_timeout < 0)) + rc = 0; + + if (!rc) + PyErr_SetString(PyExc_TypeError, + "The timeout must be None or a non-negative number"); + else { + if (py_timeout == Py_None) + p_timeout->is_none = 1; + else { + p_timeout->is_none = 0; + + p_timeout->is_zero = (!simple_timeout); + + // Note the difference between this and POSIX timeouts. System V + // timeouts expect tv_sec to represent a delta from the current + // time whereas POSIX semaphores expect an absolute value. + p_timeout->timestamp.tv_sec = (time_t)floor(simple_timeout); + p_timeout->timestamp.tv_nsec = (long)((simple_timeout - floor(simple_timeout)) * ONE_BILLION); + } + } + + return rc; +} + + +PyObject * +sem_str(Semaphore *self) { +#if PY_MAJOR_VERSION > 2 + return PyUnicode_FromFormat("Key=%ld, id=%d", (long)self->key, self->id); +#else + return PyString_FromFormat("Key=%ld, id=%d", (long)self->key, self->id); +#endif +} + + +PyObject * +sem_repr(Semaphore *self) { +#if PY_MAJOR_VERSION > 2 + return PyUnicode_FromFormat("sysv_ipc.Semaphore(%ld)", (long)self->key); +#else + return PyString_FromFormat("sysv_ipc.Semaphore(%ld)", (long)self->key); +#endif +} + + +static void +sem_set_error(void) { + switch (errno) { + case ENOENT: + case EINVAL: + PyErr_SetString(pExistentialException, + "No semaphore exists with the specified key"); + break; + + case EEXIST: + PyErr_SetString(pExistentialException, + "A semaphore with the specified key already exists"); + break; + + case EACCES: + PyErr_SetString(pPermissionsException, "Permission denied"); + break; + + case ERANGE: + PyErr_Format(PyExc_ValueError, + "The semaphore's value must remain between 0 and %ld (SEMAPHORE_VALUE_MAX)", + (long)SEMAPHORE_VALUE_MAX); + break; + + case EAGAIN: + PyErr_SetString(pBusyException, "The semaphore is busy"); + break; + + case EIDRM: + PyErr_SetString(pExistentialException, "The semaphore was removed"); + break; + + case EINTR: + PyErr_SetString(pBaseException, "Signaled while waiting"); + break; + + case ENOMEM: + PyErr_SetString(PyExc_MemoryError, "Not enough memory"); + break; + + default: + PyErr_SetFromErrno(PyExc_OSError); + break; + } +} + + +static PyObject * +sem_perform_semop(enum SEMOP_TYPE op_type, Semaphore *self, PyObject *args, PyObject *keywords) { + int rc = 0; + NoneableTimeout timeout; + struct sembuf op[1]; + /* delta (a.k.a. struct sembuf.sem_op) is a short + ref: http://www.opengroup.org/onlinepubs/000095399/functions/semop.html + */ + short int delta; + char *keyword_list[3][3] = { + {"timeout", "delta", NULL}, // P == acquire + {"delta", NULL}, // V == release + {"timeout", NULL} // Z == zero test + }; + + + /* Initialize this to the default value. If the user doesn't pass a + timeout, Python won't call convert_timeout() and so the timeout + will be otherwise uninitialized. + */ + timeout.is_none = 1; + + /* op_type is P, V or Z corresponding to the 3 Semaphore methods + that call that call semop(). */ + switch (op_type) { + case SEMOP_P: + // P == acquire + delta = -1; + rc = PyArg_ParseTupleAndKeywords(args, keywords, "|O&h", + keyword_list[SEMOP_P], + convert_timeout, &timeout, + &delta); + + if (rc && !delta) { + rc = 0; + PyErr_SetString(PyExc_ValueError, "The delta must be non-zero"); + } + else + delta = -abs(delta); + break; + + case SEMOP_V: + // V == release + delta = 1; + rc = PyArg_ParseTupleAndKeywords(args, keywords, "|h", + keyword_list[SEMOP_V], + &delta); + + if (rc && !delta) { + rc = 0; + PyErr_SetString(PyExc_ValueError, "The delta must be non-zero"); + } + else + delta = abs(delta); + break; + + case SEMOP_Z: + // Z = Zero test + delta = 0; + rc = PyArg_ParseTupleAndKeywords(args, keywords, "|O&", + keyword_list[SEMOP_Z], + convert_timeout, &timeout); + break; + + default: + PyErr_Format(pInternalException, "Bad op_type (%d)", op_type); + rc = 0; + break; + } + + if (!rc) + goto error_return; + + // Now that the caller's params have been vetted, I set up the op struct + // that I'm going to pass to semop(). + op[0].sem_num = 0; + op[0].sem_op = delta; + op[0].sem_flg = self->op_flags; + + Py_BEGIN_ALLOW_THREADS; +#ifdef SEMTIMEDOP_EXISTS + // Call semtimedop() if appropriate, otherwise call semop() + if (!timeout.is_none) { + DPRINTF("calling semtimedop on id %d, op.sem_op=%d, op.flags=0x%x\n", + self->id, op[0].sem_op, op[0].sem_flg); + DPRINTF("timeout tv_sec = %ld; timeout tv_nsec = %ld\n", + timeout.timestamp.tv_sec, timeout.timestamp.tv_nsec); + rc = semtimedop(self->id, op, 1, &timeout.timestamp); + } + else { + DPRINTF("calling semop on id %d, op.sem_op = %d, op.flags=%x\n", + self->id, op[0].sem_op, op[0].sem_flg); + rc = semop(self->id, op, 1); + } +#else + // no support for semtimedop(), always call semop() instead. + DPRINTF("calling semop on id %d, op.sem_op = %d, op.flags=%x\n", + self->id, op[0].sem_op, op[0].sem_flg); + rc = semop(self->id, op, 1); +#endif + Py_END_ALLOW_THREADS; + + if (rc == -1) { + sem_set_error(); + goto error_return; + } + + Py_RETURN_NONE; + + error_return: + return NULL; +} + + +// cmd can be any of the values defined in the documentation for semctl(). +static PyObject * +sem_get_semctl_value(int semaphore_id, int cmd) { + int rc; + + // semctl() returns an int + // ref: http://www.opengroup.org/onlinepubs/000095399/functions/semctl.html + rc = semctl(semaphore_id, 0, cmd); + + if (-1 == rc) { + sem_set_error(); + goto error_return; + } + +#if PY_MAJOR_VERSION > 2 + return PyLong_FromLong(rc); +#else + return PyInt_FromLong(rc); +#endif + + error_return: + return NULL; +} + + +static PyObject * +sem_get_ipc_perm_value(int id, enum GET_SET_IDENTIFIERS field) { + struct semid_ds sem_info; + union semun arg; + PyObject *py_value = NULL; + + arg.buf = &sem_info; + + // Here I get the values currently associated with the semaphore. + if (-1 == semctl(id, 0, IPC_STAT, arg)) { + sem_set_error(); + goto error_return; + } + + switch (field) { + case SVIFP_IPC_PERM_UID: + py_value = UID_T_TO_PY(sem_info.sem_perm.uid); + break; + + case SVIFP_IPC_PERM_GID: + py_value = GID_T_TO_PY(sem_info.sem_perm.gid); + break; + + case SVIFP_IPC_PERM_CUID: + py_value = UID_T_TO_PY(sem_info.sem_perm.cuid); + break; + + case SVIFP_IPC_PERM_CGID: + py_value = GID_T_TO_PY(sem_info.sem_perm.cgid); + break; + + case SVIFP_IPC_PERM_MODE: + py_value = MODE_T_TO_PY(sem_info.sem_perm.mode); + break; + + // This isn't an ipc_perm value but it fits here anyway. + case SVIFP_SEM_OTIME: + py_value = TIME_T_TO_PY(sem_info.sem_otime); + break; + + default: + PyErr_Format(pInternalException, + "Bad field %d passed to sem_get_ipc_perm_value", field); + goto error_return; + break; + } + + return py_value; + + error_return: + return NULL; +} + + +static int +sem_set_ipc_perm_value(int id, enum GET_SET_IDENTIFIERS field, PyObject *py_value) { + struct semid_ds sem_info; + union semun arg; + + arg.buf = &sem_info; + +#if PY_MAJOR_VERSION > 2 + if (!PyLong_Check(py_value)) +#else + if (!PyInt_Check(py_value)) +#endif + { + PyErr_Format(PyExc_TypeError, "The attribute must be an integer"); + goto error_return; + } + + arg.buf = &sem_info; + + /* Here I get the current values associated with the semaphore. It's + critical to populate sem_info with current values here (rather than + just using the struct filled with whatever garbage it acquired from + being declared on the stack) because the call to semctl(...IPC_SET...) + below will copy uid, gid and mode to the kernel's data structure. + */ + if (-1 == semctl(id, 0, IPC_STAT, arg)) { + sem_set_error(); + goto error_return; + } + + // Below I'm stuffing a Python int converted to a C long into a + // uid_t, gid_t or mode_t. A long might not fit, hence the explicit + // cast. If the user passes a value that's too big, tough cookies. + switch (field) { + case SVIFP_IPC_PERM_UID: +#if PY_MAJOR_VERSION > 2 + sem_info.sem_perm.uid = (uid_t)PyLong_AsLong(py_value); +#else + sem_info.sem_perm.uid = (uid_t)PyInt_AsLong(py_value); +#endif + break; + + case SVIFP_IPC_PERM_GID: +#if PY_MAJOR_VERSION > 2 + sem_info.sem_perm.gid = (gid_t)PyLong_AsLong(py_value); +#else + sem_info.sem_perm.gid = (gid_t)PyInt_AsLong(py_value); +#endif + break; + + case SVIFP_IPC_PERM_MODE: +#if PY_MAJOR_VERSION > 2 + sem_info.sem_perm.mode = (mode_t)PyLong_AsLong(py_value); +#else + sem_info.sem_perm.mode = (mode_t)PyInt_AsLong(py_value); +#endif + break; + + default: + PyErr_Format(pInternalException, + "Bad field %d passed to sem_set_ipc_perm_value", field); + goto error_return; + break; + } + + if (-1 == semctl(id, 0, IPC_SET, arg)) { + sem_set_error(); + goto error_return; + } + + return 0; + + error_return: + return -1; +} + + +PyObject * +sem_remove(int id) { + if (NULL == sem_get_semctl_value(id, IPC_RMID)) + return NULL; + else + Py_RETURN_NONE; +} + + +void +Semaphore_dealloc(Semaphore *self) { + Py_TYPE(self)->tp_free((PyObject*)self); +} + +PyObject * +Semaphore_new(PyTypeObject *type, PyObject *args, PyObject *keywords) { + Semaphore *self; + + self = (Semaphore *)type->tp_alloc(type, 0); + + return (PyObject *)self; +} + + +int +Semaphore_init(Semaphore *self, PyObject *args, PyObject *keywords) { + int mode = 0600; + int initial_value = 0; + int flags = 0; + union semun arg; + char *keyword_list[ ] = {"key", "flags", "mode", "initial_value", NULL}; + NoneableKey key; + + //Semaphore(key, [flags = 0, [mode = 0600, [initial_value = 0]]]) + + if (!PyArg_ParseTupleAndKeywords(args, keywords, "O&|iii", keyword_list, + &convert_key_param, &key, &flags, + &mode, &initial_value)) + goto error_return; + + DPRINTF("key is none = %d, key value = %ld\n", key.is_none, (long)key.value); + + if ( !(flags & IPC_CREAT) && (flags & IPC_EXCL) ) { + PyErr_SetString(PyExc_ValueError, + "IPC_EXCL must be combined with IPC_CREAT"); + goto error_return; + } + + if (key.is_none && ((flags & IPC_EXCL) != IPC_EXCL)) { + PyErr_SetString(PyExc_ValueError, + "Key can only be None if IPC_EXCL is set"); + goto error_return; + } + + self->op_flags = 0; + + // I mask the caller's flags against the two IPC_* flags to ensure that + // nothing funky sneaks into the flags. + flags &= (IPC_CREAT | IPC_EXCL); + + // Note that Sys V sems can be in "sets" (arrays) but I hardcode this + // to always be a set with just one member. + // Permissions and flags (i.e. IPC_CREAT | IPC_EXCL) are both crammed + // into the 3rd param. + if (key.is_none) { + // (key == None) ==> generate a key for the caller + do { + errno = 0; + self->key = get_random_key(); + + DPRINTF("Calling semget, key=%ld, mode=%o, flags=%x\n", + (long)self->key, mode, flags); + self->id = semget(self->key, 1, mode | flags); + } while ( (-1 == self->id) && (EEXIST == errno) ); + } + else { + // (key != None) ==> use key supplied by the caller + self->key = key.value; + + DPRINTF("Calling semget, key=%ld, mode=%o, flags=%x\n", + (long)self->key, mode, flags); + self->id = semget(self->key, 1, mode | flags); + } + + DPRINTF("id == %d\n", self->id); + + if (self->id == -1) { + sem_set_error(); + goto error_return; + } + + // Before attempting to set the initial value, I have to be sure that + // I created this semaphore and that I have write access to it. + if ((flags & IPC_CREX) && (mode & 0200)) { + DPRINTF("setting initial value to %d\n", initial_value); + arg.val = initial_value; + + if (-1 == semctl(self->id, 0, SETVAL, arg)) { + sem_set_error(); + goto error_return; + } + } + + return 0; + + error_return: + return -1; +} + + +PyObject * +Semaphore_P(Semaphore *self, PyObject *args, PyObject *keywords) { + return sem_perform_semop(SEMOP_P, self, args, keywords); +} + + +PyObject * +Semaphore_acquire(Semaphore *self, PyObject *args, PyObject *keywords) { + return Semaphore_P(self, args, keywords); +} + + +PyObject * +Semaphore_V(Semaphore *self, PyObject *args, PyObject *keywords) { + return sem_perform_semop(SEMOP_V, self, args, keywords); +} + + +PyObject * +Semaphore_release(Semaphore *self, PyObject *args, PyObject *keywords) { + return Semaphore_V(self, args, keywords); +} + + +PyObject * +Semaphore_Z(Semaphore *self, PyObject *args, PyObject *keywords) { + return sem_perform_semop(SEMOP_Z, self, args, keywords); +} + + +PyObject * +Semaphore_remove(Semaphore *self) { + return sem_remove(self->id); +} + +PyObject * +Semaphore_enter(Semaphore *self) { + PyObject *args = PyTuple_New(0); + PyObject *retval = NULL; + + if (Semaphore_acquire(self, args, NULL)) { + retval = (PyObject *)self; + Py_INCREF(self); + } + + Py_DECREF(args); + + return retval; +} + +PyObject * +Semaphore_exit(Semaphore *self, PyObject *args) { + PyObject *release_args = PyTuple_New(0); + PyObject *retval = NULL; + + DPRINTF("exiting context and releasing semaphore %s\n", self->key); + + retval = Semaphore_release(self, release_args, NULL); + + Py_DECREF(release_args); + + return retval; +} + +PyObject * +sem_get_key(Semaphore *self) { + return KEY_T_TO_PY(self->key); +} + +PyObject * +sem_get_value(Semaphore *self) { + return sem_get_semctl_value(self->id, GETVAL); +} + + +int +sem_set_value(Semaphore *self, PyObject *py_value) +{ + union semun arg; + long value; + +#if PY_MAJOR_VERSION > 2 + if (!PyLong_Check(py_value)) +#else + if (!PyInt_Check(py_value)) +#endif + { + PyErr_Format(PyExc_TypeError, "Attribute 'value' must be an integer"); + goto error_return; + } + +#if PY_MAJOR_VERSION > 2 + value = PyLong_AsLong(py_value); +#else + value = PyInt_AsLong(py_value); +#endif + + DPRINTF("C value is %ld\n", value); + + if ((-1 == value) && PyErr_Occurred()) { + // No idea wht could cause this -- just raise it to the caller. + goto error_return; + } + + if ((value < 0) || (value > SEMAPHORE_VALUE_MAX)) { + PyErr_Format(PyExc_ValueError, + "Attribute 'value' must be between 0 and %ld (SEMAPHORE_VALUE_MAX)", + (long)SEMAPHORE_VALUE_MAX); + goto error_return; + } + + arg.val = value; + + if (-1 == semctl(self->id, 0, SETVAL, arg)) { + sem_set_error(); + goto error_return; + } + + return 0; + + error_return: + return -1; +} + + +PyObject * +sem_get_block(Semaphore *self) { + DPRINTF("op_flags: %x\n", self->op_flags); + return PyBool_FromLong( (self->op_flags & IPC_NOWAIT) ? 0 : 1); +} + + +int +sem_set_block(Semaphore *self, PyObject *py_value) +{ + DPRINTF("op_flags before: %x\n", self->op_flags); + + if (PyObject_IsTrue(py_value)) + self->op_flags &= ~IPC_NOWAIT; + else + self->op_flags |= IPC_NOWAIT; + + DPRINTF("op_flags after: %x\n", self->op_flags); + + return 0; +} + + +PyObject * +sem_get_mode(Semaphore *self) { + return sem_get_ipc_perm_value(self->id, SVIFP_IPC_PERM_MODE); +} + + +int +sem_set_mode(Semaphore *self, PyObject *py_value) { + return sem_set_ipc_perm_value(self->id, SVIFP_IPC_PERM_MODE, py_value); +} + + +PyObject * +sem_get_undo(Semaphore *self) { + return PyBool_FromLong( (self->op_flags & SEM_UNDO) ? 1 : 0 ); +} + + +int +sem_set_undo(Semaphore *self, PyObject *py_value) +{ + DPRINTF("op_flags before: %x\n", self->op_flags); + + if (PyObject_IsTrue(py_value)) + self->op_flags |= SEM_UNDO; + else + self->op_flags &= ~SEM_UNDO; + + DPRINTF("op_flags after: %x\n", self->op_flags); + + return 0; +} + + +PyObject * +sem_get_uid(Semaphore *self) { + return sem_get_ipc_perm_value(self->id, SVIFP_IPC_PERM_UID); +} + + +int +sem_set_uid(Semaphore *self, PyObject *py_value) { + return sem_set_ipc_perm_value(self->id, SVIFP_IPC_PERM_UID, py_value); +} + + +PyObject * +sem_get_gid(Semaphore *self) { + return sem_get_ipc_perm_value(self->id, SVIFP_IPC_PERM_GID); +} + + +int +sem_set_gid(Semaphore *self, PyObject *py_value) { + return sem_set_ipc_perm_value(self->id, SVIFP_IPC_PERM_GID, py_value); +} + +PyObject * +sem_get_c_uid(Semaphore *self) { + return sem_get_ipc_perm_value(self->id, SVIFP_IPC_PERM_CUID); +} + +PyObject * +sem_get_c_gid(Semaphore *self) { + return sem_get_ipc_perm_value(self->id, SVIFP_IPC_PERM_CGID); +} + +PyObject * +sem_get_last_pid(Semaphore *self) { + return sem_get_semctl_value(self->id, GETPID); +} + +PyObject * +sem_get_waiting_for_nonzero(Semaphore *self) { + return sem_get_semctl_value(self->id, GETNCNT); +} + +PyObject * +sem_get_waiting_for_zero(Semaphore *self) { + return sem_get_semctl_value(self->id, GETZCNT); +} + +PyObject * +sem_get_o_time(Semaphore *self) { + return sem_get_ipc_perm_value(self->id, SVIFP_SEM_OTIME); +} + -- cgit v1.2.1