Created
October 11, 2023 14:37
-
-
Save tomphp/a0dd1d97128b504f8a20674b3a43a264 to your computer and use it in GitHub Desktop.
Reference counting example
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from collections import defaultdict | |
class Memory: | |
MEMORY = {0: None} | |
@staticmethod | |
def allocate(value): | |
address = max(Memory.MEMORY.keys()) + 1 | |
Memory.MEMORY[address] = value | |
print(f"allocated at address {address}: {value}") | |
return address | |
@staticmethod | |
def deallocatte(address): | |
del Memory.MEMORY[address] | |
print(f"deallocated at address {address}") | |
class Rc: | |
REFS = defaultdict(lambda: {"count": 0, "borrowed": False}) | |
def __init__(self, address): | |
self._address = address | |
self._borrowed = False | |
self._update_count(lambda current: current + 1) | |
print(f"Rc::__init__({self._address}) / reference count: {self._get_count()}") | |
def __del__(self): | |
self._update_count(lambda current: current - 1) | |
if self._borrowed: | |
self.REFS[self._address]["borrowed"] = False | |
print(f"Rc::__del__({self._address}) / reference count: {self._get_count()}") | |
if self._get_count() < 1: | |
Memory.deallocatte(self._address) | |
def clone(self): | |
return Rc(self._address) | |
def borrow(self): | |
if Rc.REFS[self._address]["borrowed"]: | |
raise RuntimeError("Can't borrow a reference twice") | |
Rc.REFS[self._address]["borrowed"] = True | |
self._borrowed = True | |
print("performed borrow") | |
def _update_count(self, update): | |
Rc.REFS[self._address]["count"] = update(self._get_count()) | |
def _get_count(self): | |
return Rc.REFS[self._address]["count"] | |
# Demonstrates the reference counter in action. | |
# Try removing the r2 and r3 assignments (but keep the .clones()) and see how it | |
# changes the lifetime of the clones. | |
def reference_counter_example(): | |
pointer = Memory.allocate("hello world") | |
r1 = Rc(pointer) | |
r2 = r1.clone() | |
r3 = r1.clone() | |
# This demonstrates that borrow can not been called while a previously borrowed | |
# reference is still in scope | |
def borrow_twice_example(): | |
pointer = Memory.allocate("hello world") | |
r1 = Rc(pointer) | |
r2 = r1.clone() | |
r2.borrow() | |
r1.borrow() | |
# This demonstrates that borrow can be called repeatedly when the clones are not | |
# held in scope by an assignment | |
def line_scoped_borrow_twice_example(): | |
pointer = Memory.allocate("hello world") | |
r1 = Rc(pointer) | |
r1.clone().borrow() | |
r1.clone().borrow() | |
r1.clone().borrow() | |
# This demonstrates that a function can be used to limit the time a clone exists for | |
def function_scoped_borrow_twice_example(): | |
def scoped_borrow(pointer): | |
pointer.borrow() | |
pointer = Memory.allocate("hello world") | |
r1 = Rc(pointer) | |
scoped_borrow(r1.clone()) | |
r1.borrow() | |
if __name__ == "__main__": | |
# Uncomment different examples | |
#reference_counter_example() | |
#borrow_twice_example() | |
#line_scoped_borrow_twice_example() | |
#function_scoped_borrow_twice_example() | |
pass |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment