Search code examples
pythonmultithreadingpython-3.xctypesrecursive-mutex

Is it possible to conveniently access the count of _thread.RLock via ctypes?


It is possible to create a count property for threading._RLock._count by inheriting from the class and exposing the data from the underlying attribute. This is easily demonstrated by example:

import threading


# noinspection PyProtectedMember
class RLock(threading._RLock):
    """RLock() -> RLock instance with count property"""

    @property
    def count(self):
        """Count property showing current level of lock ownership."""
        return self._count
  1. Is it possible to do the same with the _thread.RLock by getting the count via ctypes?
  2. If it is possible, would the code have any advantages over the version shown above?
  3. If it would be advantageous, what code would one have to write to access the count?

Solution

  • Is it possible to do the same with the _thread.RLock by getting the count via ctypes?

    Yes, it is possible, as rlockobject strunct definition is given:

    import ctypes, _thread
    
    class RLock(_thread.RLock):
    
        offsetof_rlock_count = 32 # on 64-bit system
    
        @property
        def count(self):
            rlock_count_b = ctypes.string_at(id(self)+self.offsetof_rlock_count, 8)
            return int.from_bytes(rlock_count_b, 'little', signed=False)
    
    rlock = RLock()
    with rlock:
        with rlock:
            print(rlock.count)
    

    yields:

    2
    

    or a more formal version:

    class S_rlockobject(ctypes.Structure):
    
        _fields_ = [
            ('ob_refcnt', ctypes.c_ssize_t),
            ('ob_type', ctypes.c_void_p),
            ('rlock_lock', ctypes.c_void_p),
            ('rlock_owner', ctypes.c_long),
            ('rlock_count', ctypes.c_ulong),
            ('in_weakreflist', ctypes.c_void_p),
        ]
    
    class RLock(_thread.RLock):
    
        def __init__(self):
            super().__init__()
            self._s = S_rlockobject.from_address(id(self))
    
        @property
        def count(self):
            return self._s.rlock_count
    

    If it is possible, would the code have any advantages over the version shown above? If it would be advantageous, what code would one have to write to access the count?

    Both methods utilize non-public API, it is hard to tell which is better, but I feel the inheriting pure python RLock implementation is simpler. The performance difference is neglectable here.