diff --git a/dbzero/dbzero/dbzero.py b/dbzero/dbzero/dbzero.py index c9e4f4dc..21899e3d 100644 --- a/dbzero/dbzero/dbzero.py +++ b/dbzero/dbzero/dbzero.py @@ -10,7 +10,7 @@ def load_dynamic(name, path): def __bootstrap__(): global __bootstrap__, __loader__, __file__ - paths = [os.path.join(os.path.split(__file__)[0]), "/src/dev/build/release", "/usr/local/lib/python3/dist-packages/dbzero/"] + paths = [os.path.join(os.path.split(__file__)[0]), "/src/dev/build/debug", "/usr/local/lib/python3/dist-packages/dbzero/"] __file__ = None for path in paths: if os.path.isdir(path): diff --git a/run_memcheck.sh b/run_memcheck.sh index eaa8546a..0499e038 100755 --- a/run_memcheck.sh +++ b/run_memcheck.sh @@ -5,4 +5,5 @@ export G_DEBUG=gc-friendly #valgrind -v --tool=memcheck --leak-check=no --num-callers=250 --log-file=valgrind.log python3 -m pytest -m 'not integration_test' -m 'not stress_test' -c pytest.ini --capture=no "$@" #valgrind -v --tool=memcheck --leak-check=no --num-callers=250 --log-file=valgrind.log python3 -m pytest -m 'stress_test' -c pytest.ini --capture=no "$@" #valgrind -v --tool=memcheck --leak-check=full --num-callers=40 --log-file=valgrind.log python3 -m samples.explore --path='/src/zorch/app-data' -valgrind -v --tool=memcheck --leak-check=no --num-callers=250 --log-file=valgrind.log python3 -m pytest -m 'stress_test' -k='test_no_cache_allocator_issue' -c pytest.ini --capture=no "$@" +#valgrind -v --tool=memcheck --leak-check=no --num-callers=250 --log-file=valgrind.log python3 -m pytest -m 'stress_test' -k='test_no_cache_allocator_issue' -c pytest.ini --capture=no "$@" +valgrind -v --tool=memcheck --leak-check=no --num-callers=250 --log-file=valgrind.log python3 -m pytest -k='test_base_lock_usage_does_not_exceed_limits' -c pytest.ini --capture=no "$@" diff --git a/src/dbzero/bindings/python/shared_py_object.cpp b/src/dbzero/bindings/python/shared_py_object.cpp index be947b93..f61142b8 100644 --- a/src/dbzero/bindings/python/shared_py_object.cpp +++ b/src/dbzero/bindings/python/shared_py_object.cpp @@ -5,7 +5,7 @@ namespace db0::python { - + template void incExtRefImpl(PyObject *py_object) { // increment reference count for memo objects @@ -13,13 +13,13 @@ namespace db0::python } template - void decExtRef(PyObject *py_object) { + void decExtRefImpl(PyObject *py_object) { // decrement reference count for memo objects reinterpret_cast(py_object)->ext().removeExtRef(); } template - unsigned int getExtRefcount(PyObject *py_object, unsigned int default_count) { + unsigned int getExtRefcountImpl(PyObject *py_object, unsigned int default_count) { // return reference count for memo objects return reinterpret_cast(py_object)->ext().getExtRefs(); } @@ -36,18 +36,18 @@ namespace db0::python void decExtRef(PyObject *py_object) { if (PyMemo_Check(py_object)) { - decExtRef(py_object); + decExtRefImpl(py_object); } else if (PyMemo_Check(py_object)) { - decExtRef(py_object); + decExtRefImpl(py_object); } } unsigned int getExtRefcount(PyObject *py_object, unsigned int default_count) { if (PyMemo_Check(py_object)) { - return getExtRefcount(py_object, default_count); + return getExtRefcountImpl(py_object, default_count); } else if (PyMemo_Check(py_object)) { - return getExtRefcount(py_object, default_count); + return getExtRefcountImpl(py_object, default_count); } // for non-memo objects, return the default count diff --git a/src/dbzero/bindings/python/shared_py_object.hpp b/src/dbzero/bindings/python/shared_py_object.hpp index 8a809be8..2b5e780b 100644 --- a/src/dbzero/bindings/python/shared_py_object.hpp +++ b/src/dbzero/bindings/python/shared_py_object.hpp @@ -2,8 +2,9 @@ #include #include +#include -// extended inc-ref, handles additional ref-counter for memo objects +// Extended inc-ref, handles additional ref-counter for memo objects // must dec-ref with PyEXT_DECREF #define PyEXT_INCREF(ptr) db0::python::incExtRef(ptr) #define PyEXT_DECREF(ptr) db0::python::decExtRef(ptr) @@ -20,15 +21,17 @@ namespace db0::python { - // incRef / decRef with a special handling for memo objects void incExtRef(PyObject *); void decExtRef(PyObject *); - unsigned int getExtRefcount(PyObject *, unsigned int default_count = 0); - + unsigned int getExtRefcount(PyObject *, unsigned int default_count); + // @tparam ExtRef flag indicating if should be counted as an "external" reference template class shared_py_object { public: + using self_t = shared_py_object; + static constexpr bool hasExtRefs = ExtRef; + inline shared_py_object() = default; inline shared_py_object(T py_object, bool incref = true) : m_py_object(py_object) @@ -39,7 +42,7 @@ namespace db0::python } if constexpr (ExtRef) { PyEXT_INCREF(py_object); - } + } } } @@ -48,13 +51,15 @@ namespace db0::python shared_py_object(shared_py_object &&other) : m_py_object(other.m_py_object) { + static_assert(!ExtRef, "Member only available for non-ExtRef conversion"); + static_assert(other.hasExtRefs, "Source object must have ExtRef"); if (m_py_object) { PyEXT_DECREF(m_py_object); } other.m_py_object = nullptr; } - shared_py_object(const shared_py_object &other) + shared_py_object(const self_t &other) : m_py_object(other.m_py_object) { if (m_py_object) { @@ -64,21 +69,15 @@ namespace db0::python } } } - - shared_py_object(shared_py_object &&other) + + shared_py_object(self_t &&other) : m_py_object(other.m_py_object) { other.m_py_object = nullptr; } - inline ~shared_py_object() - { - if (m_py_object) { - if constexpr (ExtRef) { - PyEXT_DECREF(m_py_object); - } - Py_DECREF(m_py_object); - } + inline ~shared_py_object() { + this->_destruct(); } inline T get() const { @@ -113,19 +112,19 @@ namespace db0::python } m_py_object = nullptr; return result; - } + } - inline bool operator==(const shared_py_object &other) const { + inline bool operator==(const self_t &other) const { return m_py_object == other.m_py_object; } - - inline bool operator!=(const shared_py_object &other) const { + + inline bool operator!=(const self_t &other) const { return m_py_object != other.m_py_object; } - shared_py_object &operator=(const shared_py_object &other) + self_t &operator=(const self_t &other) { - this->~shared_py_object(); + this->_destruct(); m_py_object = other.m_py_object; if (m_py_object) { Py_INCREF(m_py_object); @@ -135,29 +134,34 @@ namespace db0::python } return *this; } - - shared_py_object &operator=(shared_py_object &&other) + + self_t &operator=(self_t &&other) { - this->~shared_py_object(); + this->_destruct(); m_py_object = other.m_py_object; other.m_py_object = nullptr; return *this; } void reset() + { + this->_destruct(); + m_py_object = nullptr; + } + + private: + friend class shared_py_object; + T m_py_object = nullptr; + + void _destruct() { if (m_py_object) { if constexpr (ExtRef) { PyEXT_DECREF(m_py_object); } - Py_DECREF(m_py_object); - m_py_object = nullptr; + Py_DECREF(m_py_object); } } - - private: - friend class shared_py_object; - T m_py_object = nullptr; }; // PyTypeObject specialization @@ -241,7 +245,7 @@ namespace db0::python template shared_py_object shared_py_cast(shared_py_object &&obj) { return shared_py_object(static_cast(obj.steal()), false); } - + } namespace std diff --git a/src/dbzero/core/collections/vector/v_bvector.hpp b/src/dbzero/core/collections/vector/v_bvector.hpp index e5b748d7..cc983401 100644 --- a/src/dbzero/core/collections/vector/v_bvector.hpp +++ b/src/dbzero/core/collections/vector/v_bvector.hpp @@ -1632,7 +1632,7 @@ DB0_PACKED_END return super_t::measureMembers(); } } - + template template std::size_t o_bvector::safeSizeOf(buf_t buf) diff --git a/src/dbzero/core/utils/FlagSet.hpp b/src/dbzero/core/utils/FlagSet.hpp index 0a6ec934..3f5a7a16 100644 --- a/src/dbzero/core/utils/FlagSet.hpp +++ b/src/dbzero/core/utils/FlagSet.hpp @@ -65,12 +65,6 @@ DB0_PACKED_BEGIN } } - // Value constructor - explicit FlagSet(store_t value) - // only set flags that are allowed - : m_flags(value & FlagSetLimits::all()) - {} - bool operator[](enum_t flag) const { return test(flag); } @@ -134,27 +128,27 @@ DB0_PACKED_BEGIN } FlagSet operator&(EnumT flag) const { - return FlagSet(m_flags & static_cast(flag)); + return FlagSet::fromValue(m_flags & static_cast(flag)); } FlagSet operator&(const FlagSet &other) const { - return FlagSet(m_flags & other.m_flags); + return FlagSet::fromValue(m_flags & other.m_flags); } FlagSet operator|(EnumT flag) const { - return FlagSet(m_flags | static_cast(flag)); + return FlagSet::fromValue(m_flags | static_cast(flag)); } FlagSet operator|(const FlagSet &other) const { - return FlagSet(m_flags | other.m_flags); + return FlagSet::fromValue(m_flags | other.m_flags); } FlagSet operator^(const FlagSet &other) const { - return FlagSet(m_flags ^ other); + return FlagSet::fromValue(m_flags ^ other.m_flags); } FlagSet operator~() const { - return FlagSet(~m_flags); + return FlagSet::fromValue(~m_flags); } bool operator==(const FlagSet &other) const { @@ -165,6 +159,14 @@ DB0_PACKED_BEGIN return m_flags != other.m_flags; } + void operator+=(EnumT flag) { + set(flag); + } + + void operator-=(EnumT flag) { + clear(flag); + } + /** * Unpack flags into vector of specific type (can be std::string) * @tparam T unpacked type @@ -191,11 +193,20 @@ DB0_PACKED_BEGIN * @return */ static constexpr FlagSet all() { - return FlagSet(FlagSetLimits::all()); + return FlagSet::fromValue(FlagSetLimits::all()); + } + + static FlagSet fromValue(store_t value) { + return FlagSet(value); } private: store_t m_flags = 0; + + FlagSet(store_t flags) + : m_flags(flags) + { + } }; DB0_PACKED_END diff --git a/src/dbzero/object_model/LangCache.cpp b/src/dbzero/object_model/LangCache.cpp index 25d8a8f7..0c816da0 100644 --- a/src/dbzero/object_model/LangCache.cpp +++ b/src/dbzero/object_model/LangCache.cpp @@ -52,9 +52,9 @@ namespace db0 void LangCache::add(const db0::Fixture &fixture, Address address, ObjectPtr obj) { add(getFixtureId(fixture), address, obj); } - + void LangCache::resize(std::size_t new_size) - { + { assert(new_size > m_cache.size()); auto evict_index = m_evict_hand - m_cache.begin(); auto insert_index = m_insert_hand - m_cache.begin(); @@ -63,9 +63,10 @@ namespace db0 m_evict_hand = m_cache.begin() + evict_index; m_insert_hand = m_cache.begin() + insert_index; } - + void LangCache::add(std::uint16_t fixture_id, Address address, ObjectPtr obj) { + std::unique_lock lock(m_mutex); auto uid = makeUID(fixture_id, address); std::optional slot; if (isFull()) { @@ -105,6 +106,7 @@ namespace db0 bool LangCache::erase(std::uint16_t fixture_id, Address address, bool expired_only, bool as_defunct) { + std::unique_lock lock(m_mutex); auto uid = makeUID(fixture_id, address); auto it = m_uid_to_index.find(uid); // instance not found @@ -124,25 +126,34 @@ namespace db0 // just release the pointer since Python is defunct m_cache[slot_id].second.steal(); } - m_cache[slot_id] = {}; - --m_size; + // grab object from cache / invalidate slot + auto cached_obj_ptr = std::move(m_cache[slot_id].second); + --m_size; + lock.unlock(); + // NOTE: potential object destruction outside of the lock return true; } void LangCache::clear(bool expired_only) { + std::vector to_destroy; + std::unique_lock lock(m_mutex); for (auto &item: m_cache) { // NOTE: we check for any refernces except from LangCache itself (+1) if (item.second.get() && (!expired_only || !LangToolkit::hasAnyLangRefs(item.second.get(), 1))) { m_uid_to_index.erase(item.first); - item = {}; + // grab object for destruction outside of the lock + to_destroy.push_back(std::move(item.second)); --m_size; } - } + } + lock.unlock(); + // destroy outside of the lock } void LangCache::clearDefunct() - { + { + std::unique_lock lock(m_mutex); for (auto &item: m_cache) { if (item.second.get()) { m_uid_to_index.erase(item.first); @@ -152,9 +163,10 @@ namespace db0 } } } - + LangCache::ObjectSharedExtPtr LangCache::get(std::uint16_t fixture_id, Address address) const { + std::shared_lock lock(m_mutex); auto uid = makeUID(fixture_id, address); auto it = m_uid_to_index.find(uid); if (it == m_uid_to_index.end()) { diff --git a/src/dbzero/object_model/LangCache.hpp b/src/dbzero/object_model/LangCache.hpp index 2e5fbf66..6fbe5aca 100644 --- a/src/dbzero/object_model/LangCache.hpp +++ b/src/dbzero/object_model/LangCache.hpp @@ -5,6 +5,7 @@ #include #include #include +#include namespace db0 @@ -25,7 +26,7 @@ namespace db0 LangCache(std::optional capacity = {}, std::optional step = {}); virtual ~LangCache(); - + // Add a new instance to cache // @return slot id the element was written to void add(const Fixture &, Address, ObjectPtr); @@ -63,8 +64,9 @@ namespace db0 void add(std::uint16_t fixture_id, Address, ObjectPtr); bool erase(std::uint16_t fixture_id, Address, bool expired_only = false, bool as_defunct = false); - + private: + mutable std::shared_mutex m_mutex; // UID + instance pair using CacheItem = std::pair; const std::size_t m_capacity; diff --git a/src/dbzero/object_model/class/ClassFactory.cpp b/src/dbzero/object_model/class/ClassFactory.cpp index 4b548cb2..3c2b4751 100644 --- a/src/dbzero/object_model/class/ClassFactory.cpp +++ b/src/dbzero/object_model/class/ClassFactory.cpp @@ -157,10 +157,13 @@ namespace db0::object_model if (!checkAccessType(*fixture, AccessType::READ_WRITE)) { return {}; } - + // create new Class instance bool is_singleton = LangToolkit::isSingleton(lang_type); - ClassFlags flags { is_singleton ? ClassOptions::SINGLETON : 0 }; + ClassFlags flags; + if (is_singleton) { + flags += ClassOptions::SINGLETON; + } flags.set(ClassOptions::NO_DEFAULT_TAGS, LangToolkit::isNoDefaultTags(lang_type)); flags.set(ClassOptions::IMMUTABLE, LangToolkit::isImmutable(lang_type)); auto memo_base = LangToolkit::getBaseMemoType(lang_type); diff --git a/src/dbzero/object_model/list/List.cpp b/src/dbzero/object_model/list/List.cpp index 26a3f215..7f4b3aaf 100644 --- a/src/dbzero/object_model/list/List.cpp +++ b/src/dbzero/object_model/list/List.cpp @@ -9,8 +9,9 @@ namespace db0::object_model { - GC0_Define(List) + GC0_Define(List) + template o_typed_item createListItem(db0::swine_ptr &fixture, db0::bindings::TypeId type_id, typename LangToolkit::ObjectPtr lang_value, StorageClass storage_class, FlagSet access_mode) diff --git a/src/dbzero/object_model/object/Object.cpp b/src/dbzero/object_model/object/Object.cpp index 5f0c8915..c2d6fd59 100644 --- a/src/dbzero/object_model/object/Object.cpp +++ b/src/dbzero/object_model/object/Object.cpp @@ -596,4 +596,13 @@ namespace db0::object_model return true; } + void Object::dropInstance(FixtureLock &) + { + auto unique_addr = this->getUniqueAddress(); + auto ext_refs = this->getExtRefs(); + this->~Object(); + // construct a null placeholder + new ((void*)this) Object(tag_as_dropped(), unique_addr, ext_refs); + } + } \ No newline at end of file diff --git a/src/dbzero/object_model/object/Object.hpp b/src/dbzero/object_model/object/Object.hpp index a0e36d2d..bd68763f 100644 --- a/src/dbzero/object_model/object/Object.hpp +++ b/src/dbzero/object_model/object/Object.hpp @@ -30,6 +30,9 @@ namespace db0::object_model void set(FixtureLock &, const char *field_name, ObjectPtr lang_value); void remove(FixtureLock &, const char *field_name); + // Destroys an existing instance and constructs a "null" placeholder + void dropInstance(FixtureLock &); + protected: friend super_t; diff --git a/src/dbzero/object_model/object/ObjectAnyBase.cpp b/src/dbzero/object_model/object/ObjectAnyBase.cpp index 7c846976..5481d685 100644 --- a/src/dbzero/object_model/object/ObjectAnyBase.cpp +++ b/src/dbzero/object_model/object/ObjectAnyBase.cpp @@ -5,9 +5,9 @@ namespace db0::object_model { ObjectInitializerManager InitManager::instance; - + template - ObjectAnyBase::ObjectAnyBase(UniqueAddress addr, unsigned int ext_refs) + ObjectAnyBase::ObjectAnyBase(tag_as_dropped, UniqueAddress addr, unsigned int ext_refs) : m_flags { ObjectOptions::DROPPED } , m_ext_refs(ext_refs) , m_unique_address(addr) diff --git a/src/dbzero/object_model/object/ObjectAnyBase.hpp b/src/dbzero/object_model/object/ObjectAnyBase.hpp index b2950dfd..405901b1 100644 --- a/src/dbzero/object_model/object/ObjectAnyBase.hpp +++ b/src/dbzero/object_model/object/ObjectAnyBase.hpp @@ -149,8 +149,9 @@ namespace db0::object_model } // As a dropped object - ObjectAnyBase(UniqueAddress addr, unsigned int ext_refs); - + struct tag_as_dropped {}; + ObjectAnyBase(tag_as_dropped, UniqueAddress addr, unsigned int ext_refs); + void _touch(); }; diff --git a/src/dbzero/object_model/object/ObjectImplBase.cpp b/src/dbzero/object_model/object/ObjectImplBase.cpp index 708026aa..f47715cb 100644 --- a/src/dbzero/object_model/object/ObjectImplBase.cpp +++ b/src/dbzero/object_model/object/ObjectImplBase.cpp @@ -28,8 +28,8 @@ namespace db0::object_model } template - ObjectImplBase::ObjectImplBase(UniqueAddress addr, unsigned int ext_refs) - : super_t(addr, ext_refs) + ObjectImplBase::ObjectImplBase(tag_as_dropped, UniqueAddress addr, unsigned int ext_refs) + : super_t(tag_as_dropped(), addr, ext_refs) { } @@ -96,17 +96,7 @@ namespace db0::object_model InitManager::instance.tryCloseInitializer(*this); } } - - template - void ObjectImplBase::dropInstance(FixtureLock &) - { - auto unique_addr = this->getUniqueAddress(); - auto ext_refs = this->getExtRefs(); - this->~ObjectImplBase(); - // construct a null placeholder - new ((void*)this) Object(unique_addr, ext_refs); - } - + template typename ObjectImplBase::ObjectStem ObjectImplBase::tryUnloadStem(db0::swine_ptr &fixture, Address address, std::uint16_t instance_id, AccessFlags access_mode) diff --git a/src/dbzero/object_model/object/ObjectImplBase.hpp b/src/dbzero/object_model/object/ObjectImplBase.hpp index e6aa28b7..b406d94b 100644 --- a/src/dbzero/object_model/object/ObjectImplBase.hpp +++ b/src/dbzero/object_model/object/ObjectImplBase.hpp @@ -44,9 +44,10 @@ namespace db0::object_model using TypeManager = typename LangToolkit::TypeManager; using ObjectStem = ObjectVType; using TypeInitializer = ObjectInitializer::TypeInitializer; + using tag_as_dropped = typename super_t::tag_as_dropped; // Construct as null / dropped object - ObjectImplBase(UniqueAddress, unsigned int ext_refs); + ObjectImplBase(tag_as_dropped, UniqueAddress, unsigned int ext_refs); ObjectImplBase(const ObjectImplBase &) = delete; ObjectImplBase(ObjectImplBase &&) = delete; @@ -76,10 +77,7 @@ namespace db0::object_model // post-init invoked by memo type directly after __init__ void postInit(FixtureLock &); - - // Destroys an existing instance and constructs a "null" placeholder - void dropInstance(FixtureLock &); - + // Unload the object stem, to retrieve its type static ObjectStem tryUnloadStem(db0::swine_ptr &, Address, std::uint16_t instance_id = 0, AccessFlags = {}); @@ -88,7 +86,7 @@ namespace db0::object_model // Called to finalize adding members void endInit(); - + // Assign field of an uninitialized instance (assumed as a non-mutating operation) // NOTE: if lang_value is nullptr then the member is removed void setPreInit(const char *field_name, ObjectPtr lang_value) const; @@ -154,7 +152,7 @@ namespace db0::object_model std::pair tryGetMemberAt(std::pair, std::pair &) const; FieldID tryGetMember(const char *field_name, std::pair &, bool &is_init_var) const; - + // Try resolving field ID of an existing (or deleted) member and also its storage location // @param pos the member's position in the containing collection // @return FieldID + containing collection (e.g. pos_vt()) diff --git a/src/dbzero/workspace/FixtureThreads.cpp b/src/dbzero/workspace/FixtureThreads.cpp index 86096c0b..8fec56b8 100644 --- a/src/dbzero/workspace/FixtureThreads.cpp +++ b/src/dbzero/workspace/FixtureThreads.cpp @@ -8,6 +8,7 @@ namespace db0 { + class FixtureThreadCallbacksContext : public FixtureThreadContextBase { public: @@ -30,9 +31,7 @@ namespace db0 private: StateReachedCallbackList m_callbacks; }; - - - + FixtureThread::FixtureThread(std::uint64_t interval_ms) : m_interval_ms(interval_ms) {