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
Next Next commit
Added entity caching.
  • Loading branch information
jordanbriere committed Nov 25, 2019
commit bb030868efc319b60c97b9583550c37231cde4ea
65 changes: 64 additions & 1 deletion addons/source-python/packages/source-python/entities/_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@
from collections import defaultdict
# Contextlib
from contextlib import suppress
# FuncTools
from functools import update_wrapper
# WeakRef
from weakref import WeakKeyDictionary

# Source.Python Imports
# Core
Expand Down Expand Up @@ -70,7 +74,62 @@
# =============================================================================
# >> CLASSES
# =============================================================================
class Entity(BaseEntity):
class EntityCaching(BaseEntity.__class__):
"""Metaclass used to cache entity instances."""

# Internal cache dictionary
# Release the cached instances if the stored classes are garbage collected
# E.g. Subclasses from plugins that are unloaded, etc.
cache = WeakKeyDictionary()

def __new__(metaclass, classname, bases, attributes, caching=True):
"""Creates a new entity class that will cache its instances."""
cls = super().__new__(
metaclass, classname, bases, attributes
)

# New instances of this class will be cached in that dictionary
if caching:
metaclass.cache[cls] = {}

def __init__(self, index):
# Does nothing, so we don't re-initialize cached instances, etc.
return
update_wrapper(__init__, cls.__init__)
cls.__init__ = __init__

def __new__(self, index):
# Let's first lookup for a cached instance
caching = self in metaclass.cache
if caching:
cache = metaclass.cache[self]
if index in cache:
return cache[index]

# Nothing in cache, let's create a new instance
obj = cls.__base__.__new__(self, index)

# Successively initialize the base classes
# This is required, because Boost's construction happens there
for base in (self,) + bases:
getattr(
base.__init__, '__wrapped__', base.__init__)(
obj, index
)

# Let's cache the new instance we just created
if caching:
cache[index] = obj

# We are done, let's return the instance
return obj
update_wrapper(__new__, cls.__new__)
cls.__new__ = __new__

return cls


class Entity(BaseEntity, metaclass=EntityCaching):
"""Class used to interact directly with entities.

Beside the standard way of doing stuff via methods and properties this
Expand Down Expand Up @@ -1031,6 +1090,10 @@ def _on_entity_deleted(base_entity):
# Get the index of the entity...
index = base_entity.index

# Cleanup the internal cache
for objects in EntityCaching.cache.values():
objects.pop(index, None)

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