1

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?
georgexsh
  • 15,984
  • 2
  • 37
  • 62
Noctis Skytower
  • 21,433
  • 16
  • 79
  • 117
  • Why would you involve ctypes or subclasses at all? If you're going to access the implementation details of `threading._RLock`, why not just access the object's `_count` attribute directly as `whatever_rlock._count`? – user2357112 Nov 27 '17 at 20:58
  • Are you under the impression that the underscore actually does something? It's just an underscore. There's no `private` or `protected` access in Python. You don't need to go through some `klass.getDeclaredField('_count').getInt(lock)` reflection API to bypass access control like in Java, because there just isn't any access control. It's still a bad idea to poke other libraries' implementation details, though. – user2357112 Nov 27 '17 at 21:03
  • @user2357112 `_thread.RLock` is implemented in C and does not expose its `count` attribute and would probably need to be accessed via `ctypes` if at all. A subclass could provide an orderly API for getting the data. `threading._RLock._count` is accessible in Python since it is implemented in Python, but the implementation in C does not easily allow such an option. – Noctis Skytower Nov 27 '17 at 21:13
  • Ah, different class. The fact that you call it `_thread.RLock._count`, as if there actually is a `_count` attribute, is confusing. – user2357112 Nov 27 '17 at 21:15
  • @user2357112 Hopefully, the adjusted title helps to clarify the question. – Noctis Skytower Nov 27 '17 at 21:27

1 Answers1

2

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.

georgexsh
  • 15,984
  • 2
  • 37
  • 62