@@ -155,6 +155,273 @@ static PyTypeObject Locktype = {
155155 lock_methods , /*tp_methods*/
156156};
157157
158+ /* Recursive lock objects */
159+
160+ typedef struct {
161+ PyObject_HEAD
162+ PyThread_type_lock rlock_lock ;
163+ long rlock_owner ;
164+ unsigned long rlock_count ;
165+ PyObject * in_weakreflist ;
166+ } rlockobject ;
167+
168+ static void
169+ rlock_dealloc (rlockobject * self )
170+ {
171+ assert (self -> rlock_lock );
172+ if (self -> in_weakreflist != NULL )
173+ PyObject_ClearWeakRefs ((PyObject * ) self );
174+ /* Unlock the lock so it's safe to free it */
175+ if (self -> rlock_count > 0 )
176+ PyThread_release_lock (self -> rlock_lock );
177+
178+ PyThread_free_lock (self -> rlock_lock );
179+ Py_TYPE (self )-> tp_free (self );
180+ }
181+
182+ static PyObject *
183+ rlock_acquire (rlockobject * self , PyObject * args , PyObject * kwds )
184+ {
185+ char * kwlist [] = {"blocking" , NULL };
186+ int blocking = 1 ;
187+ long tid ;
188+ int r = 1 ;
189+
190+ if (!PyArg_ParseTupleAndKeywords (args , kwds , "|i:acquire" , kwlist ,
191+ & blocking ))
192+ return NULL ;
193+
194+ tid = PyThread_get_thread_ident ();
195+ if (self -> rlock_count > 0 && tid == self -> rlock_owner ) {
196+ unsigned long count = self -> rlock_count + 1 ;
197+ if (count <= self -> rlock_count ) {
198+ PyErr_SetString (PyExc_OverflowError ,
199+ "Internal lock count overflowed" );
200+ return NULL ;
201+ }
202+ self -> rlock_count = count ;
203+ Py_RETURN_TRUE ;
204+ }
205+
206+ if (self -> rlock_count > 0 ||
207+ !PyThread_acquire_lock (self -> rlock_lock , 0 )) {
208+ if (!blocking ) {
209+ Py_RETURN_FALSE ;
210+ }
211+ Py_BEGIN_ALLOW_THREADS
212+ r = PyThread_acquire_lock (self -> rlock_lock , blocking );
213+ Py_END_ALLOW_THREADS
214+ }
215+ if (r ) {
216+ assert (self -> rlock_count == 0 );
217+ self -> rlock_owner = tid ;
218+ self -> rlock_count = 1 ;
219+ }
220+
221+ return PyBool_FromLong (r );
222+ }
223+
224+ PyDoc_STRVAR (rlock_acquire_doc ,
225+ "acquire(blocking=True) -> bool\n\
226+ \n\
227+ Lock the lock. `blocking` indicates whether we should wait\n\
228+ for the lock to be available or not. If `blocking` is False\n\
229+ and another thread holds the lock, the method will return False\n\
230+ immediately. If `blocking` is True and another thread holds\n\
231+ the lock, the method will wait for the lock to be released,\n\
232+ take it and then return True.\n\
233+ (note: the blocking operation is not interruptible.)\n\
234+ \n\
235+ In all other cases, the method will return True immediately.\n\
236+ Precisely, if the current thread already holds the lock, its\n\
237+ internal counter is simply incremented. If nobody holds the lock,\n\
238+ the lock is taken and its internal counter initialized to 1." );
239+
240+ static PyObject *
241+ rlock_release (rlockobject * self )
242+ {
243+ long tid = PyThread_get_thread_ident ();
244+
245+ if (self -> rlock_count == 0 || self -> rlock_owner != tid ) {
246+ PyErr_SetString (PyExc_RuntimeError ,
247+ "cannot release un-acquired lock" );
248+ return NULL ;
249+ }
250+ if (-- self -> rlock_count == 0 ) {
251+ self -> rlock_owner = 0 ;
252+ PyThread_release_lock (self -> rlock_lock );
253+ }
254+ Py_RETURN_NONE ;
255+ }
256+
257+ PyDoc_STRVAR (rlock_release_doc ,
258+ "release()\n\
259+ \n\
260+ Release the lock, allowing another thread that is blocked waiting for\n\
261+ the lock to acquire the lock. The lock must be in the locked state,\n\
262+ and must be locked by the same thread that unlocks it; otherwise a\n\
263+ `RuntimeError` is raised.\n\
264+ \n\
265+ Do note that if the lock was acquire()d several times in a row by the\n\
266+ current thread, release() needs to be called as many times for the lock\n\
267+ to be available for other threads." );
268+
269+ static PyObject *
270+ rlock_acquire_restore (rlockobject * self , PyObject * arg )
271+ {
272+ long owner ;
273+ unsigned long count ;
274+ int r = 1 ;
275+
276+ if (!PyArg_ParseTuple (arg , "kl:_acquire_restore" , & count , & owner ))
277+ return NULL ;
278+
279+ if (!PyThread_acquire_lock (self -> rlock_lock , 0 )) {
280+ Py_BEGIN_ALLOW_THREADS
281+ r = PyThread_acquire_lock (self -> rlock_lock , 1 );
282+ Py_END_ALLOW_THREADS
283+ }
284+ if (!r ) {
285+ PyErr_SetString (ThreadError , "couldn't acquire lock" );
286+ return NULL ;
287+ }
288+ assert (self -> rlock_count == 0 );
289+ self -> rlock_owner = owner ;
290+ self -> rlock_count = count ;
291+ Py_RETURN_NONE ;
292+ }
293+
294+ PyDoc_STRVAR (rlock_acquire_restore_doc ,
295+ "_acquire_restore(state) -> None\n\
296+ \n\
297+ For internal use by `threading.Condition`." );
298+
299+ static PyObject *
300+ rlock_release_save (rlockobject * self )
301+ {
302+ long owner ;
303+ unsigned long count ;
304+
305+ owner = self -> rlock_owner ;
306+ count = self -> rlock_count ;
307+ self -> rlock_count = 0 ;
308+ self -> rlock_owner = 0 ;
309+ PyThread_release_lock (self -> rlock_lock );
310+ return Py_BuildValue ("kl" , count , owner );
311+ }
312+
313+ PyDoc_STRVAR (rlock_release_save_doc ,
314+ "_release_save() -> tuple\n\
315+ \n\
316+ For internal use by `threading.Condition`." );
317+
318+
319+ static PyObject *
320+ rlock_is_owned (rlockobject * self )
321+ {
322+ long tid = PyThread_get_thread_ident ();
323+
324+ if (self -> rlock_count > 0 && self -> rlock_owner == tid ) {
325+ Py_RETURN_TRUE ;
326+ }
327+ Py_RETURN_FALSE ;
328+ }
329+
330+ PyDoc_STRVAR (rlock_is_owned_doc ,
331+ "_is_owned() -> bool\n\
332+ \n\
333+ For internal use by `threading.Condition`." );
334+
335+ static PyObject *
336+ rlock_new (PyTypeObject * type , PyObject * args , PyObject * kwds )
337+ {
338+ rlockobject * self ;
339+
340+ self = (rlockobject * ) type -> tp_alloc (type , 0 );
341+ if (self != NULL ) {
342+ self -> rlock_lock = PyThread_allocate_lock ();
343+ if (self -> rlock_lock == NULL ) {
344+ type -> tp_free (self );
345+ PyErr_SetString (ThreadError , "can't allocate lock" );
346+ return NULL ;
347+ }
348+ self -> in_weakreflist = NULL ;
349+ self -> rlock_owner = 0 ;
350+ self -> rlock_count = 0 ;
351+ }
352+
353+ return (PyObject * ) self ;
354+ }
355+
356+ static PyObject *
357+ rlock_repr (rlockobject * self )
358+ {
359+ return PyUnicode_FromFormat ("<%s owner=%ld count=%lu>" ,
360+ Py_TYPE (self )-> tp_name , self -> rlock_owner , self -> rlock_count );
361+ }
362+
363+
364+ static PyMethodDef rlock_methods [] = {
365+ {"acquire" , (PyCFunction )rlock_acquire ,
366+ METH_VARARGS | METH_KEYWORDS , rlock_acquire_doc },
367+ {"release" , (PyCFunction )rlock_release ,
368+ METH_NOARGS , rlock_release_doc },
369+ {"_is_owned" , (PyCFunction )rlock_is_owned ,
370+ METH_NOARGS , rlock_is_owned_doc },
371+ {"_acquire_restore" , (PyCFunction )rlock_acquire_restore ,
372+ METH_O , rlock_acquire_restore_doc },
373+ {"_release_save" , (PyCFunction )rlock_release_save ,
374+ METH_NOARGS , rlock_release_save_doc },
375+ {"__enter__" , (PyCFunction )rlock_acquire ,
376+ METH_VARARGS | METH_KEYWORDS , rlock_acquire_doc },
377+ {"__exit__" , (PyCFunction )rlock_release ,
378+ METH_VARARGS , rlock_release_doc },
379+ {NULL , NULL } /* sentinel */
380+ };
381+
382+
383+ static PyTypeObject RLocktype = {
384+ PyVarObject_HEAD_INIT (& PyType_Type , 0 )
385+ "_thread.RLock" , /*tp_name*/
386+ sizeof (rlockobject ), /*tp_size*/
387+ 0 , /*tp_itemsize*/
388+ /* methods */
389+ (destructor )rlock_dealloc , /*tp_dealloc*/
390+ 0 , /*tp_print*/
391+ 0 , /*tp_getattr*/
392+ 0 , /*tp_setattr*/
393+ 0 , /*tp_reserved*/
394+ (reprfunc )rlock_repr , /*tp_repr*/
395+ 0 , /*tp_as_number*/
396+ 0 , /*tp_as_sequence*/
397+ 0 , /*tp_as_mapping*/
398+ 0 , /*tp_hash*/
399+ 0 , /*tp_call*/
400+ 0 , /*tp_str*/
401+ 0 , /*tp_getattro*/
402+ 0 , /*tp_setattro*/
403+ 0 , /*tp_as_buffer*/
404+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE , /* tp_flags */
405+ 0 , /*tp_doc*/
406+ 0 , /*tp_traverse*/
407+ 0 , /*tp_clear*/
408+ 0 , /*tp_richcompare*/
409+ offsetof(rlockobject , in_weakreflist ), /*tp_weaklistoffset*/
410+ 0 , /*tp_iter*/
411+ 0 , /*tp_iternext*/
412+ rlock_methods , /*tp_methods*/
413+ 0 , /* tp_members */
414+ 0 , /* tp_getset */
415+ 0 , /* tp_base */
416+ 0 , /* tp_dict */
417+ 0 , /* tp_descr_get */
418+ 0 , /* tp_descr_set */
419+ 0 , /* tp_dictoffset */
420+ 0 , /* tp_init */
421+ PyType_GenericAlloc , /* tp_alloc */
422+ rlock_new /* tp_new */
423+ };
424+
158425static lockobject *
159426newlockobject (void )
160427{
@@ -752,6 +1019,8 @@ PyInit__thread(void)
7521019 return NULL ;
7531020 if (PyType_Ready (& Locktype ) < 0 )
7541021 return NULL ;
1022+ if (PyType_Ready (& RLocktype ) < 0 )
1023+ return NULL ;
7551024
7561025 /* Create the module and add the functions */
7571026 m = PyModule_Create (& threadmodule );
@@ -766,6 +1035,10 @@ PyInit__thread(void)
7661035 Py_INCREF (& Locktype );
7671036 PyDict_SetItemString (d , "LockType" , (PyObject * )& Locktype );
7681037
1038+ Py_INCREF (& RLocktype );
1039+ if (PyModule_AddObject (m , "RLock" , (PyObject * )& RLocktype ) < 0 )
1040+ return NULL ;
1041+
7691042 Py_INCREF (& localtype );
7701043 if (PyModule_AddObject (m , "_local" , (PyObject * )& localtype ) < 0 )
7711044 return NULL ;
0 commit comments