Skip to content

Added entity instances caching. #291

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Nov 26, 2019
Prev Previous commit
Moved the "cache" property back to the caching class so that it can b…
…e accessed directly from the class, instead of an instance. Added a note about it into Entity's docstring.

Fixed the entity deletion listeners never being unregistered for subclass from plugins that are unloaded (the callbacks themselves were holding a reference of their class, preventing their garbage collection. Could use a proxy, but well; re-using our existing listener is much better anyway.).
  • Loading branch information
jordanbriere committed Nov 26, 2019
commit 52075fc2825340bcd8f64ea87f9fd55f9299037f
50 changes: 24 additions & 26 deletions addons/source-python/packages/source-python/entities/_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
# Contextlib
from contextlib import suppress
# WeakRef
from weakref import finalize
from weakref import WeakSet

# Source.Python Imports
# Core
Expand Down Expand Up @@ -69,6 +69,9 @@
# Get a dictionary to store the delays
_entity_delays = defaultdict(set)

# Get a set to store the registered entity classes
_entity_classes = WeakSet()


# =============================================================================
# >> CLASSES
Expand All @@ -81,17 +84,8 @@ def __init__(cls, classname, bases, attributes):
# New instances of this class will be cached in that dictionary
cls._cache = {}

# Listen for entity deletions for cleanup purposes
on_entity_deleted_listener_manager.register_listener(
cls._on_entity_deleted
)

# Unregister the listener when the class is being garbage collected
finalize(
cls,
on_entity_deleted_listener_manager.unregister_listener,
cls._on_entity_deleted
)
# Add the class to the registered classes
_entity_classes.add(cls)

def __call__(cls, index, caching=True):
"""Called when a new instance of this class is requested.
Expand All @@ -117,13 +111,13 @@ def __call__(cls, index, caching=True):
# We are done, let's return the instance
return obj

def _on_entity_deleted(cls, base_entity):
"""Called when an entity is deleted."""
if not base_entity.is_networked():
return
@property
def cache(cls):
"""Returns the cached instances of this class.

# Cleanup the cache
cls._cache.pop(base_entity.index, None)
:rtype: dict
"""
return cls._cache


class Entity(BaseEntity, metaclass=_EntityCaching):
Expand All @@ -138,6 +132,14 @@ class also provides dynamic attributes that depend on the entity that is
2. :attr:`inputs`
3. :attr:`outputs`
4. :attr:`keyvalues`

:var cache:
A read-only attribute that returns a dictionary containing the cached
instances of this class.

.. note::
This is not an instance property, so it can only be
accessed through the class itself.
"""

def __init__(self, index, caching=True):
Expand Down Expand Up @@ -285,14 +287,6 @@ def _obj(cls, ptr):
"""Return an entity instance of the given pointer."""
return cls(index_from_pointer(ptr))

@property
def cache(self):
"""Returns the cached instances of this class.

:rtype: dict
"""
return self._cache

@property
def index(self):
"""Return the entity's index.
Expand Down Expand Up @@ -1097,6 +1091,10 @@ def _on_entity_deleted(base_entity):
# Get the index of the entity...
index = base_entity.index

# Cleanup the cache
for cls in _entity_classes:
cls.cache.pop(index, None)

# Was no delay registered for this entity?
if index not in _entity_delays:
return
Expand Down