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.
- 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
|
+
};
|