From 72101f89279c577fc244f369ce8b415bc9f3530e Mon Sep 17 00:00:00 2001 From: Andrew Whitehead Date: Wed, 22 Jan 2025 13:46:01 -0800 Subject: [PATCH 1/2] handle race condition when loading library from multiple threads Signed-off-by: Andrew Whitehead --- wrappers/python/aries_askar/bindings/lib.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/wrappers/python/aries_askar/bindings/lib.py b/wrappers/python/aries_askar/bindings/lib.py index 145d3e4c..ab87cdb8 100644 --- a/wrappers/python/aries_askar/bindings/lib.py +++ b/wrappers/python/aries_askar/bindings/lib.py @@ -340,6 +340,7 @@ def __new__(cls, *args): inst = cls.INSTANCE and cls.INSTANCE() if inst is None: inst = super().__new__(cls, *args) + inst._initlock = threading.Lock() inst._lib = None inst._objs = [] # Keep a weak reference to the instance. This assumes that @@ -351,10 +352,12 @@ def __new__(cls, *args): @property def loaded(self) -> LibLoad: - """Determine if the library has been loaded.""" + """Access the loaded library instance, initializing it if necessary.""" if not self._lib: - self._lib = LibLoad(self.__class__.LIB_NAME) - self._objs.append(self._lib) + with self._initlock: + if not self._lib: + self._lib = LibLoad(self.__class__.LIB_NAME) + self._objs.append(self._lib) return self._lib def invoke(self, name, argtypes, *args): From 246640382a92e31b990395e071426f04d978003c Mon Sep 17 00:00:00 2001 From: Andrew Whitehead Date: Wed, 22 Jan 2025 13:46:48 -0800 Subject: [PATCH 2/2] use built-in cache instead of cached_property Signed-off-by: Andrew Whitehead --- wrappers/python/aries_askar/store.py | 31 ++++++++++++++++++---------- wrappers/python/requirements.txt | 1 - 2 files changed, 20 insertions(+), 12 deletions(-) diff --git a/wrappers/python/aries_askar/store.py b/wrappers/python/aries_askar/store.py index 6944db8c..44409abc 100644 --- a/wrappers/python/aries_askar/store.py +++ b/wrappers/python/aries_askar/store.py @@ -5,10 +5,9 @@ except ImportError: import json +from functools import lru_cache from typing import Optional, Sequence, Union -from cached_property import cached_property - from . import bindings from .bindings import ( EntryListHandle, @@ -32,22 +31,26 @@ def __init__(self, lst: EntryListHandle, pos: int): self._list = lst self._pos = pos - @cached_property + @property + @lru_cache(maxsize=None) def category(self) -> str: """Accessor for the entry category.""" return self._list.get_category(self._pos) - @cached_property + @property + @lru_cache(maxsize=None) def name(self) -> str: """Accessor for the entry name.""" return self._list.get_name(self._pos) @property + @lru_cache(maxsize=None) def value(self) -> bytes: """Accessor for the entry value.""" return bytes(self.raw_value) - @cached_property + @property + @lru_cache(maxsize=None) def raw_value(self) -> memoryview: """Accessor for the entry raw value.""" return self._list.get_value(self._pos) @@ -57,7 +60,8 @@ def value_json(self) -> dict: """Accessor for the entry value as JSON.""" return json.loads(self.value) - @cached_property + @property + @lru_cache(maxsize=None) def tags(self) -> dict: """Accessor for the entry tags.""" return self._list.get_tags(self._pos) @@ -147,27 +151,32 @@ def __init__(self, lst: KeyEntryListHandle, pos: int): self._list = lst self._pos = pos - @cached_property + @property + @lru_cache(maxsize=None) def algorithm(self) -> str: """Accessor for the key entry algorithm.""" return self._list.get_algorithm(self._pos) - @cached_property + @property + @lru_cache(maxsize=None) def name(self) -> str: """Accessor for the key entry name.""" return self._list.get_name(self._pos) - @cached_property + @property + @lru_cache(maxsize=None) def metadata(self) -> str: """Accessor for the key entry metadata.""" return self._list.get_metadata(self._pos) - @cached_property + @property + @lru_cache(maxsize=None) def key(self) -> Key: """Accessor for the entry metadata.""" return Key(self._list.load_key(self._pos)) - @cached_property + @property + @lru_cache(maxsize=None) def tags(self) -> dict: """Accessor for the entry tags.""" return self._list.get_tags(self._pos) diff --git a/wrappers/python/requirements.txt b/wrappers/python/requirements.txt index ba29938d..e69de29b 100644 --- a/wrappers/python/requirements.txt +++ b/wrappers/python/requirements.txt @@ -1 +0,0 @@ -cached_property~=2.0.0