process_shared 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/COPYING +19 -0
- data/ChangeLog +0 -0
- data/README.rdoc +69 -0
- data/ext/libpsem/bsem.c +188 -0
- data/ext/libpsem/bsem.h +32 -0
- data/ext/libpsem/constants.c +22 -0
- data/ext/libpsem/constants.h +18 -0
- data/ext/libpsem/extconf.rb +36 -0
- data/ext/libpsem/mempcpy.c +7 -0
- data/ext/libpsem/mempcpy.h +13 -0
- data/ext/libpsem/mutex.c +15 -0
- data/ext/libpsem/mutex.h +14 -0
- data/ext/libpsem/psem.c +15 -0
- data/ext/libpsem/psem.h +43 -0
- data/ext/libpsem/psem_error.c +46 -0
- data/ext/libpsem/psem_error.h +11 -0
- data/ext/libpsem/psem_posix.c +130 -0
- data/ext/libpsem/psem_posix.h +10 -0
- data/ext/pthread_sync_helper/extconf.rb +9 -0
- data/ext/pthread_sync_helper/pthread_sync_helper.c +43 -0
- data/ext/semaphore.c +623 -0
- data/lib/process_shared.rb +6 -0
- data/lib/process_shared/abstract_semaphore.rb +50 -0
- data/lib/process_shared/bounded_semaphore.rb +43 -0
- data/lib/process_shared/condition_variable.rb +27 -0
- data/lib/process_shared/libc.rb +36 -0
- data/lib/process_shared/libpsem.bundle +0 -0
- data/lib/process_shared/libpsem.so +0 -0
- data/lib/process_shared/mutex.rb +103 -0
- data/lib/process_shared/posix_call.rb +29 -0
- data/lib/process_shared/process_error.rb +3 -0
- data/lib/process_shared/psem.rb +109 -0
- data/lib/process_shared/rt.rb +21 -0
- data/lib/process_shared/semaphore.rb +60 -0
- data/lib/process_shared/shared_memory.rb +45 -0
- data/lib/process_shared/thread.rb +30 -0
- data/lib/process_shared/with_self.rb +20 -0
- data/lib/scratch.rb +300 -0
- data/spec/process_shared/bounded_semaphore_spec.rb +48 -0
- data/spec/process_shared/libc_spec.rb +9 -0
- data/spec/process_shared/mutex_spec.rb +74 -0
- data/spec/process_shared/psem_spec.rb +136 -0
- data/spec/process_shared/semaphore_spec.rb +76 -0
- data/spec/process_shared/shared_memory_spec.rb +36 -0
- data/spec/spec_helper.rb +35 -0
- metadata +139 -0
data/ext/libpsem/psem.c
ADDED
data/ext/libpsem/psem.h
ADDED
@@ -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,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,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;
|
data/ext/semaphore.c
ADDED
@@ -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
|
+
};
|