7
7
#include < mutex>
8
8
9
9
// ----------------------------------------------------------------------------
10
+ // Lock class
11
+ // ----------------------------------------------------------------------------
12
+
10
13
typedef struct lock
11
14
{
12
15
PyObject_HEAD
@@ -15,26 +18,13 @@ typedef struct lock
15
18
_locked = 0 ;
16
19
17
20
std::unique_ptr<std::timed_mutex> _mutex = nullptr ;
18
- std::unique_ptr<std::recursive_timed_mutex> _rmutex = nullptr ;
19
21
} Lock;
20
22
21
23
// ----------------------------------------------------------------------------
22
24
static int
23
25
Lock_init (Lock* self, PyObject* args, PyObject* kwargs)
24
26
{
25
- // Get the reentrant argument
26
- static const char * kwlist[] = { " reentrant" , NULL };
27
- PyObject* reentrant = NULL ;
28
- if (!PyArg_ParseTupleAndKeywords (args, kwargs, " |O" , (char **)kwlist, &reentrant)) {
29
- return -1 ;
30
- }
31
-
32
- // if reentrant was requested use a recursive mutex
33
- if (PyObject_IsTrue (reentrant)) {
34
- self->_rmutex = std::make_unique<std::recursive_timed_mutex>();
35
- } else {
36
- self->_mutex = std::make_unique<std::timed_mutex>();
37
- }
27
+ self->_mutex = std::make_unique<std::timed_mutex>();
38
28
39
29
return 0 ;
40
30
}
@@ -44,7 +34,6 @@ static void
44
34
Lock_dealloc (Lock* self)
45
35
{
46
36
self->_mutex = nullptr ;
47
- self->_rmutex = nullptr ;
48
37
49
38
Py_TYPE (self)->tp_free ((PyObject*)self);
50
39
}
@@ -60,8 +49,12 @@ Lock_acquire(Lock* self, PyObject* args, PyObject* kwargs)
60
49
return NULL ;
61
50
}
62
51
63
- double timeout_value = 0.0 ;
64
- if (timeout != Py_None) {
52
+ if (timeout == Py_None) {
53
+ AllowThreads _;
54
+
55
+ self->_mutex ->lock ();
56
+ } else {
57
+ double timeout_value = 0.0 ;
65
58
if (PyFloat_Check (timeout)) {
66
59
timeout_value = PyFloat_AsDouble (timeout);
67
60
} else if (PyLong_Check (timeout)) {
@@ -70,32 +63,16 @@ Lock_acquire(Lock* self, PyObject* args, PyObject* kwargs)
70
63
PyErr_SetString (PyExc_TypeError, " timeout must be a float or an int" );
71
64
return NULL ;
72
65
}
73
- }
74
66
75
- if (self->_mutex != nullptr ) {
76
67
AllowThreads _;
77
68
78
- if (timeout == Py_None) {
79
-
80
- self->_mutex ->lock ();
81
- } else if (!self->_mutex ->try_lock_for (std::chrono::milliseconds ((long long )(timeout_value * 1000 )))) {
82
- Py_RETURN_FALSE;
83
- }
84
- self->_locked = 1 ;
85
- } else if (self->_rmutex != nullptr ) {
86
- AllowThreads _;
87
-
88
- if (timeout == Py_None) {
89
- self->_rmutex ->lock ();
90
- } else if (!self->_rmutex ->try_lock_for (std::chrono::milliseconds ((long long )(timeout_value * 1000 )))) {
69
+ if (!self->_mutex ->try_lock_for (std::chrono::milliseconds ((long long )(timeout_value * 1000 )))) {
91
70
Py_RETURN_FALSE;
92
71
}
93
- self->_locked ++;
94
- } else {
95
- PyErr_SetString (PyExc_RuntimeError, " Lock not initialized" );
96
- return NULL ;
97
72
}
98
73
74
+ self->_locked = 1 ;
75
+
99
76
Py_RETURN_TRUE;
100
77
}
101
78
@@ -108,16 +85,8 @@ Lock_release(Lock* self)
108
85
return NULL ;
109
86
}
110
87
111
- if (self->_mutex != nullptr ) {
112
- self->_mutex ->unlock ();
113
- self->_locked = 0 ; // Reset the lock state
114
- } else if (self->_rmutex != nullptr ) {
115
- self->_rmutex ->unlock ();
116
- self->_locked --; // Decrement the lock count for reentrant locks
117
- } else {
118
- PyErr_SetString (PyExc_RuntimeError, " Lock not initialized" );
119
- return NULL ;
120
- }
88
+ self->_mutex ->unlock ();
89
+ self->_locked = 0 ; // Reset the lock state
121
90
122
91
Py_RETURN_NONE;
123
92
}
@@ -133,11 +102,39 @@ Lock_locked(Lock* self)
133
102
Py_RETURN_FALSE;
134
103
}
135
104
105
+ // ----------------------------------------------------------------------------
106
+ static PyObject*
107
+ Lock_enter (Lock* self, PyObject* args, PyObject* kwargs)
108
+ {
109
+
110
+ AllowThreads _;
111
+
112
+ self->_mutex ->lock ();
113
+
114
+ self->_locked = 1 ;
115
+
116
+ Py_RETURN_NONE;
117
+ }
118
+
119
+ // ----------------------------------------------------------------------------
120
+ static PyObject*
121
+ Lock_exit (Lock* self, PyObject* args, PyObject* kwargs)
122
+ {
123
+ // This method is called when the lock is used in a "with" statement
124
+ if (Lock_release (self) == NULL ) {
125
+ return NULL ; // Propagate any error from release
126
+ }
127
+
128
+ return Py_False;
129
+ }
130
+
136
131
// ----------------------------------------------------------------------------
137
132
static PyMethodDef Lock_methods[] = {
138
133
{ " acquire" , (PyCFunction)Lock_acquire, METH_VARARGS | METH_KEYWORDS, " Acquire the lock with an optional timeout" },
139
134
{ " release" , (PyCFunction)Lock_release, METH_NOARGS, " Release the lock" },
140
135
{ " locked" , (PyCFunction)Lock_locked, METH_NOARGS, " Return whether the lock is acquired" },
136
+ { " __enter__" , (PyCFunction)Lock_enter, METH_NOARGS, " Enter the lock context" },
137
+ { " __exit__" , (PyCFunction)Lock_exit, METH_VARARGS | METH_KEYWORDS, " Exit the lock context" },
141
138
{ NULL } /* Sentinel */
142
139
};
143
140
@@ -159,3 +156,156 @@ static PyTypeObject LockType = {
159
156
.tp_init = (initproc)Lock_init,
160
157
.tp_new = PyType_GenericNew,
161
158
};
159
+
160
+ // ----------------------------------------------------------------------------
161
+ // RLock class
162
+ // ----------------------------------------------------------------------------
163
+
164
+ typedef struct rlock
165
+ {
166
+ PyObject_HEAD
167
+
168
+ std::atomic<int >
169
+ _locked = 0 ;
170
+
171
+ std::unique_ptr<std::recursive_timed_mutex> _mutex = nullptr ;
172
+ } RLock;
173
+
174
+ // ----------------------------------------------------------------------------
175
+ static int
176
+ RLock_init (RLock* self, PyObject* args, PyObject* kwargs)
177
+ {
178
+ self->_mutex = std::make_unique<std::recursive_timed_mutex>();
179
+
180
+ return 0 ;
181
+ }
182
+
183
+ // ----------------------------------------------------------------------------
184
+ static void
185
+ RLock_dealloc (RLock* self)
186
+ {
187
+ self->_mutex = nullptr ;
188
+
189
+ Py_TYPE (self)->tp_free ((PyObject*)self);
190
+ }
191
+
192
+ // ----------------------------------------------------------------------------
193
+ static PyObject*
194
+ RLock_acquire (RLock* self, PyObject* args, PyObject* kwargs)
195
+ {
196
+ // Get timeout argument
197
+ static const char * kwlist[] = { " timeout" , NULL };
198
+ PyObject* timeout = Py_None;
199
+ if (!PyArg_ParseTupleAndKeywords (args, kwargs, " |O" , (char **)kwlist, &timeout)) {
200
+ return NULL ;
201
+ }
202
+
203
+ if (timeout == Py_None) {
204
+ AllowThreads _;
205
+
206
+ self->_mutex ->lock ();
207
+ } else {
208
+ double timeout_value = 0.0 ;
209
+ if (PyFloat_Check (timeout)) {
210
+ timeout_value = PyFloat_AsDouble (timeout);
211
+ } else if (PyLong_Check (timeout)) {
212
+ timeout_value = PyLong_AsDouble (timeout);
213
+ } else {
214
+ PyErr_SetString (PyExc_TypeError, " timeout must be a float or an int" );
215
+ return NULL ;
216
+ }
217
+
218
+ AllowThreads _;
219
+
220
+ if (!self->_mutex ->try_lock_for (std::chrono::milliseconds ((long long )(timeout_value * 1000 )))) {
221
+ Py_RETURN_FALSE;
222
+ }
223
+ }
224
+
225
+ self->_locked ++;
226
+
227
+ Py_RETURN_TRUE;
228
+ }
229
+
230
+ // ----------------------------------------------------------------------------
231
+ static PyObject*
232
+ RLock_release (RLock* self)
233
+ {
234
+ if (self->_locked <= 0 ) {
235
+ PyErr_SetString (PyExc_RuntimeError, " Lock is not acquired" );
236
+ return NULL ;
237
+ }
238
+
239
+ self->_mutex ->unlock ();
240
+ self->_locked --;
241
+
242
+ Py_RETURN_NONE;
243
+ }
244
+
245
+ // ----------------------------------------------------------------------------
246
+ static PyObject*
247
+ RLock_locked (RLock* self)
248
+ {
249
+ if (self->_locked > 0 ) {
250
+ Py_RETURN_TRUE;
251
+ }
252
+
253
+ Py_RETURN_FALSE;
254
+ }
255
+
256
+ // ----------------------------------------------------------------------------
257
+ static PyObject*
258
+ RLock_enter (RLock* self, PyObject* args, PyObject* kwargs)
259
+ {
260
+ AllowThreads _;
261
+
262
+ self->_mutex ->lock ();
263
+
264
+ self->_locked ++;
265
+
266
+ Py_RETURN_NONE;
267
+ }
268
+
269
+ // ----------------------------------------------------------------------------
270
+ static PyObject*
271
+ RLock_exit (RLock* self, PyObject* args, PyObject* kwargs)
272
+ {
273
+ // This method is called when the lock is used in a "with" statement
274
+ if (RLock_release (self) == NULL ) {
275
+ return NULL ; // Propagate any error from release
276
+ }
277
+
278
+ return Py_False;
279
+ }
280
+
281
+ // ----------------------------------------------------------------------------
282
+ static PyMethodDef RLock_methods[] = {
283
+ { " acquire" ,
284
+ (PyCFunction)RLock_acquire,
285
+ METH_VARARGS | METH_KEYWORDS,
286
+ " Acquire the lock with an optional timeout" },
287
+ { " release" , (PyCFunction)RLock_release, METH_NOARGS, " Release the lock" },
288
+ { " locked" , (PyCFunction)RLock_locked, METH_NOARGS, " Return whether the lock is acquired at least once" },
289
+ { " __enter__" , (PyCFunction)RLock_enter, METH_NOARGS, " Enter the lock context" },
290
+ { " __exit__" , (PyCFunction)RLock_exit, METH_VARARGS | METH_KEYWORDS, " Exit the lock context" },
291
+ { NULL } /* Sentinel */
292
+ };
293
+
294
+ // ----------------------------------------------------------------------------
295
+ static PyMemberDef RLock_members[] = {
296
+ { NULL } /* Sentinel */
297
+ };
298
+
299
+ // ----------------------------------------------------------------------------
300
+ static PyTypeObject RLockType = {
301
+ .ob_base = PyVarObject_HEAD_INIT (NULL , 0 ).tp_name = " ddtrace.internal._threads.RLock" ,
302
+ .tp_basicsize = sizeof (RLock),
303
+ .tp_itemsize = 0 ,
304
+ .tp_dealloc = (destructor)RLock_dealloc,
305
+ .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
306
+ .tp_doc = PyDoc_STR (" Native re-entrant lock implementation" ),
307
+ .tp_methods = RLock_methods,
308
+ .tp_members = RLock_members,
309
+ .tp_init = (initproc)RLock_init,
310
+ .tp_new = PyType_GenericNew,
311
+ };
0 commit comments