process_shared 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. data/COPYING +19 -0
  2. data/ChangeLog +0 -0
  3. data/README.rdoc +69 -0
  4. data/ext/libpsem/bsem.c +188 -0
  5. data/ext/libpsem/bsem.h +32 -0
  6. data/ext/libpsem/constants.c +22 -0
  7. data/ext/libpsem/constants.h +18 -0
  8. data/ext/libpsem/extconf.rb +36 -0
  9. data/ext/libpsem/mempcpy.c +7 -0
  10. data/ext/libpsem/mempcpy.h +13 -0
  11. data/ext/libpsem/mutex.c +15 -0
  12. data/ext/libpsem/mutex.h +14 -0
  13. data/ext/libpsem/psem.c +15 -0
  14. data/ext/libpsem/psem.h +43 -0
  15. data/ext/libpsem/psem_error.c +46 -0
  16. data/ext/libpsem/psem_error.h +11 -0
  17. data/ext/libpsem/psem_posix.c +130 -0
  18. data/ext/libpsem/psem_posix.h +10 -0
  19. data/ext/pthread_sync_helper/extconf.rb +9 -0
  20. data/ext/pthread_sync_helper/pthread_sync_helper.c +43 -0
  21. data/ext/semaphore.c +623 -0
  22. data/lib/process_shared.rb +6 -0
  23. data/lib/process_shared/abstract_semaphore.rb +50 -0
  24. data/lib/process_shared/bounded_semaphore.rb +43 -0
  25. data/lib/process_shared/condition_variable.rb +27 -0
  26. data/lib/process_shared/libc.rb +36 -0
  27. data/lib/process_shared/libpsem.bundle +0 -0
  28. data/lib/process_shared/libpsem.so +0 -0
  29. data/lib/process_shared/mutex.rb +103 -0
  30. data/lib/process_shared/posix_call.rb +29 -0
  31. data/lib/process_shared/process_error.rb +3 -0
  32. data/lib/process_shared/psem.rb +109 -0
  33. data/lib/process_shared/rt.rb +21 -0
  34. data/lib/process_shared/semaphore.rb +60 -0
  35. data/lib/process_shared/shared_memory.rb +45 -0
  36. data/lib/process_shared/thread.rb +30 -0
  37. data/lib/process_shared/with_self.rb +20 -0
  38. data/lib/scratch.rb +300 -0
  39. data/spec/process_shared/bounded_semaphore_spec.rb +48 -0
  40. data/spec/process_shared/libc_spec.rb +9 -0
  41. data/spec/process_shared/mutex_spec.rb +74 -0
  42. data/spec/process_shared/psem_spec.rb +136 -0
  43. data/spec/process_shared/semaphore_spec.rb +76 -0
  44. data/spec/process_shared/shared_memory_spec.rb +36 -0
  45. data/spec/spec_helper.rb +35 -0
  46. metadata +139 -0
@@ -0,0 +1,15 @@
1
+ #include "psem.h"
2
+
3
+ int OK = 0;
4
+ int ERROR = -1;
5
+
6
+ int E_SOURCE_SYSTEM = 1;
7
+ int E_SOURCE_PSEM = 2;
8
+
9
+ int E_NAME_TOO_LONG = 1;
10
+
11
+ #ifdef HAVE_SEM_OPEN
12
+ #include "psem_posix.c"
13
+ #endif
14
+
15
+ size_t sizeof_psem_t = sizeof (psem_t);
@@ -0,0 +1,43 @@
1
+ #ifndef __PSEM_H__
2
+ #define __PSEM_H__
3
+
4
+ /**
5
+ * Portable semaphore interface focusing on cross-process use.
6
+ */
7
+
8
+ #ifdef HAVE_SEM_OPEN
9
+ #include "psem_posix.h"
10
+ #endif
11
+
12
+ #include "psem_error.h"
13
+
14
+ typedef struct psem psem_t;
15
+
16
+ extern size_t sizeof_psem_t;
17
+
18
+ extern int OK;
19
+ extern int ERROR;
20
+
21
+ extern int E_SOURCE_SYSTEM;
22
+ extern int E_SOURCE_PSEM;
23
+
24
+ extern int E_NAME_TOO_LONG;
25
+
26
+ int psem_errno();
27
+
28
+ psem_t * psem_alloc();
29
+ void psem_free(psem_t *);
30
+
31
+ int psem_open(psem_t *, const char *, unsigned int, error_t **);
32
+ int psem_close(psem_t *, error_t **);
33
+ int psem_unlink(const char *, error_t **);
34
+
35
+ int psem_post(psem_t *, error_t **);
36
+
37
+ int psem_wait(psem_t *, error_t **);
38
+ int psem_trywait(psem_t *, error_t **);
39
+ int psem_timedwait(psem_t *, float, error_t **);
40
+
41
+ int psem_getvalue(psem_t *, int *, error_t **);
42
+
43
+ #endif /* __PSEM_H__ */
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Similar to GError from GLib.
3
+ */
4
+
5
+ #include <stdlib.h> /* malloc, free */
6
+
7
+ #include "psem_error.h"
8
+
9
+ struct error {
10
+ int error_source;
11
+ int error_number;
12
+ };
13
+
14
+ error_t *
15
+ error_alloc()
16
+ {
17
+ return (error_t *) malloc(sizeof (error_t));
18
+ }
19
+
20
+ void
21
+ error_free(error_t *err)
22
+ {
23
+ free(err);
24
+ }
25
+
26
+ void
27
+ error_set(error_t *err, int source, int value)
28
+ {
29
+ err->error_source = source;
30
+ err->error_number = value;
31
+ }
32
+
33
+ void
34
+ error_new(error_t **err, int source, int value)
35
+ {
36
+ if (err != NULL) {
37
+ if (*err == NULL) {
38
+ *err = error_alloc();
39
+ error_set(*err, source, value);
40
+ } else {
41
+ /* tried to create a new error atop an existing error... */
42
+ }
43
+ } else {
44
+ /* error is being ignored by caller */
45
+ }
46
+ }
@@ -0,0 +1,11 @@
1
+ #ifndef __PSEM_ERROR_H__
2
+ #define __PSEM_ERROR_H__
3
+
4
+ typedef struct error error_t;
5
+
6
+ error_t * error_alloc();
7
+ void error_free(error_t *);
8
+
9
+ void error_set(error_t *, int, int);
10
+
11
+ #endif /* __PSEM_ERROR_H__ */
@@ -0,0 +1,130 @@
1
+ /*
2
+ * A type which wraps a semaphore
3
+ *
4
+ * semaphore.c
5
+ *
6
+ * Copyright (c) 2006-2008, R Oudkerk
7
+ *
8
+ * All rights reserved.
9
+ *
10
+ * Redistribution and use in source and binary forms, with or without
11
+ * modification, are permitted provided that the following conditions
12
+ * are met:
13
+ *
14
+ * 1. Redistributions of source code must retain the above copyright
15
+ * notice, this list of conditions and the following disclaimer.
16
+ *
17
+ * 2. Redistributions in binary form must reproduce the above
18
+ * copyright notice, this list of conditions and the following
19
+ * disclaimer in the documentation and/or other materials provided
20
+ * with the distribution.
21
+ *
22
+ * 3. Neither the name of author nor the names of any contributors
23
+ * may be used to endorse or promote products derived from this
24
+ * software without specific prior written permission.
25
+ *
26
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS"
27
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
28
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
29
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR
30
+ * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
31
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
32
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
33
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
34
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
35
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
36
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
37
+ * POSSIBILITY OF SUCH DAMAGE.
38
+ */
39
+
40
+ /*
41
+ * Modifications Copyright (c) 2011, Patrick Mahoney
42
+ */
43
+
44
+ #include <errno.h>
45
+ #include <fcntl.h> /* For O_* constants */
46
+ #include <sys/stat.h> /* For mode constants */
47
+ #include <semaphore.h>
48
+ #include <stdlib.h> /* malloc, free */
49
+ #include <math.h> /* floorf */
50
+ #include <time.h> /* timespec */
51
+
52
+ #include "psem.h"
53
+ #include "psem_posix.h"
54
+
55
+ psem_t *
56
+ psem_alloc(void) {
57
+ return (psem_t *) malloc(sizeof(psem_t));
58
+ }
59
+
60
+ void
61
+ psem_free(psem_t *psem) {
62
+ free(psem);
63
+ }
64
+
65
+ #define errcheck_val(expr, errval, err) \
66
+ do { \
67
+ if ((expr) == (errval)) { \
68
+ error_new((err), E_SOURCE_SYSTEM, errno); \
69
+ return ERROR; \
70
+ } \
71
+ return OK; \
72
+ } while (0)
73
+
74
+ #define errcheck(expr, err) errcheck_val((expr), -1, (err))
75
+
76
+ int
77
+ psem_open(psem_t *psem, const char *name, unsigned int value, error_t **err)
78
+ {
79
+ errcheck_val(psem->sem = sem_open(name, O_CREAT | O_EXCL, 0600, value),
80
+ SEM_FAILED,
81
+ err);
82
+ }
83
+
84
+ int
85
+ psem_close(psem_t *psem, error_t **err)
86
+ {
87
+ errcheck(sem_close(psem->sem), err);
88
+ }
89
+
90
+ int
91
+ psem_unlink(const char *name, error_t **err)
92
+ {
93
+ errcheck(sem_unlink(name), err);
94
+ }
95
+
96
+ int
97
+ psem_post(psem_t *psem, error_t **err)
98
+ {
99
+ errcheck(sem_post(psem->sem), err);
100
+ }
101
+
102
+ int
103
+ psem_wait(psem_t *psem, error_t **err)
104
+ {
105
+ errcheck(sem_wait(psem->sem), err);
106
+ }
107
+
108
+ int
109
+ psem_trywait(psem_t *psem, error_t **err)
110
+ {
111
+ errcheck(sem_trywait(psem->sem), err);
112
+ }
113
+
114
+ int
115
+ psem_timedwait(psem_t *psem, float timeout_s, error_t **err)
116
+ {
117
+ struct timespec abs_timeout;
118
+
119
+ abs_timeout.tv_sec = floorf(timeout_s);
120
+ abs_timeout.tv_nsec =
121
+ floorf((timeout_s - abs_timeout.tv_sec) * (1000 * 1000 * 1000));
122
+
123
+ errcheck(sem_timedwait(psem->sem, &abs_timeout), err);
124
+ }
125
+
126
+ int
127
+ psem_getvalue(psem_t *psem, int *sval, error_t **err)
128
+ {
129
+ errcheck(sem_getvalue(psem->sem, sval), err);
130
+ }
@@ -0,0 +1,10 @@
1
+ #ifndef __PSEM_POSIX_H__
2
+ #define __PSEM_POSIX_H__
3
+
4
+ #include <semaphore.h>
5
+
6
+ struct psem {
7
+ sem_t *sem;
8
+ };
9
+
10
+ #endif /* __PSEM_POSIX_H__ */
@@ -0,0 +1,9 @@
1
+ require 'mkmf'
2
+
3
+ #have_func("pthread_mutex_init", "pthread.h")
4
+ #have_func("pthread_cond_init", "pthread.h")
5
+ #have_func("pthread_mutex_trylock", "pthread.h")
6
+
7
+ have_library("pthread", "pthread_mutex_init")
8
+
9
+ create_makefile("pthread_sync_helper")
@@ -0,0 +1,43 @@
1
+ #include <pthread.h>
2
+ #include <sys/mman.h> /* PROT_*, MAP_* */
3
+ #include <fcntl.h> /* O_* */
4
+
5
+ /* Declarations. These are split this way to avoid compiler warnings. */
6
+
7
+ extern size_t sizeof_pthread_mutex_t;
8
+ extern size_t sizeof_pthread_mutexattr_t;
9
+
10
+ extern int pthread_process_shared;
11
+
12
+ extern int o_rdwr;
13
+ extern int o_creat;
14
+
15
+ extern int prot_read;
16
+ extern int prot_write;
17
+ extern int prot_exec;
18
+ extern int prot_none;
19
+
20
+ extern void * map_failed;
21
+
22
+ extern int map_shared;
23
+ extern int map_private;
24
+
25
+ /* Definitions. These are split from declrations to avoid compiler warnings. */
26
+
27
+ size_t sizeof_pthread_mutex_t = sizeof (pthread_mutex_t);
28
+ size_t sizeof_pthread_mutexattr_t = sizeof (pthread_mutexattr_t);
29
+
30
+ int pthread_process_shared = PTHREAD_PROCESS_SHARED;
31
+
32
+ int o_rdwr = O_RDWR;
33
+ int o_creat = O_CREAT;
34
+
35
+ int prot_read = PROT_READ;
36
+ int prot_write = PROT_WRITE;
37
+ int prot_exec = PROT_EXEC;
38
+ int prot_none = PROT_NONE;
39
+
40
+ void * map_failed = MAP_FAILED;
41
+
42
+ int map_shared = MAP_SHARED;
43
+ int map_private = MAP_PRIVATE;
@@ -0,0 +1,623 @@
1
+ /*
2
+ * A type which wraps a semaphore
3
+ *
4
+ * semaphore.c
5
+ *
6
+ * Copyright (c) 2006-2008, R Oudkerk
7
+ *
8
+ * All rights reserved.
9
+ *
10
+ * Redistribution and use in source and binary forms, with or without
11
+ * modification, are permitted provided that the following conditions
12
+ * are met:
13
+ *
14
+ * 1. Redistributions of source code must retain the above copyright
15
+ * notice, this list of conditions and the following disclaimer.
16
+ *
17
+ * 2. Redistributions in binary form must reproduce the above
18
+ * copyright notice, this list of conditions and the following
19
+ * disclaimer in the documentation and/or other materials provided
20
+ * with the distribution.
21
+ *
22
+ * 3. Neither the name of author nor the names of any contributors
23
+ * may be used to endorse or promote products derived from this
24
+ * software without specific prior written permission.
25
+ *
26
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS"
27
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
28
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
29
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR
30
+ * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
31
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
32
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
33
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
34
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
35
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
36
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
37
+ * POSSIBILITY OF SUCH DAMAGE.
38
+ */
39
+
40
+ /*
41
+ * Modifications Copyright (c) 2011, Patrick Mahoney
42
+ */
43
+
44
+ #include "processing.h"
45
+
46
+ enum {
47
+ RECURSIVE_MUTEX,
48
+ SEMAPHORE,
49
+ BOUNDED_SEMAPHORE
50
+ };
51
+
52
+ typedef struct {
53
+ PyObject_HEAD
54
+ SEM_HANDLE handle;
55
+ long last_tid;
56
+ int count;
57
+ int maxvalue;
58
+ int kind;
59
+ } SemLock;
60
+
61
+ #define ISMINE(o) (o->count > 0 && PyThread_get_thread_ident() == o->last_tid)
62
+
63
+
64
+ #ifdef MS_WINDOWS
65
+
66
+ /*
67
+ * Windows definitions
68
+ */
69
+
70
+ static SECURITY_ATTRIBUTES sa = { sizeof(SECURITY_ATTRIBUTES), NULL, TRUE };
71
+
72
+ #define SEM_FAILED NULL
73
+
74
+ #define SEM_CLEAR_ERROR() SetLastError(0)
75
+ #define SEM_GET_LAST_ERROR() GetLastError()
76
+ #define SEM_CREATE(name, val, max) CreateSemaphore(&sa, val, max, NULL)
77
+ #define SEM_CLOSE(sem) (CloseHandle(sem) ? 0 : -1)
78
+ #define SEM_GETVALUE(sem, pval) _SemLock_GetSemaphoreValue(sem, pval)
79
+ #define SEM_UNLINK(name) 0
80
+
81
+ static int
82
+ _SemLock_GetSemaphoreValue(HANDLE handle, long *value)
83
+ {
84
+ long previous;
85
+
86
+ switch (WaitForSingleObject(handle, 0)) {
87
+ case WAIT_OBJECT_0:
88
+ if (!ReleaseSemaphore(handle, 1, &previous))
89
+ return STANDARD_ERROR;
90
+ *value = previous + 1;
91
+ return 0;
92
+ case WAIT_TIMEOUT:
93
+ *value = 0;
94
+ return 0;
95
+ default:
96
+ return STANDARD_ERROR;
97
+ }
98
+ }
99
+
100
+ static PyObject *
101
+ SemLock_acquire(SemLock *self, PyObject *args, PyObject *kwds)
102
+ {
103
+ int blocking = 1;
104
+ double timeout;
105
+ PyObject *timeout_obj = Py_None;
106
+ DWORD res, dwTimeout;
107
+
108
+ static char *kwlist[] = {"block", "timeout", NULL};
109
+
110
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "|iO", kwlist,
111
+ &blocking, &timeout_obj))
112
+ return NULL;
113
+
114
+ if (self->kind == RECURSIVE_MUTEX && ISMINE(self)) {
115
+ ++self->count;
116
+ Py_RETURN_TRUE;
117
+ }
118
+
119
+ /* work out timeout */
120
+ if (!blocking) {
121
+ dwTimeout = 0;
122
+ } else if (timeout_obj == Py_None) {
123
+ dwTimeout = INFINITE;
124
+ } else {
125
+ timeout = PyFloat_AsDouble(timeout_obj);
126
+ if (PyErr_Occurred())
127
+ return NULL;
128
+ timeout *= 1000.0; /* convert to millisecs */
129
+ if (timeout < 0.0) {
130
+ timeout = 0.0;
131
+ } else if (timeout >= ((double)INFINITE - 0.5)) {
132
+ PyErr_SetString(PyExc_OverflowError, "timeout is too large");
133
+ return NULL;
134
+ }
135
+ dwTimeout = (DWORD)(timeout + 0.5);
136
+ }
137
+
138
+ /* do the wait */
139
+ if (dwTimeout == 0) {
140
+ res = WaitForSingleObject(self->handle, 0);
141
+ } else if (main_thread == GetCurrentThreadId()) {
142
+ res = WaitForSingleObject(self->handle, 0);
143
+ if (res == WAIT_TIMEOUT) {
144
+ HANDLE handles[2] = {self->handle, hInterruptEvent};
145
+ ResetEvent(hInterruptEvent);
146
+ Py_BEGIN_ALLOW_THREADS
147
+ res = WaitForMultipleObjects(2, handles, FALSE, dwTimeout);
148
+ Py_END_ALLOW_THREADS
149
+ }
150
+ } else {
151
+ Py_BEGIN_ALLOW_THREADS
152
+ res = WaitForSingleObject(self->handle, dwTimeout);
153
+ Py_END_ALLOW_THREADS
154
+ }
155
+
156
+ /* handle result */
157
+ switch (res) {
158
+ case WAIT_TIMEOUT:
159
+ Py_RETURN_FALSE;
160
+ case WAIT_OBJECT_0:
161
+ self->last_tid = GetCurrentThreadId();
162
+ ++self->count;
163
+ Py_RETURN_TRUE;
164
+ case (WAIT_OBJECT_0 + 1): /* we got SIGINT; do like in timemodule.c */
165
+ Sleep(1);
166
+ errno = EINTR;
167
+ PyErr_SetFromErrno(PyExc_OSError);
168
+ return NULL;
169
+ case WAIT_FAILED:
170
+ return PyErr_SetFromWindowsErr(0);
171
+ default:
172
+ PyErr_SetString(PyExc_RuntimeError, "WaitForSingleObject() or "
173
+ "WaitForMultipleObjects() gave unrecognized value");
174
+ return NULL;
175
+ }
176
+ }
177
+
178
+ static PyObject *
179
+ SemLock_release(SemLock *self, PyObject *args)
180
+ {
181
+ if (self->kind == RECURSIVE_MUTEX) {
182
+ if (!ISMINE(self)) {
183
+ PyErr_SetString(PyExc_AssertionError, "attempt to release "
184
+ "recursive lock not owned by thread");
185
+ return NULL;
186
+ }
187
+ if (self->count > 1) {
188
+ --self->count;
189
+ Py_RETURN_NONE;
190
+ }
191
+ assert(self->count == 1);
192
+ }
193
+
194
+ if (!ReleaseSemaphore(self->handle, 1, NULL)) {
195
+ if (GetLastError() == ERROR_TOO_MANY_POSTS) {
196
+ PyErr_SetString(PyExc_ValueError,
197
+ "semaphore or lock released too many times");
198
+ return NULL;
199
+ } else {
200
+ return PyErr_SetFromWindowsErr(0);
201
+ }
202
+ }
203
+
204
+ --self->count;
205
+ Py_RETURN_NONE;
206
+ }
207
+
208
+ #else /* !MS_WINDOWS */
209
+
210
+ /*
211
+ * Unix definitions
212
+ */
213
+
214
+ #define SEM_CLEAR_ERROR()
215
+ #define SEM_GET_LAST_ERROR() 0
216
+ #define SEM_CREATE(name, val, max) sem_open(name, O_CREAT | O_EXCL, 0600, val)
217
+ #define SEM_CLOSE(sem) sem_close(sem)
218
+ #define SEM_GETVALUE(sem, pval) sem_getvalue(sem, pval)
219
+ #define SEM_UNLINK(name) sem_unlink(name)
220
+
221
+ #if HAVE_BROKEN_SEM_UNLINK
222
+ # define sem_unlink(name) 0
223
+ #endif
224
+
225
+ #if !HAVE_SEM_TIMEDWAIT
226
+ # define sem_timedwait(sem,deadline) sem_timedwait_save(sem,deadline,_save)
227
+
228
+ int
229
+ sem_timedwait_save(sem_t *sem, struct timespec *deadline, PyThreadState *_save)
230
+ {
231
+ int res;
232
+ unsigned long delay, difference;
233
+ struct timeval now, tvdeadline, tvdelay;
234
+
235
+ errno = 0;
236
+ tvdeadline.tv_sec = deadline->tv_sec;
237
+ tvdeadline.tv_usec = deadline->tv_nsec / 1000;
238
+
239
+ for (delay = 0 ; ; delay += 1000) {
240
+ /* poll */
241
+ if (sem_trywait(sem) == 0)
242
+ return 0;
243
+ else if (errno != EAGAIN)
244
+ return STANDARD_ERROR;
245
+
246
+ /* get current time */
247
+ if (gettimeofday(&now, NULL) < 0)
248
+ return STANDARD_ERROR;
249
+
250
+ /* check for timeout */
251
+ if (tvdeadline.tv_sec < now.tv_sec ||
252
+ (tvdeadline.tv_sec == now.tv_sec &&
253
+ tvdeadline.tv_usec <= now.tv_usec)) {
254
+ errno = ETIMEDOUT;
255
+ return STANDARD_ERROR;
256
+ }
257
+
258
+ /* calculate how much time is left */
259
+ difference = (tvdeadline.tv_sec - now.tv_sec) * 1000000 +
260
+ (tvdeadline.tv_usec - now.tv_usec);
261
+
262
+ /* check delay not too long -- maximum is 20 msecs */
263
+ if (delay > 20000)
264
+ delay = 20000;
265
+ if (delay > difference)
266
+ delay = difference;
267
+
268
+ /* sleep */
269
+ tvdelay.tv_sec = delay / 1000000;
270
+ tvdelay.tv_usec = delay % 1000000;
271
+ if (select(0, NULL, NULL, NULL, &tvdelay) < 0)
272
+ return STANDARD_ERROR;
273
+
274
+ /* check for signals */
275
+ Py_BLOCK_THREADS
276
+ res = PyErr_CheckSignals();
277
+ Py_UNBLOCK_THREADS
278
+
279
+ if (res) {
280
+ errno = EINTR;
281
+ return EXCEPTION_HAS_BEEN_SET;
282
+ }
283
+ }
284
+ }
285
+
286
+ #endif /* !HAVE_SEM_TIMEDWAIT */
287
+
288
+ static PyObject *
289
+ SemLock_acquire(SemLock *self, PyObject *args, PyObject *kwds)
290
+ {
291
+ int blocking = 1, res;
292
+ double timeout;
293
+ PyObject *timeout_obj = Py_None;
294
+ struct timespec deadline = {0};
295
+ struct timeval now;
296
+ long sec, nsec;
297
+
298
+ static char *kwlist[] = {"block", "timeout", NULL};
299
+
300
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "|iO", kwlist,
301
+ &blocking, &timeout_obj))
302
+ return NULL;
303
+
304
+ if (self->kind == RECURSIVE_MUTEX && ISMINE(self)) {
305
+ ++self->count;
306
+ Py_RETURN_TRUE;
307
+ }
308
+
309
+ if (timeout_obj != Py_None) {
310
+ timeout = PyFloat_AsDouble(timeout_obj);
311
+ if (PyErr_Occurred())
312
+ return NULL;
313
+ if (timeout < 0.0)
314
+ timeout = 0.0;
315
+
316
+ if (gettimeofday(&now, NULL) < 0) {
317
+ PyErr_SetFromErrno(PyExc_OSError);
318
+ return NULL;
319
+ }
320
+ sec = (long) timeout;
321
+ nsec = (long) (1e9 * (timeout - sec) + 0.5);
322
+ deadline.tv_sec = now.tv_sec + sec;
323
+ deadline.tv_nsec = now.tv_usec * 1000 + nsec;
324
+ deadline.tv_sec += (deadline.tv_nsec / 1000000000);
325
+ deadline.tv_nsec %= 1000000000;
326
+ }
327
+
328
+ do {
329
+ Py_BEGIN_ALLOW_THREADS
330
+ if (blocking && timeout_obj == Py_None)
331
+ res = sem_wait(self->handle);
332
+ else if (!blocking)
333
+ res = sem_trywait(self->handle);
334
+ else
335
+ res = sem_timedwait(self->handle, &deadline);
336
+ Py_END_ALLOW_THREADS
337
+ if (res == EXCEPTION_HAS_BEEN_SET)
338
+ break;
339
+ } while (res < 0 && errno == EINTR && !PyErr_CheckSignals());
340
+
341
+ if (res < 0) {
342
+ if (errno == EAGAIN || errno == ETIMEDOUT)
343
+ Py_RETURN_FALSE;
344
+ else if (errno == EINTR)
345
+ return NULL;
346
+ else
347
+ return PyErr_SetFromErrno(PyExc_OSError);
348
+ }
349
+
350
+ ++self->count;
351
+ self->last_tid = PyThread_get_thread_ident();
352
+
353
+ Py_RETURN_TRUE;
354
+ }
355
+
356
+ static PyObject *
357
+ SemLock_release(SemLock *self, PyObject *args)
358
+ {
359
+ switch (self->kind) {
360
+ case RECURSIVE_MUTEX:
361
+ if (!ISMINE(self)) {
362
+ PyErr_SetString(PyExc_AssertionError, "attempt to release "
363
+ "recursive lock not owned by thread");
364
+ return NULL;
365
+ }
366
+ if (self->count > 1) {
367
+ --self->count;
368
+ Py_RETURN_NONE;
369
+ }
370
+ assert(self->count == 1);
371
+ break;
372
+ #if HAVE_BROKEN_SEM_GETVALUE
373
+ case BOUNDED_SEMAPHORE:
374
+ /* We will only check properly the Lock case (where maxvalue == 1) */
375
+ if (self->maxvalue == 1) {
376
+ /* make sure that already locked */
377
+ if (sem_trywait(self->handle) < 0) {
378
+ if (errno != EAGAIN)
379
+ return PyErr_SetFromErrno(PyExc_OSError);
380
+ /* it is already locked as expected */
381
+ } else {
382
+ /* it was not locked -- so undo wait and raise error */
383
+ if (sem_post(self->handle) < 0)
384
+ return PyErr_SetFromErrno(PyExc_OSError);
385
+ PyErr_SetString(PyExc_ValueError,
386
+ "semaphore or lock released too many times");
387
+ return NULL;
388
+ }
389
+ }
390
+ #else
391
+ case BOUNDED_SEMAPHORE:
392
+ {
393
+ int sval;
394
+
395
+ /* This check is not an absolute guarantee that the semaphore
396
+ does not rise above maxvalue. */
397
+ if (sem_getvalue(self->handle, &sval) < 0) {
398
+ return PyErr_SetFromErrno(PyExc_OSError);
399
+ } else if (sval >= self->maxvalue) {
400
+ PyErr_SetString(PyExc_ValueError,
401
+ "semaphore or lock released too many times");
402
+ return NULL;
403
+ }
404
+ }
405
+ #endif
406
+ }
407
+
408
+ if (sem_post(self->handle) < 0)
409
+ return PyErr_SetFromErrno(PyExc_OSError);
410
+
411
+ --self->count;
412
+ Py_RETURN_NONE;
413
+ }
414
+
415
+ #endif /* !MS_WINDOWS */
416
+
417
+ /*
418
+ * All platforms
419
+ */
420
+
421
+ static PyObject *
422
+ _SemLock_create(PyTypeObject *type, SEM_HANDLE handle, int kind, int maxvalue)
423
+ {
424
+ SemLock *self;
425
+
426
+ self = (SemLock*)type->tp_alloc(type, 0);
427
+ if (!self)
428
+ return NULL;
429
+ self->handle = handle;
430
+ self->kind = kind;
431
+ self->count = 0;
432
+ self->last_tid = 0;
433
+ self->maxvalue = maxvalue;
434
+ return (PyObject*)self;
435
+ }
436
+
437
+ static PyObject *
438
+ SemLock_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
439
+ {
440
+ char buffer[256];
441
+ SEM_HANDLE handle = SEM_FAILED;
442
+ int kind, maxvalue, value;
443
+ PyObject *result;
444
+ static char *kwlist[] = {"kind", "value", NULL};
445
+ static int counter = 0;
446
+
447
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "ii", kwlist, &kind, &value))
448
+ return NULL;
449
+
450
+ if (kind < RECURSIVE_MUTEX || kind > BOUNDED_SEMAPHORE) {
451
+ PyErr_SetString(PyExc_ValueError, "unrecongnized blocker type");
452
+ return NULL;
453
+ }
454
+
455
+ PyOS_snprintf(buffer, sizeof(buffer), "/pr%d-%d", getpid(), counter++);
456
+
457
+ if (kind == BOUNDED_SEMAPHORE)
458
+ maxvalue = value;
459
+ else if (kind == RECURSIVE_MUTEX)
460
+ maxvalue = 1;
461
+ else
462
+ maxvalue = INT_MAX;
463
+
464
+ SEM_CLEAR_ERROR();
465
+ handle = SEM_CREATE(buffer, value, maxvalue);
466
+ /* On Windows we should fail if GetLastError() == ERROR_ALREADY_EXISTS */
467
+ if (handle == SEM_FAILED || SEM_GET_LAST_ERROR() != 0)
468
+ goto failure;
469
+
470
+ if (SEM_UNLINK(buffer) < 0)
471
+ goto failure;
472
+
473
+ result = _SemLock_create(type, handle, kind, maxvalue);
474
+ if (!result)
475
+ goto failure;
476
+
477
+ return result;
478
+
479
+ failure:
480
+ if (handle != SEM_FAILED)
481
+ SEM_CLOSE(handle);
482
+ SetException(NULL, STANDARD_ERROR);
483
+ return NULL;
484
+ }
485
+
486
+ static PyObject *
487
+ SemLock_rebuild(PyTypeObject *type, PyObject *args)
488
+ {
489
+ SEM_HANDLE handle;
490
+ int kind, maxvalue;
491
+
492
+ if (!PyArg_ParseTuple(args, F_SEM_HANDLE "ii", &handle, &kind, &maxvalue))
493
+ return NULL;
494
+
495
+ return _SemLock_create(type, handle, kind, maxvalue);
496
+ }
497
+
498
+ static void
499
+ SemLock_dealloc(SemLock* self)
500
+ {
501
+ if (self->handle != SEM_FAILED)
502
+ SEM_CLOSE(self->handle);
503
+ self->ob_type->tp_free((PyObject*)self);
504
+ }
505
+
506
+ static PyObject *
507
+ SemLock_count(SemLock *self)
508
+ {
509
+ return PyInt_FromLong((long)self->count);
510
+ }
511
+
512
+ static PyObject *
513
+ SemLock_ismine(SemLock *self)
514
+ {
515
+ /* only makes sense for a lock */
516
+ return PyBool_FromLong(ISMINE(self));
517
+ }
518
+
519
+ static PyObject *
520
+ SemLock_getvalue(SemLock *self)
521
+ {
522
+ #if HAVE_BROKEN_SEM_GETVALUE
523
+ PyErr_SetNone(PyExc_NotImplementedError);
524
+ return NULL;
525
+ #else
526
+ int sval;
527
+ if (SEM_GETVALUE(self->handle, &sval) < 0)
528
+ SetException(NULL, STANDARD_ERROR);
529
+ return PyInt_FromLong((long)sval);
530
+ #endif
531
+ }
532
+
533
+ static PyObject *
534
+ SemLock_afterfork(SemLock *self)
535
+ {
536
+ self->count = 0;
537
+ Py_RETURN_NONE;
538
+ }
539
+
540
+ /*
541
+ * Semaphore methods
542
+ */
543
+
544
+ static PyMethodDef SemLock_methods[] = {
545
+ {"acquire", (PyCFunction)SemLock_acquire, METH_KEYWORDS,
546
+ "acquire the semaphore/lock"},
547
+ {"release", (PyCFunction)SemLock_release, METH_NOARGS,
548
+ "release the semaphore/lock"},
549
+ {"__enter__", (PyCFunction)SemLock_acquire, METH_KEYWORDS,
550
+ "enter the semaphore/lock"},
551
+ {"__exit__", (PyCFunction)SemLock_release, METH_VARARGS,
552
+ "exit the semaphore/lock"},
553
+ {"_count", (PyCFunction)SemLock_count, METH_NOARGS,
554
+ "number of `acquire()`s minus number of `release()`s for this process"},
555
+ {"_isMine", (PyCFunction)SemLock_ismine, METH_NOARGS,
556
+ "whether the lock is owned by this thread"},
557
+ {"_getValue", (PyCFunction)SemLock_getvalue, METH_NOARGS,
558
+ "get the value of the semaphore"},
559
+ {"_rebuild", (PyCFunction)SemLock_rebuild, METH_VARARGS | METH_CLASS,
560
+ ""},
561
+ {"_afterFork", (PyCFunction)SemLock_afterfork, METH_NOARGS,
562
+ "rezero the net acquisition count after fork()"},
563
+ {NULL}
564
+ };
565
+
566
+ /*
567
+ * Member table
568
+ */
569
+
570
+ static PyMemberDef SemLock_members[] = {
571
+ {"handle", T_SEM_HANDLE, offsetof(SemLock, handle), READONLY, ""},
572
+ {"kind", T_INT, offsetof(SemLock, kind), READONLY, ""},
573
+ {"maxvalue", T_INT, offsetof(SemLock, maxvalue), READONLY, ""},
574
+ {NULL}
575
+ };
576
+
577
+ /*
578
+ * Semaphore type
579
+ */
580
+
581
+ PyTypeObject SemLockType = {
582
+ PyObject_HEAD_INIT(NULL)
583
+ 0, /* ob_size */
584
+ "_processing.SemLock", /* tp_name */
585
+ sizeof(SemLock), /* tp_basicsize */
586
+ 0, /* tp_itemsize */
587
+ (destructor)SemLock_dealloc,
588
+ /* tp_dealloc */
589
+ 0, /* tp_print */
590
+ 0, /* tp_getattr */
591
+ 0, /* tp_setattr */
592
+ 0, /* tp_compare */
593
+ 0, /* tp_repr */
594
+ 0, /* tp_as_number */
595
+ 0, /* tp_as_sequence */
596
+ 0, /* tp_as_mapping */
597
+ 0, /* tp_hash */
598
+ 0, /* tp_call */
599
+ 0, /* tp_str */
600
+ 0, /* tp_getattro */
601
+ 0, /* tp_setattro */
602
+ 0, /* tp_as_buffer */
603
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
604
+ /* tp_flags */
605
+ "Semaphore/Mutex type", /* tp_doc */
606
+ 0, /* tp_traverse */
607
+ 0, /* tp_clear */
608
+ 0, /* tp_richcompare */
609
+ 0, /* tp_weaklistoffset */
610
+ 0, /* tp_iter */
611
+ 0, /* tp_iternext */
612
+ SemLock_methods, /* tp_methods */
613
+ SemLock_members, /* tp_members */
614
+ 0, /* tp_getset */
615
+ 0, /* tp_base */
616
+ 0, /* tp_dict */
617
+ 0, /* tp_descr_get */
618
+ 0, /* tp_descr_set */
619
+ 0, /* tp_dictoffset */
620
+ 0, /* tp_init */
621
+ 0, /* tp_alloc */
622
+ (newfunc)SemLock_new, /* tp_new */
623
+ };