forked from gitpan/pyperl
-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathlang_lock.h
210 lines (165 loc) · 5.96 KB
/
lang_lock.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
/* Copyright 2000-2001 ActiveState
*/
/* #define NO_PERL_LOCK /**/
/* #define LOCK_DEBUG /**/
#if !defined(NO_PERL_LOCK) && defined(WITH_THREAD)
#define DO_THREAD /* a simpler macro to test for in files that include this */
#endif
#ifdef LOCK_DEBUG
#ifdef DO_THREAD
/* XXX these test are not valid in a multi-thread environment.
* Should test if the current thread has the given lock.
*/
#error "LOCK_DEBUG with real threads doesn't work"
extern int perl_lock_held();
#define PERL_LOCK_HELD perl_lock_held()
#define PYTHON_LOCK_HELD (last_py_tstate == NULL)
#else /* DO_THREAD */
#define PERL_LOCK_HELD perl_lock_held
#define PYTHON_LOCK_HELD python_lock_held
#endif /* DO_THREAD */
extern void Lock_Fatal_Error(const char*, const char*, int);
#define LOCK_FATAL_ERROR(msg) Lock_Fatal_Error(msg, __FILE__, __LINE__)
/* Poor mans Eiffel :-) */
#define ASSERT_LOCK_PERL \
do { \
if (!PERL_LOCK_HELD || PYTHON_LOCK_HELD) \
LOCK_FATAL_ERROR("Only perl lock should be held"); \
} while (0)
#define ASSERT_LOCK_PYTHON \
do { \
if (PERL_LOCK_HELD || !PYTHON_LOCK_HELD) \
LOCK_FATAL_ERROR("Only python lock should be held"); \
} while (0)
#define ASSERT_LOCK_BOTH \
do { \
if (!PERL_LOCK_HELD || !PYTHON_LOCK_HELD) \
LOCK_FATAL_ERROR("Both perl and python lock should be held"); \
} while (0)
#else /* LOCK_DEBUG */
/* All assertions expand to nothing */
#define ASSERT_LOCK_PERL
#define ASSERT_LOCK_PYTHON
#define ASSERT_LOCK_BOTH
extern void lang_lock_init(void);
#endif /* LOCK_DEBUG */
#ifdef DO_THREAD
#ifdef MULTI_PERL
extern perl_key last_py_tstate;
/* These assume that the thread specific 'ctx' pointer is
* in scope.
*/
#define ENTER_PERL do { ctx->last_py_state = PyEval_SaveThread(); \
} while (0)
#define ENTER_PYTHON do { PyEval_RestoreThread(ctx->last_py_state); \
} while (0)
#define PERL_LOCK /* nothing */
#define PERL_UNLOCK /* nothing */
#define PYTHON_LOCK ENTER_PYTHON
#define PYTHON_UNLOCK ENTER_PERL
#else /* MULTI_PERL */
/* This stuff is similar to the threading macros you find in _tkinter.c,
* but not exactly the same :-)
*/
#include "pythread.h"
extern PyThread_type_lock perl_lock;
extern PyThreadState *last_py_tstate;
/* The locking rules are:
1) All code should be protected by one (or more) locks.
2) When we execute python code we should have the python lock and
*not* have the perl lock
3) When we execute perl code we should have the perl lock and *not*
the python lock
4) You can have both locks while executing API functions that does
not risk invoking arbitrary perl/python code.
5) Lot of API functions might invoke perl/python callbacks through
hooks, overloading or tying. These must only be invoked with
the correct single lock.
*/
/* This one should only be called when you are sure you have the python lock,
and not the perl lock. The result is that you have perl lock.
*/
#define ENTER_PERL do { PyThreadState *tstate; \
ASSERT_LOCK_PYTHON; \
tstate = PyEval_SaveThread(); \
PyThread_acquire_lock(perl_lock, 1); \
last_py_tstate = tstate; \
} while (0)
/* This one should only be called when you are sure you have the perl lock,
and not the python lock. The result is that you have the python lock.
*/
#define ENTER_PYTHON do { PyThreadState *tstate; \
ASSERT_LOCK_PERL; \
tstate = last_py_tstate; \
last_py_tstate = NULL; \
PyThread_release_lock(perl_lock); \
PyEval_RestoreThread(tstate); \
} while (0)
/* These can only be called while you have the python lock */
#define PERL_LOCK do { ASSERT_LOCK_PYTHON; \
while (!PyThread_acquire_lock(perl_lock, 0)) { \
ENTER_PERL; \
ENTER_PYTHON; \
} \
} while (0)
#define PERL_UNLOCK do { ASSERT_LOCK_BOTH; \
PyThread_release_lock(perl_lock); \
} while (0)
/* These can only be called while you have the perl lock */
#define PYTHON_LOCK do { ENTER_PYTHON; PERL_LOCK; } while (0)
#define PYTHON_UNLOCK \
do { ASSERT_LOCK_BOTH; \
if (last_py_tstate != NULL) \
Py_FatalError("PYTHON_UNLOCK: non-NULL tstate"); \
last_py_tstate = PyEval_SaveThread(); \
} while (0)
/* The reason PYTHON_LOCK is defined as it is, is that we should always
* obtain locks in the same sequence in order to avoid potential deadlock.
*/
#endif /* MULTI_PERL */
#else /* DO_THREAD */
#ifdef LOCK_DEBUG
extern int perl_lock_held;
extern int python_lock_held;
#define ENTER_PERL \
do { \
ASSERT_LOCK_PYTHON; \
python_lock_held = 0; \
perl_lock_held = 1; \
} while (0) \
#define ENTER_PYTHON \
do { \
ASSERT_LOCK_PERL; \
python_lock_held = 1; \
perl_lock_held = 0; \
} while (0)
#define PERL_LOCK \
do { \
ASSERT_LOCK_PYTHON; \
perl_lock_held = 1; \
} while (0)
#define PERL_UNLOCK \
do { \
ASSERT_LOCK_BOTH; \
perl_lock_held = 0; \
} while (0)
#define PYTHON_LOCK \
do { \
ASSERT_LOCK_PERL; \
python_lock_held = 1; \
} while (0)
#define PYTHON_UNLOCK \
do { \
ASSERT_LOCK_BOTH; \
python_lock_held = 0; \
} while (0)
#else /* LOCK_DEBUG */
/* No threads and no debug, all macros expand to nothing */
#define ENTER_PERL
#define ENTER_PYTHON
#define PERL_LOCK
#define PERL_UNLOCK
#define PYTHON_UNLOCK
#define PYTHON_LOCK
#endif /* LOCK_DEBUG */
#endif /* DO_THREAD */