diff --git a/python_tests/test_cache.py b/python_tests/test_cache.py index 6eaf540d..b2fabb72 100644 --- a/python_tests/test_cache.py +++ b/python_tests/test_cache.py @@ -47,7 +47,7 @@ def test_base_lock_usage_does_not_exceed_limits(db0_fixture): assert usage_2 - usage_1 < cache_size * 1.5 -def test_lang_cache_can_reach_capacity(db0_fixture): +def test_lang_cache_can_reach_capacity(db0_fixture): buf = db0.list() # python instances are added to lang cache until it reaches capacity initial_capacity = db0.get_lang_cache_stats()["capacity"] diff --git a/python_tests/test_memo_no_cache.py b/python_tests/test_memo_no_cache.py new file mode 100644 index 00000000..9fd60ccb --- /dev/null +++ b/python_tests/test_memo_no_cache.py @@ -0,0 +1,64 @@ +from random import random +import pytest +import dbzero as db0 +from .memo_test_types import MemoTestClass, TriColor, MemoAnyAttrs +from dataclasses import dataclass +import random +import string +import gc + + +def rand_string(max_len): + str_len = random.randint(1, max_len) + return ''.join(random.choice(string.ascii_letters) for i in range(str_len)) + + +@db0.memo(no_cache=True) +@dataclass +class MemoNoCacheClass: + data: str + + def __init__(self): + self.data = rand_string(12 << 10) + + +def test_create_memo_no_cache(db0_fixture): + obj_1 = MemoNoCacheClass() + assert obj_1.data is not None + + +def test_no_cache_instances_removed_from_lang_cache(db0_fixture): + buf = db0.list() + for _ in range(100): + obj = MemoNoCacheClass() + buf.append(obj) + + gc.collect() + # make sure objects were not cached + assert db0.get_lang_cache_stats()["size"] < 10 + + +def test_memo_no_cache_issue1(db0_fixture): + """ + Issue: test failing with RuntimeError: BDevStorage::read: page not found: 8, state: 2 + Reason: objects marked with no_cache were not being registered with DirtyCache, added no_dirty_cache flag + """ + buf = db0.list() + for _ in range(3): + obj = MemoNoCacheClass() + buf.append(obj) + del obj + + +def test_excluding_no_cache_instances_from_dbzero_cache(db0_fixture): + buf = db0.list() + initial_cache_size = db0.get_cache_stats()["size"] + for _ in range(100): + obj = MemoNoCacheClass() + buf.append(obj) + + gc.collect() + final_cache_size = db0.get_cache_stats()["size"] + # make sure cache utilization is low + assert abs(final_cache_size - initial_cache_size) < (300 << 10) + \ No newline at end of file diff --git a/src/dbzero/bindings/python/Memo.cpp b/src/dbzero/bindings/python/Memo.cpp index f5d0d6e7..ab4b8c95 100644 --- a/src/dbzero/bindings/python/Memo.cpp +++ b/src/dbzero/bindings/python/Memo.cpp @@ -210,7 +210,7 @@ namespace db0::python PyErr_Restore(ptype, pvalue, ptraceback); return -1; } - + // invoke post-init on associated dbzero object auto &object = self->modifyExt(); db0::FixtureLock fixture(object.getFixture()); @@ -219,10 +219,12 @@ namespace db0::python // need to call modifyExt again after postInit because the instance has just been created // and potentially needs to be included in the AtomicContext self->modifyExt(); - fixture->getLangCache().add(object.getAddress(), self); - - // finally, unless opted-out, assign the type tag(s) of the entire type hierarchy const Class *class_ptr = &object.getType(); + if (!class_ptr || !class_ptr->isNoCache()) { + fixture->getLangCache().add(object.getAddress(), self); + } + + // finally, unless opted-out, assign the type tag(s) of the entire type hierarchy if (class_ptr && class_ptr->assignDefaultTags()) { auto &tag_index = fixture->get(); while (class_ptr) { @@ -242,9 +244,12 @@ namespace db0::python if (memo_obj->ext().isSingleton()) { db0::FixtureLock lock(memo_obj->ext().getFixture()); memo_obj->modifyExt().unSingleton(lock); - // the acutal destroy will be performed by the GC0 once removed from the LangCache - auto &lang_cache = memo_obj->ext().getFixture()->getLangCache(); - lang_cache.erase(memo_obj->ext().getAddress()); + if (!memo_obj->ext().isNoCache()) { + // the actual destroy will be performed by the GC0 once removed from the LangCache + auto &lang_cache = memo_obj->ext().getFixture()->getLangCache(); + lang_cache.erase(memo_obj->ext().getAddress()); + } + return; } @@ -258,11 +263,14 @@ namespace db0::python // create a null placeholder in place of the original instance to mark as deleted auto &lang_cache = memo_obj->ext().getFixture()->getLangCache(); + bool no_cache = memo_obj->ext().isNoCache(); auto obj_addr = memo_obj->ext().getUniqueAddress(); db0::FixtureLock lock(memo_obj->ext().getFixture()); memo_obj->modifyExt().dropInstance(lock); // remove instance from the lang cache - lang_cache.erase(obj_addr); + if (!no_cache) { + lang_cache.erase(obj_addr); + } } PyObject *tryMemoObject_getattro(MemoObject *memo_obj, PyObject *attr) @@ -449,7 +457,7 @@ namespace db0::python PyObject *wrapPyType(PyTypeObject *base_class, bool is_singleton, bool no_default_tags, const char *prefix_name, const char *type_id, const char *file_name, std::vector &&init_vars, PyObject *py_dyn_prefix_callable, - std::vector &&migrations) + std::vector &&migrations, bool no_cache) { auto py_class = Py_BORROW(base_class); auto py_module = Py_OWN(findModule(*Py_OWN(PyObject_GetAttrString((PyObject*)*py_class, "__module__")))); @@ -473,6 +481,9 @@ namespace db0::python auto &type_manager = PyToolkit::getTypeManager(); MemoFlags type_flags = no_default_tags ? MemoFlags { MemoOptions::NO_DEFAULT_TAGS } : MemoFlags(); + if (no_cache) { + type_flags.set(MemoOptions::NO_CACHE); + } auto type_info = MemoTypeDecoration( py_module, prefix_name, @@ -488,7 +499,7 @@ namespace db0::python PyToolkit::getTypeManager().addMemoType(*new_type, type_id, std::move(type_info)); // register new type with the module where the original type was located PySafeModule_AddObject(*py_module, type_name.c_str(), new_type); - + // add class fields class member to access memo type information auto py_class_fields = Py_OWN(PyClassFields_create(*new_type)); if (PySafeDict_SetItemString((*new_type)->tp_dict, "__fields__", py_class_fields) < 0) { @@ -511,18 +522,19 @@ namespace db0::python PyObject *py_dyn_prefix = nullptr; // migrations are only processed for singleton types PyObject *py_migrations = nullptr; + PyObject *py_no_cache = nullptr; static const char *kwlist[] = { "input", "singleton", "no_default_tags", "prefix", "id", "py_file", "py_init_vars", - "py_dyn_prefix", "py_migrations", NULL }; - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|OOOOOOOO", const_cast(kwlist), &class_obj, &py_singleton, - &py_no_default_tags, &py_prefix_name, &py_type_id, &py_file_name, &py_init_vars, &py_dyn_prefix, &py_migrations)) - { - PyErr_SetString(PyExc_TypeError, "Invalid input arguments"); + "py_dyn_prefix", "py_migrations", "no_cache", NULL }; + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|OOOOOOOOO", const_cast(kwlist), &class_obj, &py_singleton, + &py_no_default_tags, &py_prefix_name, &py_type_id, &py_file_name, &py_init_vars, &py_dyn_prefix, &py_migrations, &py_no_cache)) + { return NULL; } bool is_singleton = py_singleton && PyObject_IsTrue(py_singleton); bool no_default_tags = py_no_default_tags && PyObject_IsTrue(py_no_default_tags); + bool no_cache = py_no_cache && PyObject_IsTrue(py_no_cache); const char *prefix_name = (py_prefix_name && py_prefix_name != Py_None) ? PyUnicode_AsUTF8(py_prefix_name) : nullptr; const char *type_id = py_type_id ? PyUnicode_AsUTF8(py_type_id) : nullptr; const char *file_name = (py_file_name && py_file_name != Py_None) ? PyUnicode_AsUTF8(py_file_name) : nullptr; @@ -557,7 +569,7 @@ namespace db0::python auto migrations = extractMigrations(py_migrations); return wrapPyType(castToType(class_obj), is_singleton, no_default_tags, prefix_name, type_id, file_name, - std::move(init_vars), py_dyn_prefix, std::move(migrations) + std::move(init_vars), py_dyn_prefix, std::move(migrations), no_cache ); } diff --git a/src/dbzero/bindings/python/MemoTypeDecoration.cpp b/src/dbzero/bindings/python/MemoTypeDecoration.cpp index 11c07e84..c71ab4ad 100644 --- a/src/dbzero/bindings/python/MemoTypeDecoration.cpp +++ b/src/dbzero/bindings/python/MemoTypeDecoration.cpp @@ -4,8 +4,6 @@ #include #include -DEFINE_ENUM_VALUES(db0::python::MemoOptions, "NO_DEFAULT_TAGS") - namespace db0::python { diff --git a/src/dbzero/bindings/python/MemoTypeDecoration.hpp b/src/dbzero/bindings/python/MemoTypeDecoration.hpp index 2fc7228a..ac95941f 100644 --- a/src/dbzero/bindings/python/MemoTypeDecoration.hpp +++ b/src/dbzero/bindings/python/MemoTypeDecoration.hpp @@ -12,19 +12,15 @@ #include #include #include +#include namespace db0::python { - - enum MemoOptions: std::uint16_t - { - // instances of this type opted out of auto-assigned type tags - NO_DEFAULT_TAGS = 0x0001 - }; - - using MemoFlags = db0::FlagSet; - + + using MemoOptions = db0::object_model::MemoOptions; + using MemoFlags = db0::object_model::MemoFlags; + // check for a memo type (i.e. generated by PyAPI_wrapPyClass) bool PyMemo_Check(PyObject *obj); bool PyMemoType_Check(PyTypeObject *type); @@ -119,5 +115,3 @@ namespace db0::python }; } - -DECLARE_ENUM_VALUES(db0::python::MemoOptions, 1) \ No newline at end of file diff --git a/src/dbzero/bindings/python/PyToolkit.cpp b/src/dbzero/bindings/python/PyToolkit.cpp index b704b40b..ae728407 100644 --- a/src/dbzero/bindings/python/PyToolkit.cpp +++ b/src/dbzero/bindings/python/PyToolkit.cpp @@ -128,7 +128,7 @@ namespace db0::python const ClassFactory &class_factory, TypeObjectPtr lang_type_ptr, std::uint16_t instance_id) { // try unloading from cache first - auto &lang_cache = fixture->getLangCache(); + auto &lang_cache = fixture->getLangCache(); auto obj_ptr = tryUnloadObjectFromCache(lang_cache, address, nullptr); if (obj_ptr.get()) { @@ -173,7 +173,9 @@ namespace db0::python memo_ptr->unload(fixture, std::move(stem), type, Object::with_type_hint{}); // NOTE: Py_OWN only possible with a proper object obj_ptr = Py_OWN((PyObject*)memo_ptr); - lang_cache.add(address, obj_ptr.get()); + if (!memo_ptr->ext().isNoCache()) { + lang_cache.add(address, obj_ptr.get()); + } return obj_ptr; } @@ -205,7 +207,9 @@ namespace db0::python memo_ptr->unload(fixture, address, type, Object::with_type_hint{}); // NOTE: Py_OWN only possible with a proper object obj_ptr = Py_OWN((PyObject*)memo_ptr); - lang_cache.add(address, obj_ptr.get()); + if (!memo_ptr->ext().isNoCache()) { + lang_cache.add(address, obj_ptr.get()); + } return obj_ptr; } @@ -521,6 +525,24 @@ namespace db0::python } } + bool PyToolkit::isNoCache(TypeObjectPtr py_type) + { + if (isMemoType(py_type)) { + return MemoTypeDecoration::get(py_type).getFlags()[MemoOptions::NO_CACHE]; + } else { + return false; + } + } + + FlagSet PyToolkit::getMemoFlags(TypeObjectPtr py_type) + { + if (isMemoType(py_type)) { + return MemoTypeDecoration::get(py_type).getFlags(); + } else { + return {}; + } + } + const char *PyToolkit::getPrefixName(TypeObjectPtr memo_type) { assert(isMemoType(memo_type)); diff --git a/src/dbzero/bindings/python/PyToolkit.hpp b/src/dbzero/bindings/python/PyToolkit.hpp index 29f6a136..644a6b0e 100644 --- a/src/dbzero/bindings/python/PyToolkit.hpp +++ b/src/dbzero/bindings/python/PyToolkit.hpp @@ -192,9 +192,11 @@ namespace db0::python static const char *getMemoTypeID(TypeObjectPtr memo_type); static const std::vector &getInitVars(TypeObjectPtr memo_type); - static bool isSingleton(TypeObjectPtr py_type); + static bool isSingleton(TypeObjectPtr); // check if a memo type is marked with no_default_tags flag - static bool isNoDefaultTags(TypeObjectPtr py_type); + static bool isNoDefaultTags(TypeObjectPtr); + static bool isNoCache(TypeObjectPtr); + static FlagSet getMemoFlags(TypeObjectPtr); inline static void incRef(ObjectPtr py_object) { Py_INCREF(py_object); diff --git a/src/dbzero/bindings/python/collections/PyDict.cpp b/src/dbzero/bindings/python/collections/PyDict.cpp index b4392d62..8ab57f29 100644 --- a/src/dbzero/bindings/python/collections/PyDict.cpp +++ b/src/dbzero/bindings/python/collections/PyDict.cpp @@ -357,11 +357,12 @@ namespace db0::python return runSafe(tryDictObject_update, dict_object, args, kwargs); } - shared_py_object tryMake_DB0Dict(db0::swine_ptr &fixture, PyObject *args, PyObject *kwargs) + shared_py_object tryMake_DB0Dict(db0::swine_ptr &fixture, PyObject *args, + PyObject *kwargs, AccessFlags access_mode) { auto py_dict = DictDefaultObject_new(); - db0::FixtureLock lock(fixture); - auto &dict = py_dict->makeNew(*lock); + db0::FixtureLock lock(fixture); + auto &dict = py_dict->makeNew(*lock, access_mode); // if args if (!tryDictObject_update(py_dict.get(), args, kwargs)) { @@ -373,16 +374,16 @@ namespace db0::python return py_dict; } - shared_py_object tryMake_DB0DictInternal(PyObject *args, PyObject *kwargs) + shared_py_object tryMake_DB0DictInternal(PyObject *args, PyObject *kwargs, AccessFlags access_mode) { auto fixture = PyToolkit::getPyWorkspace().getWorkspace().getCurrentFixture(); - return tryMake_DB0Dict(fixture, args, kwargs); + return tryMake_DB0Dict(fixture, args, kwargs, access_mode); } DictObject *PyAPI_makeDict(PyObject *, PyObject* args, PyObject* kwargs) { - PY_API_FUNC - return runSafe(tryMake_DB0DictInternal, args, kwargs).steal(); + PY_API_FUNC + return runSafe(tryMake_DB0DictInternal, args, kwargs, AccessFlags {}).steal(); } PyObject *tryDictObject_clear(DictObject *dict_obj) diff --git a/src/dbzero/bindings/python/collections/PyDict.hpp b/src/dbzero/bindings/python/collections/PyDict.hpp index 7c003c7a..47d3e371 100644 --- a/src/dbzero/bindings/python/collections/PyDict.hpp +++ b/src/dbzero/bindings/python/collections/PyDict.hpp @@ -9,6 +9,7 @@ namespace db0::python using DictObject = PyWrapper; using DictIteratorObject = PySharedWrapper; + using AccessFlags = db0::AccessFlags; DictObject *DictObject_new(PyTypeObject *type, PyObject *, PyObject *); shared_py_object DictDefaultObject_new(); @@ -28,8 +29,11 @@ namespace db0::python PyObject *PyAPI_DictObject_items(DictObject *dict_obj); void PyAPI_DictObject_del(DictObject* dict_obj); extern PyTypeObject DictObjectType; - - shared_py_object tryMake_DB0Dict(db0::swine_ptr &, PyObject *args, PyObject *kwargs); + + shared_py_object tryMake_DB0Dict(db0::swine_ptr &, PyObject *args, + PyObject *kwargs, AccessFlags + ); + DictObject *PyAPI_makeDict(PyObject *, PyObject*, PyObject*); bool DictObject_Check(PyObject *); diff --git a/src/dbzero/bindings/python/collections/PyList.cpp b/src/dbzero/bindings/python/collections/PyList.cpp index d2e438af..f0857957 100644 --- a/src/dbzero/bindings/python/collections/PyList.cpp +++ b/src/dbzero/bindings/python/collections/PyList.cpp @@ -60,7 +60,7 @@ namespace db0::python return list_obj_copy; } - + PyObject *PyAPI_ListObject_multiply(ListObject *list_obj, PyObject *elem) { PY_API_FUNC @@ -68,11 +68,11 @@ namespace db0::python } shared_py_object tryMake_DB0ListInternal(db0::swine_ptr &fixture, - PyObject *const *args, Py_ssize_t nargs) + PyObject *const *args, Py_ssize_t nargs, AccessFlags access_mode) { auto py_list = Py_OWN(ListObject_new(&ListObjectType, NULL, NULL)); db0::FixtureLock lock(fixture); - auto &list = py_list->makeNew(*lock); + auto &list = py_list->makeNew(*lock, access_mode); if (nargs == 1) { if (!tryObjectT_extend(py_list.get(), args, nargs)) { return nullptr; @@ -83,7 +83,7 @@ namespace db0::python return py_list; } - ListObject *tryMake_ListInternal(PyObject *self, PyObject *const *args, Py_ssize_t nargs) + ListObject *tryMake_ListInternal(PyObject *self, PyObject *const *args, Py_ssize_t nargs, AccessFlags access_mode) { if (nargs != 1 && nargs != 0) { PyErr_SetString(PyExc_TypeError, "list() takes exactly one or zero argument"); @@ -91,7 +91,7 @@ namespace db0::python } auto fixture = PyToolkit::getPyWorkspace().getWorkspace().getCurrentFixture(); - return tryMake_DB0ListInternal(fixture, args, nargs).steal(); + return tryMake_DB0ListInternal(fixture, args, nargs, access_mode).steal(); } PyObject *tryListObject_GetItemSlice(ListObject *py_src_list, PyObject *elem) @@ -350,14 +350,16 @@ namespace db0::python return Py_TYPE(object) == &ListObjectType; } - shared_py_object tryMake_DB0List(db0::swine_ptr &fixture, PyObject *const *args, Py_ssize_t nargs) { - return tryMake_DB0ListInternal(fixture, args, nargs); + shared_py_object tryMake_DB0List(db0::swine_ptr &fixture, PyObject *const *args, + Py_ssize_t nargs, AccessFlags access_mode) + { + return tryMake_DB0ListInternal(fixture, args, nargs, access_mode); } PyObject *PyAPI_makeList(PyObject *self, PyObject *const *args, Py_ssize_t nargs) { PY_API_FUNC - return runSafe(tryMake_ListInternal, self, args, nargs); + return runSafe(tryMake_ListInternal, self, args, nargs, AccessFlags{}); } PyObject *tryLoadList(ListObject *list, PyObject *kwargs, std::unordered_set *load_stack_ptr) diff --git a/src/dbzero/bindings/python/collections/PyList.hpp b/src/dbzero/bindings/python/collections/PyList.hpp index 6d8bccee..46255069 100644 --- a/src/dbzero/bindings/python/collections/PyList.hpp +++ b/src/dbzero/bindings/python/collections/PyList.hpp @@ -10,6 +10,7 @@ namespace db0::python { using ListObject = PyWrapper; + using AccessFlags = db0::AccessFlags; ListObject *ListObject_new(PyTypeObject *type, PyObject *, PyObject *); shared_py_object ListDefaultObject_new(); @@ -25,7 +26,7 @@ namespace db0::python extern PyTypeObject ListObjectType; // construct from a Python list (or empty) - shared_py_object tryMake_DB0List(db0::swine_ptr &, PyObject *const *args, Py_ssize_t nargs); + shared_py_object tryMake_DB0List(db0::swine_ptr &, PyObject *const *args, Py_ssize_t nargs, AccessFlags); PyObject *PyAPI_makeList(PyObject *self, PyObject *const *args, Py_ssize_t nargs); bool ListObject_Check(PyObject *); diff --git a/src/dbzero/bindings/python/collections/PySet.cpp b/src/dbzero/bindings/python/collections/PySet.cpp index d0cea71f..c66e5796 100644 --- a/src/dbzero/bindings/python/collections/PySet.cpp +++ b/src/dbzero/bindings/python/collections/PySet.cpp @@ -344,7 +344,7 @@ namespace db0::python } shared_py_object tryMake_DB0Set( - db0::swine_ptr &fixture, PyObject *const *args, Py_ssize_t nargs) + db0::swine_ptr &fixture, PyObject *const *args, Py_ssize_t nargs, AccessFlags access_mode) { // make actual dbzero instance, use default fixture auto py_set = SetDefaultObject_new(); @@ -375,7 +375,7 @@ namespace db0::python SetObject *tryMake_Set(PyObject *, PyObject *const *args, Py_ssize_t nargs) { auto fixture = PyToolkit::getPyWorkspace().getWorkspace().getCurrentFixture(); - return tryMake_DB0Set(fixture, args, nargs).steal(); + return tryMake_DB0Set(fixture, args, nargs, {}).steal(); } SetObject *PyAPI_makeSet(PyObject *obj, PyObject *const *args, Py_ssize_t nargs) diff --git a/src/dbzero/bindings/python/collections/PySet.hpp b/src/dbzero/bindings/python/collections/PySet.hpp index 9d668334..654cf186 100644 --- a/src/dbzero/bindings/python/collections/PySet.hpp +++ b/src/dbzero/bindings/python/collections/PySet.hpp @@ -8,6 +8,7 @@ namespace db0::python { using SetObject = PyWrapper; + using AccessFlags = db0::AccessFlags; SetObject *SetObject_new(PyTypeObject *type, PyObject *, PyObject *); shared_py_object SetDefaultObject_new(); @@ -40,8 +41,9 @@ namespace db0::python PyObject *PyAPI_SetObject_difference_in_place(SetObject *self, PyObject * ob); PyObject *PyAPI_SetObject_update(SetObject *self, PyObject * ob); PyObject *PyAPI_SetObject_intersection_in_place(SetObject *self, PyObject * ob); - - shared_py_object tryMake_DB0Set(db0::swine_ptr &, PyObject *const *args, Py_ssize_t nargs); + + shared_py_object tryMake_DB0Set(db0::swine_ptr &, PyObject *const *args, + Py_ssize_t nargs, AccessFlags); SetObject *PyAPI_makeSet(PyObject *, PyObject *const *args, Py_ssize_t nargs); bool SetObject_Check(PyObject *); diff --git a/src/dbzero/bindings/python/collections/PyTuple.cpp b/src/dbzero/bindings/python/collections/PyTuple.cpp index 1f001945..513ee4b1 100644 --- a/src/dbzero/bindings/python/collections/PyTuple.cpp +++ b/src/dbzero/bindings/python/collections/PyTuple.cpp @@ -225,7 +225,7 @@ namespace db0::python } shared_py_object tryMake_DB0Tuple(db0::swine_ptr &fixture, PyObject *const *args, - Py_ssize_t nargs) + Py_ssize_t nargs, AccessFlags access_mode) { using Tuple = db0::object_model::Tuple; @@ -238,7 +238,7 @@ namespace db0::python auto py_tuple = TupleDefaultObject_new(); db0::FixtureLock lock(fixture); if (nargs == 0) { - auto &tuple = py_tuple->makeNew(*lock, Tuple::tag_new_tuple(), 0); + auto &tuple = py_tuple->makeNew(*lock, Tuple::tag_new_tuple(), 0, access_mode); fixture->getLangCache().add(tuple.getAddress(), py_tuple.get()); return py_tuple; } @@ -259,12 +259,12 @@ namespace db0::python if (PyErr_Occurred()) { return nullptr; // Error from PyIter_Next } - auto &tuple = py_tuple->makeNew(*lock, Tuple::tag_new_tuple(), values.size()); + auto &tuple = py_tuple->makeNew(*lock, Tuple::tag_new_tuple(), values.size(), access_mode); for (std::size_t index = 0; index != values.size(); ++index) { tuple.setItem(lock, index, values[index]); } } else { - auto &tuple = py_tuple->makeNew(*lock, Tuple::tag_new_tuple(), length); + auto &tuple = py_tuple->makeNew(*lock, Tuple::tag_new_tuple(), length, access_mode); int index = 0; Py_FOR(item, iterator) { tuple.setItem(lock, index++, item); @@ -279,16 +279,17 @@ namespace db0::python return py_tuple; } - shared_py_object tryMake_DB0TupleInternal(PyObject *const *args, Py_ssize_t nargs) + shared_py_object tryMake_DB0TupleInternal(PyObject *const *args, + Py_ssize_t nargs, AccessFlags access_mode) { auto fixture = PyToolkit::getPyWorkspace().getWorkspace().getCurrentFixture(); - return tryMake_DB0Tuple(fixture, args, nargs); + return tryMake_DB0Tuple(fixture, args, nargs, access_mode); } - + PyObject *PyAPI_makeTuple(PyObject *self, PyObject *const *args, Py_ssize_t nargs) { - PY_API_FUNC - return runSafe(tryMake_DB0TupleInternal, args, nargs).steal(); + PY_API_FUNC + return runSafe(tryMake_DB0TupleInternal, args, nargs, AccessFlags{}).steal(); } bool TupleObject_Check(PyObject *object) { diff --git a/src/dbzero/bindings/python/collections/PyTuple.hpp b/src/dbzero/bindings/python/collections/PyTuple.hpp index 6e27159e..7d249b9a 100644 --- a/src/dbzero/bindings/python/collections/PyTuple.hpp +++ b/src/dbzero/bindings/python/collections/PyTuple.hpp @@ -23,7 +23,9 @@ namespace db0::python extern PyTypeObject TupleObjectType; - shared_py_object tryMake_DB0Tuple(db0::swine_ptr &, PyObject *const *args, Py_ssize_t nargs); + shared_py_object tryMake_DB0Tuple(db0::swine_ptr &, PyObject *const *args, + Py_ssize_t nargs, AccessFlags + ); PyObject *PyAPI_makeTuple(PyObject *self, PyObject *const *args, Py_ssize_t nargs); bool TupleObject_Check(PyObject *); diff --git a/src/dbzero/core/collections/vector/v_bvector.hpp b/src/dbzero/core/collections/vector/v_bvector.hpp index b5b31271..401a46a1 100644 --- a/src/dbzero/core/collections/vector/v_bvector.hpp +++ b/src/dbzero/core/collections/vector/v_bvector.hpp @@ -68,8 +68,8 @@ DB0_PACKED_END /** * New, empty instance of the data structure */ - v_bvector(Memspace &mem) - : super_t(mem, mem.getPageSize()) + v_bvector(Memspace &mem, AccessFlags access_mode = {}) + : super_t(mem, mem.getPageSize(), access_mode) , m_db_shift(data_container::shift(mem.getPageSize())) , m_db_mask(data_container::mask(mem.getPageSize())) , m_pb_shift(ptr_container::shift(mem.getPageSize())) @@ -77,17 +77,17 @@ DB0_PACKED_END { } - v_bvector(mptr ptr) - : super_t(ptr) + v_bvector(mptr ptr, AccessFlags access_mode = {}) + : super_t(ptr, access_mode) , m_db_shift(data_container::shift((*this)->m_page_size)) , m_db_mask(data_container::mask((*this)->m_page_size)) , m_pb_shift(ptr_container::shift((*this)->m_page_size)) - , m_pb_mask(ptr_container::mask((*this)->m_page_size)) + , m_pb_mask(ptr_container::mask((*this)->m_page_size)) { } - - v_bvector(db0::tag_verified, mptr ptr, std::size_t size_of = 0) - : super_t(db0::tag_verified(), ptr, size_of) + + v_bvector(db0::tag_verified, mptr ptr, std::size_t size_of = 0, AccessFlags access_mode = {}) + : super_t(db0::tag_verified(), ptr, size_of, access_mode) , m_db_shift(data_container::shift((*this)->m_page_size)) , m_db_mask(data_container::mask((*this)->m_page_size)) , m_pb_shift(ptr_container::shift((*this)->m_page_size)) @@ -117,12 +117,10 @@ DB0_PACKED_END this->m_last_block = nullptr; super_t::operator=(std::move(other)); } - - /** - * Construct populated with values from a specific sequence - */ - template v_bvector(Memspace &mem, const SequenceT &in) - : v_bvector(mem) + + // Construct populated with values from a specific sequence + template v_bvector(Memspace &mem, const SequenceT &in, AccessFlags access_mode = {}) + : v_bvector(mem, access_mode) { for (const auto &item: in) { push_back(item); @@ -130,18 +128,18 @@ DB0_PACKED_END } template - void init(Memspace &mem, const SequenceT &in) + void init(Memspace &mem, const SequenceT &in, AccessFlags access_mode = {}) { - super_t::init(mem); + super_t::init(mem, access_mode); for (const auto &item: in) { push_back(item); } } - std::uint16_t initUnique(Memspace &mem) + std::uint16_t initUnique(Memspace &mem, AccessFlags access_mode = {}) { auto page_size = mem.getPageSize(); - auto result = super_t::initUnique(mem, page_size); + auto result = super_t::initUnique(mem, page_size, access_mode); this->m_db_shift = data_container::shift(mem.getPageSize()); this->m_db_mask = data_container::mask(mem.getPageSize()); this->m_pb_shift = ptr_container::shift(mem.getPageSize()); @@ -149,9 +147,10 @@ DB0_PACKED_END return result; } - template std::uint16_t initUnique(Memspace &mem, const SequenceT &in) + template + std::uint16_t initUnique(Memspace &mem, const SequenceT &in, AccessFlags access_mode = {}) { - auto result = this->initUnique(mem); + auto result = this->initUnique(mem, access_mode); for (const auto &item: in) { push_back(item); } diff --git a/src/dbzero/core/crdt/CRDT_Allocator.cpp b/src/dbzero/core/crdt/CRDT_Allocator.cpp index e79e7146..d642461e 100644 --- a/src/dbzero/core/crdt/CRDT_Allocator.cpp +++ b/src/dbzero/core/crdt/CRDT_Allocator.cpp @@ -565,7 +565,6 @@ namespace db0 if (!m_allocs.lower_equal_window(address, alloc_window)) { THROWF(db0::BadAddressException) << "Invalid address: " << address; } - assert(!alloc_window[1].isEnd()); const auto alloc = *alloc_window[1].first; m_alloc_delta -= alloc.m_stride; diff --git a/src/dbzero/core/dram/DRAM_Allocator.cpp b/src/dbzero/core/dram/DRAM_Allocator.cpp index d5b32214..0161688f 100644 --- a/src/dbzero/core/dram/DRAM_Allocator.cpp +++ b/src/dbzero/core/dram/DRAM_Allocator.cpp @@ -44,7 +44,7 @@ namespace db0 } std::optional
DRAM_Allocator::tryAlloc(std::size_t size, std::uint32_t slot_num, - bool aligned, unsigned char realm_id) + bool aligned, unsigned char realm_id, unsigned char) { assert(slot_num == 0); assert(!aligned && "DRAM_Allocator: aligned allocation not supported"); diff --git a/src/dbzero/core/dram/DRAM_Allocator.hpp b/src/dbzero/core/dram/DRAM_Allocator.hpp index 96108a57..edd47008 100644 --- a/src/dbzero/core/dram/DRAM_Allocator.hpp +++ b/src/dbzero/core/dram/DRAM_Allocator.hpp @@ -26,7 +26,7 @@ namespace db0 void update(const std::unordered_set &allocs); std::optional
tryAlloc(std::size_t size, std::uint32_t slot_num = 0, - bool aligned = false, unsigned char realm_id = 0) override; + bool aligned = false, unsigned char realm_id = 0, unsigned char locality = 0) override; void free(Address) override; diff --git a/src/dbzero/core/memory/AccessOptions.hpp b/src/dbzero/core/memory/AccessOptions.hpp index 60963063..5704667e 100644 --- a/src/dbzero/core/memory/AccessOptions.hpp +++ b/src/dbzero/core/memory/AccessOptions.hpp @@ -9,15 +9,17 @@ namespace db0 enum class AccessOptions : std::uint16_t { - read = 0x0001, - write = 0x0002, + read = 0x0001, + write = 0x0002, // flag indicating the newly created resource - create = 0x0004, - no_cache = 0x0008, + create = 0x0004, + no_cache = 0x0008, // resource which should be kept in-memory - no_flush = 0x0010, + no_flush = 0x0010, // disable copy-on-write (e.g. when accessed as read-only) - no_cow = 0x0020 + no_cow = 0x0020, + // flag to excempt resource from dirty cache tracking (relevant for BoundaryLock) + no_dirty_cache = 0x0040 }; /** @@ -31,10 +33,8 @@ namespace db0 static constexpr std::uint16_t RESOURCE_DIRTY = 0x0100; // Flag indicating if the lock has been registered with cache recycler static constexpr std::uint16_t RESOURCE_RECYCLED = 0x0200; - // a flag indicating that the resource should not be cached - static constexpr std::uint16_t RESOURCE_NO_CACHE = 0x0400; // prevent resource from being overwritten (e.g. prevent upgrade to a higher transaction number in PrefixImpl) - static constexpr std::uint16_t RESOURCE_FREEZE = 0x0800; + static constexpr std::uint16_t RESOURCE_FREEZE = 0x0400; enum class AccessType: unsigned int { @@ -44,6 +44,8 @@ namespace db0 AccessType parseAccessType(const std::string &access_type); + using AccessFlags = FlagSet; + } DECLARE_ENUM_VALUES(db0::AccessOptions, 7) diff --git a/src/dbzero/core/memory/AlgoAllocator.cpp b/src/dbzero/core/memory/AlgoAllocator.cpp index b651c585..5dea62da 100644 --- a/src/dbzero/core/memory/AlgoAllocator.cpp +++ b/src/dbzero/core/memory/AlgoAllocator.cpp @@ -14,7 +14,7 @@ namespace db0 } std::optional
AlgoAllocator::tryAlloc(std::size_t size, std::uint32_t slot_num, - bool aligned, unsigned char) + bool aligned, unsigned char, unsigned char) { assert(slot_num == 0); assert(!aligned && "AlgoAllocator: aligned allocation not supported"); diff --git a/src/dbzero/core/memory/AlgoAllocator.hpp b/src/dbzero/core/memory/AlgoAllocator.hpp index ba016011..ac54d6d0 100644 --- a/src/dbzero/core/memory/AlgoAllocator.hpp +++ b/src/dbzero/core/memory/AlgoAllocator.hpp @@ -17,8 +17,8 @@ namespace db0 AlgoAllocator(AddressPoolF f, ReverseAddressPoolF rf, std::size_t alloc_size); - std::optional
tryAlloc(std::size_t size, std::uint32_t slot_num = 0, - bool aligned = false, unsigned char realm_id = 0) override; + std::optional
tryAlloc(std::size_t size, std::uint32_t slot_num = 0, + bool aligned = false, unsigned char realm_id = 0, unsigned char locality = 0) override; void free(Address) override; diff --git a/src/dbzero/core/memory/Allocator.cpp b/src/dbzero/core/memory/Allocator.cpp index ad7eed46..878108ae 100644 --- a/src/dbzero/core/memory/Allocator.cpp +++ b/src/dbzero/core/memory/Allocator.cpp @@ -6,24 +6,26 @@ namespace db0 { std::optional Allocator::tryAllocUnique( - std::size_t size, std::uint32_t slot_num, bool aligned, unsigned char realm_id) + std::size_t, std::uint32_t, bool, unsigned char, unsigned char) { THROWF(InternalException) << "Allocator: unique allocation not supported by: " << typeid(*this).name() << THROWF_END; } - Address Allocator::alloc(std::size_t size, std::uint32_t slot_num, bool aligned, unsigned char realm_id) + Address Allocator::alloc(std::size_t size, std::uint32_t slot_num, bool aligned, + unsigned char realm_id, unsigned char locality) { - auto result = tryAlloc(size, slot_num, aligned, realm_id); + auto result = tryAlloc(size, slot_num, aligned, realm_id, locality); if (!result) { THROWF(InternalException) << "Allocator: out of memory" << THROWF_END; } return *result; } - UniqueAddress Allocator::allocUnique(std::size_t size, std::uint32_t slot_num, bool aligned, unsigned char realm_id) + UniqueAddress Allocator::allocUnique(std::size_t size, std::uint32_t slot_num, bool aligned, + unsigned char realm_id, unsigned char locality) { - auto result = tryAllocUnique(size, slot_num, aligned, realm_id); + auto result = tryAllocUnique(size, slot_num, aligned, realm_id, locality); if (!result) { THROWF(InternalException) << "Allocator: out of memory" << THROWF_END; } diff --git a/src/dbzero/core/memory/Allocator.hpp b/src/dbzero/core/memory/Allocator.hpp index 735e84f5..8ca9d6c3 100644 --- a/src/dbzero/core/memory/Allocator.hpp +++ b/src/dbzero/core/memory/Allocator.hpp @@ -24,17 +24,18 @@ namespace db0 * @param align a flag for page-aligned allocation * @param unique a flag for generating a unique, never repeating addresses * @param realm_id the realm ID to allocate from (where supported) + * @param locality the locality (hint) to allocate from (where supported) (0 = any locality) * Note that slot functionality is implementation specific and may not be supported by all allocators. * We use slots in special cases where objects needs to be allocated from a limited narrow address range */ virtual std::optional
tryAlloc(std::size_t size, std::uint32_t slot_num = 0, - bool aligned = false, unsigned char realm_id = 0) = 0; + bool aligned = false, unsigned char realm_id = 0, unsigned char locality = 0) = 0; // Try allocating a unique, never repeating address // NOTE: this functionality is only supported by some allocators // The default throwing implementation is provided virtual std::optional tryAllocUnique(std::size_t size, std::uint32_t slot_num = 0, - bool aligned = false, unsigned char realm_id = 0); + bool aligned = false, unsigned char realm_id = 0, unsigned char locality = 0); /** * Free previously allocated address @@ -78,11 +79,13 @@ namespace db0 * @param slot_num optional slot number to allocate from (slot_num = 0 means any slot). * @return the address of the range */ - Address alloc(std::size_t size, std::uint32_t slot_num = 0, bool aligned = false, unsigned char realm_id = 0); + Address alloc(std::size_t size, std::uint32_t slot_num = 0, bool aligned = false, + unsigned char realm_id = 0, unsigned char locality = 0); - UniqueAddress allocUnique(std::size_t size, std::uint32_t slot_num = 0, bool aligned = false, unsigned char realm_id = 0); + UniqueAddress allocUnique(std::size_t size, std::uint32_t slot_num = 0, bool aligned = false, + unsigned char realm_id = 0, unsigned char locality = 0); - // Check if the address is wihith the range managed by the allocator + // Check if the address is within the range managed by the allocator // (only applicable to limited allocators - e.g. SlabAllocator) virtual bool inRange(Address) const; diff --git a/src/dbzero/core/memory/BitsetAllocator.hpp b/src/dbzero/core/memory/BitsetAllocator.hpp index 9101130d..c4c3f92f 100644 --- a/src/dbzero/core/memory/BitsetAllocator.hpp +++ b/src/dbzero/core/memory/BitsetAllocator.hpp @@ -24,7 +24,7 @@ namespace db0 BitsetAllocator(BitSetT &&bitset, Address base_addr, std::size_t alloc_size, int direction); std::optional
tryAlloc(std::size_t size, std::uint32_t slot_num = 0, - bool aligned = false, unsigned char realm_id = 0) override; + bool aligned = false, unsigned char realm_id = 0, unsigned char locality = 0) override; void free(Address) override; @@ -95,7 +95,7 @@ namespace db0 } template std::optional
- BitsetAllocator::tryAlloc(std::size_t size, std::uint32_t slot_num, bool aligned, unsigned char) + BitsetAllocator::tryAlloc(std::size_t size, std::uint32_t slot_num, bool aligned, unsigned char, unsigned char) { assert(slot_num == 0); // all BitSetAllocator allocations are aligned diff --git a/src/dbzero/core/memory/BoundaryLock.cpp b/src/dbzero/core/memory/BoundaryLock.cpp index 7dcee878..943e8f0d 100644 --- a/src/dbzero/core/memory/BoundaryLock.cpp +++ b/src/dbzero/core/memory/BoundaryLock.cpp @@ -8,8 +8,8 @@ namespace db0 BoundaryLock::BoundaryLock(StorageContext context, std::uint64_t address, std::shared_ptr lhs, std::size_t lhs_size, std::shared_ptr rhs, std::size_t rhs_size, FlagSet access_mode) - // important to use no_cache for BoundaryLock (this is to allow release/creation of a new boundary lock without collisions) - : ResourceLock(context, address, lhs_size + rhs_size, access_mode | AccessOptions::no_cache) + // important to use no_dirty_cache for BoundaryLock (this is to allow release/creation of a new boundary lock without collisions) + : ResourceLock(context, address, lhs_size + rhs_size, access_mode | AccessOptions::no_dirty_cache) , m_lhs(lhs) , m_lhs_size(lhs_size) , m_rhs(rhs) @@ -28,8 +28,8 @@ namespace db0 std::shared_ptr lhs, std::size_t lhs_size, std::shared_ptr rhs, std::size_t rhs_size, FlagSet access_mode) - // important to use no_cache for BoundaryLock (this is to allow release/creation of a new boundary lock without collisions) - : ResourceLock(context, address, lhs_size + rhs_size, access_mode | AccessOptions::no_cache) + // important to use no_dirty_cache for BoundaryLock (this is to allow release/creation of a new boundary lock without collisions) + : ResourceLock(context, address, lhs_size + rhs_size, access_mode | AccessOptions::no_dirty_cache) , m_lhs(lhs) , m_lhs_size(lhs_size) , m_rhs(rhs) diff --git a/src/dbzero/core/memory/Memspace.cpp b/src/dbzero/core/memory/Memspace.cpp index 7ffcfa69..6273a117 100644 --- a/src/dbzero/core/memory/Memspace.cpp +++ b/src/dbzero/core/memory/Memspace.cpp @@ -136,15 +136,15 @@ namespace db0 m_prefix->cancelAtomic(); } - Address Memspace::alloc(std::size_t size, std::uint32_t slot_num, unsigned char realm_id) { + Address Memspace::alloc(std::size_t size, std::uint32_t slot_num, unsigned char realm_id, unsigned char locality) { // align if the alloc size > page size - return getAllocatorForUpdate().alloc(size, slot_num, size > m_page_size, realm_id); + return getAllocatorForUpdate().alloc(size, slot_num, size > m_page_size, realm_id, locality); } - UniqueAddress Memspace::allocUnique(std::size_t size, std::uint32_t slot_num, unsigned char realm_id) { - return getAllocatorForUpdate().allocUnique(size, slot_num, size > m_page_size, realm_id); + UniqueAddress Memspace::allocUnique(std::size_t size, std::uint32_t slot_num, unsigned char realm_id, unsigned char locality) { + return getAllocatorForUpdate().allocUnique(size, slot_num, size > m_page_size, realm_id, locality); } - + void Memspace::free(Address address) { getAllocatorForUpdate().free(address); } diff --git a/src/dbzero/core/memory/Memspace.hpp b/src/dbzero/core/memory/Memspace.hpp index 0c10858b..8252ed00 100644 --- a/src/dbzero/core/memory/Memspace.hpp +++ b/src/dbzero/core/memory/Memspace.hpp @@ -44,8 +44,10 @@ namespace db0 } // Memspace::alloc implements the auto-align logic - Address alloc(std::size_t size, std::uint32_t slot_num = 0, unsigned char realm_id = 0); - UniqueAddress allocUnique(std::size_t size, std::uint32_t slot_num = 0, unsigned char realm_id = 0); + Address alloc(std::size_t size, std::uint32_t slot_num = 0, unsigned char realm_id = 0, + unsigned char locality = 0); + UniqueAddress allocUnique(std::size_t size, std::uint32_t slot_num = 0, unsigned char realm_id = 0, + unsigned char locality = 0); void free(Address); diff --git a/src/dbzero/core/memory/MetaAllocator.cpp b/src/dbzero/core/memory/MetaAllocator.cpp index af056aca..60d76f8f 100644 --- a/src/dbzero/core/memory/MetaAllocator.cpp +++ b/src/dbzero/core/memory/MetaAllocator.cpp @@ -101,7 +101,7 @@ namespace db0 , m_slab_size(slab_size) { } - + class SlabManager { public: @@ -149,22 +149,58 @@ namespace db0 } }; + // NOTE: only localities 0 and 1 are currently supported + struct ActiveSlab: public std::array + { + bool contains(std::uint32_t slab_id) const { + return ((*this)[0] == slab_id || (*this)[1] == slab_id); + } + + bool contains(const FindResult &slab) const { + return ((*this)[0] == slab || (*this)[1] == slab); + } + + FindResult find(std::uint32_t slab_id) const + { + if ((*this)[0] == slab_id) { + return (*this)[0]; + } else if ((*this)[1] == slab_id) { + return (*this)[1]; + } + return {}; + } + + void erase(const FindResult &slab) + { + if ((*this)[0] == slab) { + (*this)[0] = {}; + } else if ((*this)[1] == slab) { + (*this)[1] = {}; + } else { + assert(false); + THROWF(db0::InternalException) << "Slab not found in active slabs." << THROWF_END; + } + } + }; + /** * Retrieves the active slab or returns nullptr if no active slab available */ - FindResult tryGetActiveSlab() { - return m_active_slab; + FindResult tryGetActiveSlab(unsigned char locality) { + assert(locality < m_active_slab.size()); + return m_active_slab[locality]; } - - void resetActiveSlab() { - m_active_slab = {}; + + void resetActiveSlab(unsigned char locality) { + assert(locality < m_active_slab.size()); + m_active_slab[locality] = {}; } /** * Retrieve the 1st slab to allocate a block of at least min_capacity * this is only a 'hint' and if the allocation is not possible, the next slab should be attempted */ - FindResult findFirst(std::size_t min_capacity) + FindResult findFirst(std::size_t min_capacity, unsigned char locality) { // visit slabs starting from the largest available capacity auto it = m_capacity_items.cbegin(); @@ -173,25 +209,23 @@ namespace db0 // no existing slab has sufficient capacity return {}; } - - if (m_active_slab == it->m_slab_id) { + + if (m_active_slab.contains(it->m_slab_id)) { // do not include active slab in find operation ++it; continue; } auto slab = openSlab(m_slab_address_func(it->m_slab_id)); - if (!m_active_slab) { + if (!m_active_slab[locality]) { // make the slab active - m_active_slab = slab; + m_active_slab[locality] = slab; } return slab; } } - /** - * Continue after findFirst - */ - FindResult findNext(FindResult last_result, std::size_t min_capacity) + // Continue after findFirst + FindResult findNext(FindResult last_result, std::size_t min_capacity, unsigned char locality) { for (;;) { // this is to find the next item in order @@ -201,14 +235,14 @@ namespace db0 return {}; } - if (m_active_slab == it.first->m_slab_id) { + if (m_active_slab.contains(it.first->m_slab_id)) { // do not include active slab in find operation continue; } auto slab = openSlab(m_slab_address_func(it.first->m_slab_id)); - if (!m_active_slab) { + if (!m_active_slab[locality]) { // make the slab active - m_active_slab = slab; + m_active_slab[locality] = slab; } return slab; } @@ -241,10 +275,8 @@ namespace db0 return { slab, slab_id }; } - /** - * Create a new, registered slab instance - */ - FindResult addNewSlab() + // Create a new, registered slab instance + FindResult addNewSlab(unsigned char locality) { auto [slab, slab_id] = createNewSlab(); auto address = m_slab_address_func(slab_id); @@ -275,8 +307,8 @@ namespace db0 } // make the newly added slab active - m_active_slab = { slab, cap_item }; - return m_active_slab; + m_active_slab[locality] = { slab, cap_item }; + return m_active_slab[locality]; } std::uint32_t getRemainingCapacity(std::uint32_t slab_id) const @@ -312,8 +344,8 @@ namespace db0 FindResult tryFind(std::uint32_t slab_id) const { if (slab_id < nextSlabId()) { - if (m_active_slab == slab_id) { - return m_active_slab; + if (m_active_slab.contains(slab_id)) { + return m_active_slab.find(slab_id); } // look up with the cache first auto address = m_slab_address_func(slab_id); @@ -533,7 +565,8 @@ namespace db0 // slab cache by address mutable std::unordered_map > m_slabs; mutable std::vector > m_reserved_slabs; - mutable FindResult m_active_slab; + // active slabs for each supported locality (0 or 1) + mutable ActiveSlab m_active_slab; // address by allocation ID (from the algo-allocator) std::function m_slab_address_func; std::function m_slab_id_func; @@ -648,8 +681,8 @@ namespace db0 }); } // unregister if active - if (m_active_slab == slab) { - m_active_slab = {}; + if (m_active_slab.contains(slab)) { + m_active_slab.erase(slab); } // unregister from slab defs if (!m_slab_defs.erase_equal(slab.m_cap_item.m_slab_id).first) { @@ -789,31 +822,31 @@ namespace db0 } std::optional
MetaAllocator::tryAlloc(std::size_t size, std::uint32_t slot_num, - bool aligned, unsigned char realm_id) + bool aligned, unsigned char realm_id, unsigned char locality) { std::uint16_t instance_id; - return tryAllocImpl(size, slot_num, aligned, false, instance_id, realm_id); + return tryAllocImpl(size, slot_num, aligned, false, instance_id, realm_id, locality); } std::optional MetaAllocator::tryAllocUnique(std::size_t size, std::uint32_t slot_num, - bool aligned, unsigned char realm_id) + bool aligned, unsigned char realm_id, unsigned char locality) { std::uint16_t instance_id; - auto addr = tryAllocImpl(size, slot_num, aligned, true, instance_id, realm_id); + auto addr = tryAllocImpl(size, slot_num, aligned, true, instance_id, realm_id, locality); if (addr) { return UniqueAddress(*addr, instance_id); } return {}; } - std::optional
MetaAllocator::tryAllocImpl(std::size_t size, std::uint32_t slot_num, - bool aligned, bool unique, std::uint16_t &instance_id, unsigned char realm_id) + std::optional
MetaAllocator::tryAllocImpl(std::size_t size, std::uint32_t slot_num, bool aligned, bool unique, + std::uint16_t &instance_id, unsigned char realm_id, unsigned char locality) { assert(slot_num == 0); assert(size > 0); // try allocating from the active slab first auto &realm = m_realms[realm_id]; - auto slab = realm.tryGetActiveSlab(); + auto slab = realm.tryGetActiveSlab(locality); bool is_first = true; bool is_new = false; for (;;) { @@ -822,7 +855,7 @@ namespace db0 auto addr = slab.m_slab->tryAlloc(size, 0, aligned); if (!addr) { // NOTE: since the last allocation failed, don't use this slab as "active" - realm.resetActiveSlab(); + realm.resetActiveSlab(locality); break; } @@ -843,13 +876,13 @@ namespace db0 } } if (is_first) { - slab = realm.findFirst(size); + slab = realm.findFirst(size, locality); is_first = false; } else { - slab = realm.findNext(slab, size); + slab = realm.findNext(slab, size, locality); } if (!slab.m_slab) { - slab = realm.addNewSlab(); + slab = realm.addNewSlab(locality); is_new = true; } } @@ -938,10 +971,6 @@ namespace db0 return slab.m_slab->isAllocated(address, size_of_result); } - std::uint32_t MetaAllocator::getSlabId(Address address) const { - return m_slab_id_function(address); - } - unsigned int MetaAllocator::getSlabCount() const { unsigned int total_slab_count = 0; @@ -1163,6 +1192,10 @@ namespace db0 void MetaAllocator::Realm::cancelAtomic() { m_slab_manager->cancelAtomic(); } + + std::uint32_t MetaAllocator::getSlabId(Address address) const { + return m_slab_id_function(address); + } } diff --git a/src/dbzero/core/memory/MetaAllocator.hpp b/src/dbzero/core/memory/MetaAllocator.hpp index 2f523a36..a8a42c74 100644 --- a/src/dbzero/core/memory/MetaAllocator.hpp +++ b/src/dbzero/core/memory/MetaAllocator.hpp @@ -199,10 +199,10 @@ DB0_PACKED_END using SlabTreeT = SGB_Tree; std::optional
tryAlloc(std::size_t size, std::uint32_t slot_num = 0, - bool aligned = false, unsigned char realm_id = 0) override; + bool aligned = false, unsigned char realm_id = 0, unsigned char locality = 0) override; std::optional tryAllocUnique(std::size_t size, std::uint32_t slot_num = 0, - bool aligned = false, unsigned char realm_id = 0) override; + bool aligned = false, unsigned char realm_id = 0, unsigned char locality = 0) override; void free(Address) override; @@ -232,12 +232,7 @@ DB0_PACKED_END static std::function getSlabIdFunction(std::size_t offset, std::size_t page_size, std::size_t slab_size); - - /** - * Calculate slab ID for the given address - */ - std::uint32_t getSlabId(Address) const; - + unsigned int getSlabCount() const; /** @@ -278,6 +273,10 @@ DB0_PACKED_END void beginAtomic(); void endAtomic(); void cancelAtomic(); + + protected: + // Calculate slab ID for the given address + std::uint32_t getSlabId(Address) const; private: std::shared_ptr m_prefix; @@ -363,10 +362,10 @@ DB0_PACKED_END void deferredFree(Address); // NOTE: instance ID will only be populated when unique = true - std::optional
tryAllocImpl(std::size_t size, std::uint32_t slot_num, - bool aligned, bool unique, std::uint16_t &instance_id, unsigned char realm_id); + std::optional
tryAllocImpl(std::size_t size, std::uint32_t slot_num, bool aligned, bool unique, + std::uint16_t &instance_id, unsigned char realm_id, unsigned char locality); }; - + } namespace std diff --git a/src/dbzero/core/memory/OneShotAllocator.cpp b/src/dbzero/core/memory/OneShotAllocator.cpp index 73ca2d71..769b421c 100644 --- a/src/dbzero/core/memory/OneShotAllocator.cpp +++ b/src/dbzero/core/memory/OneShotAllocator.cpp @@ -13,7 +13,7 @@ namespace db0 } std::optional
OneShotAllocator::tryAlloc(std::size_t size, std::uint32_t slot_num, - bool aligned, unsigned char) + bool aligned, unsigned char, unsigned char) { assert(slot_num == 0); assert(!aligned && "OneShotAllocator: aligned allocation not supported"); diff --git a/src/dbzero/core/memory/OneShotAllocator.hpp b/src/dbzero/core/memory/OneShotAllocator.hpp index 4e686cb8..000f0832 100644 --- a/src/dbzero/core/memory/OneShotAllocator.hpp +++ b/src/dbzero/core/memory/OneShotAllocator.hpp @@ -15,7 +15,7 @@ namespace db0 OneShotAllocator(Address addr, std::size_t size); std::optional
tryAlloc(std::size_t size, std::uint32_t slot_num = 0, - bool aligned = false, unsigned char realm_id = 0) override; + bool aligned = false, unsigned char realm_id = 0, unsigned char locality = 0) override; void free(Address) override; diff --git a/src/dbzero/core/memory/PrefixImpl.cpp b/src/dbzero/core/memory/PrefixImpl.cpp index f98aceb4..6c4af42b 100644 --- a/src/dbzero/core/memory/PrefixImpl.cpp +++ b/src/dbzero/core/memory/PrefixImpl.cpp @@ -196,7 +196,7 @@ namespace db0 std::shared_ptr PrefixImpl::mapWideRange( std::uint64_t first_page, std::uint64_t end_page, std::uint64_t address, std::size_t size, StateNumType state_num, FlagSet access_mode) - { + { StateNumType read_state_num = 0; auto lock_info = m_cache.findRange(first_page, end_page, address, size, state_num, access_mode, read_state_num); if (!lock_info.second && lock_info.first) { diff --git a/src/dbzero/core/memory/ResourceLock.cpp b/src/dbzero/core/memory/ResourceLock.cpp index 649ec55d..abcec796 100644 --- a/src/dbzero/core/memory/ResourceLock.cpp +++ b/src/dbzero/core/memory/ResourceLock.cpp @@ -23,9 +23,6 @@ namespace db0 FlagSet access_mode, std::shared_ptr cow_lock) : m_context(storage_context) , m_address(address) - , m_resource_flags( - (access_mode[AccessOptions::no_cache] ? db0::RESOURCE_NO_CACHE : 0) - ) , m_access_mode(access_mode) , m_data(size, static_cast(0)) , m_cow_lock(cow_lock) @@ -43,8 +40,7 @@ namespace db0 , m_address(lock->m_address) // copy-on-write, the recycled flag must be erased , m_resource_flags( - (lock->m_resource_flags & ~(db0::RESOURCE_RECYCLED | db0::RESOURCE_DIRTY)) | - (access_mode[AccessOptions::no_cache] ? db0::RESOURCE_NO_CACHE : 0) + (lock->m_resource_flags & ~(db0::RESOURCE_RECYCLED | db0::RESOURCE_DIRTY)) ) , m_access_mode(access_mode) , m_data(lock->m_data) @@ -83,7 +79,7 @@ namespace db0 } bool ResourceLock::isCached() const { - return !(m_resource_flags & db0::RESOURCE_NO_CACHE); + return !m_access_mode[AccessOptions::no_cache]; } bool ResourceLock::resetDirtyFlag() @@ -110,8 +106,8 @@ namespace db0 { if (m_access_mode[AccessOptions::no_flush]) { m_access_mode.set(AccessOptions::no_flush, false); - // if dirty, we need to register with the dirty cache - if (isDirty() && !m_access_mode[AccessOptions::no_cache]) { + // if dirty, we need to register with the dirty cache (unless exliclity marked no_dirty_cache) + if (isDirty() && !m_access_mode[AccessOptions::no_dirty_cache]) { m_context.m_cache_ref.get().append(shared_from_this()); } } @@ -130,8 +126,8 @@ namespace db0 { if (atomicCheckAndSetFlags(m_resource_flags, db0::RESOURCE_DIRTY)) { // register lock with the dirty cache - // NOTE: locks marked no_cache (e.g. BoundaryLock) or no_flush (atomic locks) are not registered with the dirty cache - if (!m_access_mode[AccessOptions::no_cache] && !m_access_mode[AccessOptions::no_flush]) { + // NOTE: locks marked no_dirty_cache (e.g. BoundaryLock) or no_flush (atomic locks) are not registered with the dirty cache + if (!m_access_mode[AccessOptions::no_dirty_cache] && !m_access_mode[AccessOptions::no_flush]) { // register with the dirty cache m_context.m_cache_ref.get().append(shared_from_this()); } @@ -214,7 +210,7 @@ namespace db0 return result; } - std::uint64_t ResourceLock::getAddressOf(const void *ptr) const + std::uint64_t ResourceLock::getAddressOf(const void *ptr) const { assert(ptr >= m_data.data() && ptr < m_data.data() + m_data.size()); return m_address + static_cast(ptr) - m_data.data(); diff --git a/src/dbzero/core/memory/SlabAllocator.cpp b/src/dbzero/core/memory/SlabAllocator.cpp index 2a935ddc..d2a536f5 100644 --- a/src/dbzero/core/memory/SlabAllocator.cpp +++ b/src/dbzero/core/memory/SlabAllocator.cpp @@ -64,7 +64,7 @@ namespace db0 } std::optional
SlabAllocator::tryAlloc(std::size_t size, std::uint32_t slot_num, - bool aligned, unsigned char) + bool aligned, unsigned char, unsigned char) { assert(slot_num == 0); assert(size > 0); diff --git a/src/dbzero/core/memory/SlabAllocator.hpp b/src/dbzero/core/memory/SlabAllocator.hpp index f81c7475..81540f23 100644 --- a/src/dbzero/core/memory/SlabAllocator.hpp +++ b/src/dbzero/core/memory/SlabAllocator.hpp @@ -71,7 +71,7 @@ DB0_PACKED_BEGIN virtual ~SlabAllocator(); std::optional
tryAlloc(std::size_t size, std::uint32_t slot_num = 0, - bool aligned = false, unsigned char realm_id = 0) override; + bool aligned = false, unsigned char realm_id = 0, unsigned char locality = 0) override; void free(Address) override; diff --git a/src/dbzero/core/memory/SlotAllocator.cpp b/src/dbzero/core/memory/SlotAllocator.cpp index 377a0e72..c0cf0a8d 100644 --- a/src/dbzero/core/memory/SlotAllocator.cpp +++ b/src/dbzero/core/memory/SlotAllocator.cpp @@ -47,23 +47,23 @@ namespace db0 }; std::optional
SlotAllocator::tryAlloc(std::size_t size, std::uint32_t slot_num, - bool aligned, unsigned char realm_id) + bool aligned, unsigned char realm_id, unsigned char locality) { if (!slot_num) { - return m_allocator_ptr->tryAlloc(size, 0, aligned, realm_id); + return m_allocator_ptr->tryAlloc(size, 0, aligned, realm_id, locality); } - return select(slot_num).tryAlloc(size, 0, aligned, realm_id); + return select(slot_num).tryAlloc(size, 0, aligned, realm_id, locality); } std::optional SlotAllocator::tryAllocUnique(std::size_t size, std::uint32_t slot_num, - bool aligned, unsigned char realm_id) + bool aligned, unsigned char realm_id, unsigned char locality) { if (!slot_num) { - return m_allocator_ptr->tryAllocUnique(size, 0, aligned, realm_id); + return m_allocator_ptr->tryAllocUnique(size, 0, aligned, realm_id, locality); } - return select(slot_num).tryAllocUnique(size, 0, aligned, realm_id); + return select(slot_num).tryAllocUnique(size, 0, aligned, realm_id, locality); } void SlotAllocator::free(Address address) { diff --git a/src/dbzero/core/memory/SlotAllocator.hpp b/src/dbzero/core/memory/SlotAllocator.hpp index 50323ddb..ad54fa23 100644 --- a/src/dbzero/core/memory/SlotAllocator.hpp +++ b/src/dbzero/core/memory/SlotAllocator.hpp @@ -23,11 +23,11 @@ namespace db0 void setSlot(std::uint32_t slot_num, std::shared_ptr slot_allocator); std::optional
tryAlloc(std::size_t size, std::uint32_t slot_num = 0, - bool aligned = false, unsigned char realm_id = 0) override; + bool aligned = false, unsigned char realm_id = 0, unsigned char locality = 0) override; // Unique allocations are not supported because of the limited slot's address space std::optional tryAllocUnique(std::size_t size, std::uint32_t slot_num = 0, - bool aligned = false, unsigned char realm_id = 0) override; + bool aligned = false, unsigned char realm_id = 0, unsigned char locality = 0) override; void free(Address) override; diff --git a/src/dbzero/core/storage/BDevStorage.hpp b/src/dbzero/core/storage/BDevStorage.hpp index 8040674f..cf39a1a4 100644 --- a/src/dbzero/core/storage/BDevStorage.hpp +++ b/src/dbzero/core/storage/BDevStorage.hpp @@ -20,8 +20,8 @@ namespace db0 { -DB0_PACKED_BEGIN +DB0_PACKED_BEGIN struct DB0_PACKED_ATTR o_prefix_config: public o_fixed { // magic number for the .db0 file @@ -43,6 +43,7 @@ DB0_PACKED_BEGIN o_prefix_config(std::uint32_t block_size, std::uint32_t page_size, std::uint32_t dram_page_size); }; +DB0_PACKED_END /** * Block-device based storage implementation @@ -194,6 +195,5 @@ DB0_PACKED_BEGIN void _read(std::uint64_t address, StateNumType state_num, std::size_t size, void *buffer, FlagSet = { AccessOptions::read, AccessOptions::write }, unsigned int *chain_len = nullptr) const; }; - -DB0_PACKED_END + } \ No newline at end of file diff --git a/src/dbzero/core/utils/FlagSet.hpp b/src/dbzero/core/utils/FlagSet.hpp index a04db28f..0a6ec934 100644 --- a/src/dbzero/core/utils/FlagSet.hpp +++ b/src/dbzero/core/utils/FlagSet.hpp @@ -13,8 +13,6 @@ namespace db0 { -DB0_PACKED_BEGIN - template class FlagSetLimits { public: @@ -31,7 +29,8 @@ DB0_PACKED_BEGIN THROWF(db0::InternalException) << "Missing DECLARE_ENUM_FLAGS directive. Internal error." << THROWF_END; } }; - + +DB0_PACKED_BEGIN template class DB0_PACKED_ATTR FlagSet { public: @@ -135,9 +134,9 @@ DB0_PACKED_BEGIN } FlagSet operator&(EnumT flag) const { - return FlagSet(m_flags & flag); + return FlagSet(m_flags & static_cast(flag)); } - + FlagSet operator&(const FlagSet &other) const { return FlagSet(m_flags & other.m_flags); } @@ -198,6 +197,7 @@ DB0_PACKED_BEGIN private: store_t m_flags = 0; }; +DB0_PACKED_END } @@ -241,8 +241,6 @@ namespace std { std::ostream &operator<<(std::ostream &os, EnumTypeName option) } \ THROWF(db0::InputException) << "Unrecognized flag: " << str_input << THROWF_END; } } -DB0_PACKED_END - namespace std { diff --git a/src/dbzero/core/vspace/v_object.hpp b/src/dbzero/core/vspace/v_object.hpp index 01f1ac3b..21917248 100644 --- a/src/dbzero/core/vspace/v_object.hpp +++ b/src/dbzero/core/vspace/v_object.hpp @@ -54,7 +54,7 @@ namespace db0 v_object(Memspace &memspace, Tuple&& t, int_seq) : v_this(ptr_t::makeNew( memspace, - c_type::measure(std::get(std::forward(t))...), + c_type::measure(std::get(std::forward(t))...), std::get(std::forward(t)) ) ) { @@ -71,6 +71,33 @@ namespace db0 c_type::__new(reinterpret_cast(&v_this.modify()), std::get(std::forward(t))...); } + template::value-1> + void init(Memspace &memspace, Tuple&& t, int_seq) + { + v_this = ptr_t::makeNew( + memspace, + c_type::measure(std::get(std::forward(t))...), + // access options (the last argument) + std::get(std::forward(t)) + ); + c_type::__new(reinterpret_cast(&v_this.modify()), std::get(std::forward(t))...); + } + + template::value-1> + std::uint16_t initUnique(Memspace &memspace, Tuple&& t, int_seq) + { + std::uint16_t instance_id; + v_this = ptr_t::makeNewUnique( + memspace, + instance_id, + c_type::measure(std::get(std::forward(t))...), + // access options (the last argument) + std::get(std::forward(t)) + ); + c_type::__new(reinterpret_cast(&v_this.modify()), std::get(std::forward(t))...); + return instance_id; + } + public: /** * Allocating constructor with flags @@ -90,37 +117,37 @@ namespace db0 : v_object(memspace, tag_prelocked(), std::forward_as_tuple(std::forward(args)...), make_int_seq_t()) { } - - /** - * Standard allocating constructor - */ + + // Standard allocating constructor template, Args...>* = nullptr, last_type_is_not_t* = nullptr> v_object(Memspace &memspace, Args&&... args) : v_object(memspace, std::forward(args)..., FlagSet {}) { } - /** - * Create a new dbzero instance in the given memory space - */ - template - void init(Memspace &memspace, Args&&... args) - { - v_this = ptr_t::makeNew(memspace, c_type::measure(std::forward(args)...)); - c_type::__new(reinterpret_cast(&v_this.modify()), std::forward(args)...); + // Create a new dbzero instance in the given memory space + template, Args...>* = nullptr> + void init(Memspace &memspace, Args&&... args) { + init(memspace, std::forward_as_tuple(std::forward(args)...), make_int_seq_t()); } - + + template, Args...>* = nullptr> + void init(Memspace &memspace, Args&&... args) { + init(memspace, std::forward(args)..., FlagSet {}); + } + // Create new instance assigned unique address // @return instance id - template - std::uint16_t initUnique(Memspace &memspace, Args&&... args) - { - std::uint16_t instance_id; - v_this = ptr_t::makeNewUnique(memspace, instance_id, c_type::measure(std::forward(args)...)); - c_type::__new(reinterpret_cast(&v_this.modify()), std::forward(args)...); - return instance_id; + template, Args...>* = nullptr> + std::uint16_t initUnique(Memspace &memspace, Args&&... args) { + return initUnique(memspace, std::forward_as_tuple(std::forward(args)...), make_int_seq_t()); } - + + template, Args...>* = nullptr> + std::uint16_t initUnique(Memspace &memspace, Args&&... args) { + return initUnique(memspace, std::forward(args)..., FlagSet {}); + } + // Construct from v-pointer v_object(ptr_t &&ptr) : v_this(std::move(ptr)) @@ -275,6 +302,10 @@ namespace db0 return v_this.isModified(); } + bool isNoCache() const { + return v_this.isNoCache(); + } + protected: // container reference mutable ptr_t v_this; diff --git a/src/dbzero/core/vspace/v_ptr.cpp b/src/dbzero/core/vspace/v_ptr.cpp index 6e8ae044..82932b37 100644 --- a/src/dbzero/core/vspace/v_ptr.cpp +++ b/src/dbzero/core/vspace/v_ptr.cpp @@ -33,10 +33,6 @@ namespace db0 assert(m_mem_lock.m_buffer); } - FlagSet vtypeless::getAccessMode() const { - return m_access_mode; - } - vtypeless &vtypeless::operator=(const vtypeless &other) { m_address = other.m_address; diff --git a/src/dbzero/core/vspace/v_ptr.hpp b/src/dbzero/core/vspace/v_ptr.hpp index 446646f8..756e0b50 100644 --- a/src/dbzero/core/vspace/v_ptr.hpp +++ b/src/dbzero/core/vspace/v_ptr.hpp @@ -17,15 +17,9 @@ namespace db0 { -DB0_PACKED_BEGIN - template class v_object; - struct DB0_PACKED_ATTR vso_null_t - { - }; - class vtypeless { protected : @@ -86,7 +80,9 @@ DB0_PACKED_BEGIN assertFlags(); } - FlagSet getAccessMode() const; + inline FlagSet getAccessMode() const { + return m_access_mode; + } vtypeless &operator=(const vtypeless &other); void operator=(vtypeless &&); @@ -122,6 +118,10 @@ DB0_PACKED_BEGIN inline Memspace *getMemspacePtr() const { return m_memspace_ptr; } + + inline bool isNoCache() const { + return m_access_mode[AccessOptions::no_cache]; + } /** * Get use count of the underlying lock @@ -279,7 +279,7 @@ DB0_PACKED_BEGIN { // read not allowed for instance creation assert(!access_mode[AccessOptions::read]); - auto address = memspace.alloc(size, SLOT_NUM, REALM_ID); + auto address = memspace.alloc(size, SLOT_NUM, REALM_ID, getLocality(access_mode)); // lock for create & write // NOTE: must extract physical address for mapRange auto mem_lock = memspace.getPrefix().mapRange(address, size, access_mode | AccessOptions::write); @@ -298,7 +298,7 @@ DB0_PACKED_BEGIN { // read not allowed for instance creation assert(!access_mode[AccessOptions::read]); - auto unique_address = memspace.allocUnique(size, SLOT_NUM, REALM_ID); + auto unique_address = memspace.allocUnique(size, SLOT_NUM, REALM_ID, getLocality(access_mode)); instance_id = unique_address.getInstanceId(); // lock for create & write // NOTE: must extract physical address for mapRange @@ -342,6 +342,11 @@ DB0_PACKED_BEGIN private: + static inline unsigned char getLocality(FlagSet access_mode) { + // NOTE: use locality = 1 for no_cache allocations, 0 otherwise (undefined) + return access_mode[AccessOptions::no_cache] ? 1 : 0; + } + void assureInitialized() const { assert(m_memspace_ptr); @@ -398,6 +403,4 @@ DB0_PACKED_BEGIN } }; -DB0_PACKED_END - } diff --git a/src/dbzero/object_model/ObjectBase.hpp b/src/dbzero/object_model/ObjectBase.hpp index 6b9a5f8f..c7cef626 100644 --- a/src/dbzero/object_model/ObjectBase.hpp +++ b/src/dbzero/object_model/ObjectBase.hpp @@ -11,7 +11,7 @@ namespace db0 { - using StorageClass = db0::object_model::StorageClass; + using StorageClass = db0::object_model::StorageClass; template void addToGC0(Fixture &fixture, void *vptr) { fixture.getGC0().add(vptr); @@ -42,7 +42,7 @@ namespace db0 using self_t = ObjectBase; using LangToolkit = db0::object_model::LangConfig::LangToolkit; using ObjectPtr = LangToolkit::ObjectPtr; - + // Constructs a "null" placeholder instance ObjectBase() = default; @@ -63,10 +63,10 @@ namespace db0 initNew(fixture, std::forward(args)...); } - // Open an existing instance + // Fetch an existing instance struct tag_from_address {}; - ObjectBase(tag_from_address, db0::swine_ptr &fixture, Address address) - : has_fixture(typename has_fixture::tag_from_address(), fixture, address) + ObjectBase(tag_from_address, db0::swine_ptr &fixture, Address address, AccessFlags access_mode = {}) + : has_fixture(typename has_fixture::tag_from_address(), fixture, address, 0, access_mode) { m_gc_registered = tryAddToGC0(*fixture, this); } @@ -203,6 +203,11 @@ namespace db0 new ((void*)this) T(); } + // Get access flags to propagate to members (e.g. no_cache) + AccessFlags getMemberFlags() const { + return this->v_this.getAccessMode() & AccessOptions::no_cache; + } + protected: friend class db0::GC0; @@ -214,9 +219,9 @@ namespace db0 this->modify().m_header.m_instance_id = instance_id; } else { has_fixture::init(fixture, std::forward(args)...); - } + } } - + // member should be overridden for derived types which need pre-commit using PreCommitFunction = void (*)(void *, bool revert); static PreCommitFunction getPreCommitFunction() { diff --git a/src/dbzero/object_model/class/Class.cpp b/src/dbzero/object_model/class/Class.cpp index f7a32082..1c8fb710 100644 --- a/src/dbzero/object_model/class/Class.cpp +++ b/src/dbzero/object_model/class/Class.cpp @@ -798,5 +798,9 @@ namespace db0::object_model assert(index < m_unique_keys.size()); return m_unique_keys[index]; } - + + void Class::setRuntimeFlags(FlagSet memo_options) { + m_no_cache = memo_options[MemoOptions::NO_CACHE]; + } + } diff --git a/src/dbzero/object_model/class/Class.hpp b/src/dbzero/object_model/class/Class.hpp index d0c1e3fb..2adfb4ef 100644 --- a/src/dbzero/object_model/class/Class.hpp +++ b/src/dbzero/object_model/class/Class.hpp @@ -54,7 +54,6 @@ namespace db0::object_model using VFidelityVector = db0::v_bvector >; DB0_PACKED_BEGIN - struct DB0_PACKED_ATTR o_class: public db0::o_fixed { // common object header @@ -81,8 +80,7 @@ DB0_PACKED_BEGIN const VFieldMatrix &, const VFidelityVector &, const Schema &, const char *type_id, const char *prefix_name, ClassFlags, std::uint32_t base_class_ref, std::uint32_t num_bases ); - }; - + }; DB0_PACKED_END // address <-> class_ref conversion functions @@ -105,11 +103,6 @@ DB0_PACKED_END static constexpr std::uint32_t SLOT_NUM = Fixture::TYPE_SLOT_NUM; static constexpr unsigned int PRIMARY_FIDELITY = 2; - // e.g. PyObject* - using LangToolkit = db0::python::PyToolkit; - using ObjectPtr = typename LangToolkit::ObjectPtr; - using ObjectSharedPtr = typename LangToolkit::ObjectSharedPtr; - struct Member { // primary field ID (primary key) @@ -252,7 +245,14 @@ DB0_PACKED_END // Get specific slot's fidelity (or 0 if not assigned) unsigned int getFidelity(std::uint32_t index) const; + + // Set / update only the runtime flags from the memo type decoration + void setRuntimeFlags(FlagSet); + inline bool isNoCache() const { + return m_no_cache; + } + protected: friend class ClassFactory; friend ClassPtr; @@ -305,6 +305,8 @@ DB0_PACKED_END std::unordered_set m_init_vars; const std::uint32_t m_uid = 0; mutable MemberCacheT m_member_cache; + // runtime flags + bool m_no_cache = false; // A function to retrieve the total number of instances of the schema std::function getTotalFunc() const; diff --git a/src/dbzero/object_model/class/ClassFactory.cpp b/src/dbzero/object_model/class/ClassFactory.cpp index 24f43be4..2dd140e8 100644 --- a/src/dbzero/object_model/class/ClassFactory.cpp +++ b/src/dbzero/object_model/class/ClassFactory.cpp @@ -153,6 +153,7 @@ 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 }; @@ -179,6 +180,9 @@ namespace db0::object_model m_class_ptr_index.insert(class_ptr); // registering type in the by-pointer cache (for accessing by-ClassPtr) type = this->getType(class_ptr, type, lang_type); + if (lang_type) { + type->setRuntimeFlags(LangToolkit::getMemoFlags(lang_type)); + } } it_cached = m_type_cache.insert({lang_type, type}).first; @@ -216,6 +220,8 @@ namespace db0::object_model } if (lang_type && !it_cached->second.m_lang_type) { it_cached->second.m_lang_type = lang_type; + it_cached->second.m_class->setInitVars(LangToolkit::getInitVars(lang_type)); + it_cached->second.m_class->setRuntimeFlags(LangToolkit::getMemoFlags(lang_type)); } return it_cached->second.m_class; } @@ -278,8 +284,9 @@ namespace db0::object_model lang_type = tryFindLangType(*type); } // initialize the language model - if (lang_type) { + if (lang_type) { type->setInitVars(LangToolkit::getInitVars(lang_type)); + type->setRuntimeFlags(LangToolkit::getMemoFlags(lang_type)); } // register the mapping to language specific type object it_cached = m_ptr_cache.insert({ptr, ClassItem { type, lang_type }}).first; @@ -287,8 +294,9 @@ namespace db0::object_model } // register the lang type mapping if missing if (lang_type && !it_cached->second.m_lang_type) { - it_cached->second.m_lang_type = lang_type; + it_cached->second.m_lang_type = lang_type; it_cached->second.m_class->setInitVars(LangToolkit::getInitVars(lang_type)); + it_cached->second.m_class->setRuntimeFlags(LangToolkit::getMemoFlags(lang_type)); } return it_cached->second; } diff --git a/src/dbzero/object_model/dict/Dict.cpp b/src/dbzero/object_model/dict/Dict.cpp index 35514bc6..5b4392c9 100644 --- a/src/dbzero/object_model/dict/Dict.cpp +++ b/src/dbzero/object_model/dict/Dict.cpp @@ -16,7 +16,7 @@ namespace db0::object_model GC0_Define(Dict) template o_typed_item createTypedItem(db0::swine_ptr &fixture, - typename LangToolkit::ObjectPtr lang_value) + typename LangToolkit::ObjectPtr lang_value, AccessFlags access_mode) { auto type_id = LangToolkit::getTypeManager().getTypeId(lang_value); // NOTE: packed storage not supported for dict keys/values @@ -30,30 +30,31 @@ namespace db0::object_model return { storage_class, - createMember(fixture, type_id, storage_class, lang_value) + createMember(fixture, type_id, storage_class, lang_value, access_mode) }; } template dict_item createDictItem(db0::swine_ptr &fixture, std::uint64_t hash, - o_typed_item key, o_typed_item value) + o_typed_item key, o_typed_item value, AccessFlags) { - DictIndex bindex(*fixture, o_pair_item(key, value) ); + // FIXME: currently unable to pass access flags since MorphingBIndex doesn't support it + DictIndex bindex(*fixture, o_pair_item(key, value)); return { hash, bindex }; } - Dict::Dict(db0::swine_ptr &fixture) - : super_t(fixture) + Dict::Dict(db0::swine_ptr &fixture, AccessFlags access_mode) + : super_t(fixture, access_mode) , m_index(*fixture) { modify().m_index_ptr = m_index.getAddress(); } - Dict::Dict(db0::swine_ptr &fixture, Address address) - : super_t(super_t::tag_from_address(), fixture, address) + Dict::Dict(db0::swine_ptr &fixture, Address address, AccessFlags access_mode) + : super_t(super_t::tag_from_address(), fixture, address, access_mode) , m_index(myPtr((*this)->m_index_ptr)) { } - + Dict::Dict(db0::swine_ptr &fixture, const Dict &dict) : super_t(fixture) , m_index(*fixture) @@ -97,18 +98,21 @@ namespace db0::object_model void Dict::setItem(FixtureLock &fixture, std::uint64_t key_hash, ObjectPtr key, ObjectPtr value) { using TypeId = db0::bindings::TypeId; - if(value == nullptr){ + if (value == nullptr) { // remove the item if value is nullptr (for e.g when del is called) pop(key_hash, key); restoreIterators(); return; } - auto key_item = createTypedItem(*fixture, key); - auto value_item = createTypedItem(*fixture, value); + auto member_flags = getMemberFlags(); + auto key_item = createTypedItem(*fixture, key, member_flags); + auto value_item = createTypedItem(*fixture, value, member_flags); auto it = m_index.find(key_hash); if (it == m_index.end()) { - m_index.insert(createDictItem(*fixture, key_hash, key_item, value_item)); + m_index.insert( + createDictItem(*fixture, key_hash, key_item, value_item, member_flags) + ); ++modify().m_size; } else { auto address = (*it).value; diff --git a/src/dbzero/object_model/dict/Dict.hpp b/src/dbzero/object_model/dict/Dict.hpp index c9f3eb25..991cc576 100644 --- a/src/dbzero/object_model/dict/Dict.hpp +++ b/src/dbzero/object_model/dict/Dict.hpp @@ -21,8 +21,6 @@ namespace db0::object_model { -DB0_PACKED_BEGIN - using Fixture = db0::Fixture; using PairItem_Address = ValueT_Address; // a MorphingBIndex derived collection type @@ -30,7 +28,8 @@ DB0_PACKED_BEGIN using DictIndex = CollectionIndex; using dict_item = db0::key_value >; class DictIterator; - + +DB0_PACKED_BEGIN struct DB0_PACKED_ATTR o_dict: public db0::o_fixed { // common object header @@ -43,6 +42,7 @@ DB0_PACKED_BEGIN return m_header.hasRefs(); } }; +DB0_PACKED_END class Dict: public db0::ObjectBase, StorageClass::DB0_DICT> { @@ -59,10 +59,10 @@ DB0_PACKED_BEGIN // as null placeholder Dict() = default; - explicit Dict(db0::swine_ptr &); + explicit Dict(db0::swine_ptr &, AccessFlags = {}); explicit Dict(db0::swine_ptr &fixture, const Dict &); explicit Dict(tag_no_gc, db0::swine_ptr &fixture, const Dict &); - Dict(db0::swine_ptr &, Address); + Dict(db0::swine_ptr &, Address, AccessFlags = {}); ~Dict(); void operator=(Dict &&); @@ -108,6 +108,4 @@ DB0_PACKED_BEGIN void restoreIterators(); }; -DB0_PACKED_END - } \ No newline at end of file diff --git a/src/dbzero/object_model/has_fixture.hpp b/src/dbzero/object_model/has_fixture.hpp index 75c9f9bd..22f000d6 100644 --- a/src/dbzero/object_model/has_fixture.hpp +++ b/src/dbzero/object_model/has_fixture.hpp @@ -32,8 +32,8 @@ namespace db0 // Open an existing instance // NOTE: we use tag_verified to avoid registering unverified instance with GC0 struct tag_from_address {}; - has_fixture(tag_from_address, db0::swine_ptr &fixture, Address address, std::size_t size_of = 0) - : BaseT(db0::tag_verified(), mptr(*fixture, address), size_of) + has_fixture(tag_from_address, db0::swine_ptr &fixture, Address address, std::size_t size_of = 0, AccessFlags access_mode = {}) + : BaseT(db0::tag_verified(), mptr(*fixture, address), size_of, access_mode) { // take weak ref of the Fixture fixture.take_weak(); diff --git a/src/dbzero/object_model/list/List.cpp b/src/dbzero/object_model/list/List.cpp index c1b9b7e8..1ad099f5 100644 --- a/src/dbzero/object_model/list/List.cpp +++ b/src/dbzero/object_model/list/List.cpp @@ -12,18 +12,19 @@ namespace db0::object_model 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) - { - return { storage_class, createMember(fixture, type_id, storage_class, lang_value) }; + db0::bindings::TypeId type_id, typename LangToolkit::ObjectPtr lang_value, + StorageClass storage_class, FlagSet access_mode) + { + return { storage_class, createMember(fixture, type_id, storage_class, lang_value, access_mode) }; } - List::List(db0::swine_ptr &fixture) - : super_t(fixture) + List::List(db0::swine_ptr &fixture, AccessFlags access_mode) + : super_t(fixture, access_mode) { } - List::List(db0::swine_ptr &fixture, Address address) - : super_t(super_t::tag_from_address(), fixture, address) + List::List(db0::swine_ptr &fixture, Address address, AccessFlags access_mode) + : super_t(super_t::tag_from_address(), fixture, address, access_mode) { } @@ -31,7 +32,7 @@ namespace db0::object_model : super_t(fixture, list) { } - + List::List(tag_no_gc, db0::swine_ptr &fixture, const List &list) : super_t(tag_no_gc(), fixture, list) { @@ -59,7 +60,7 @@ namespace db0::object_model } v_bvector::push_back( - createListItem(*fixture, type_id, *lang_value, storage_class) + createListItem(*fixture, type_id, *lang_value, storage_class, getMemberFlags()) ); restoreIterators(); } @@ -88,7 +89,7 @@ namespace db0::object_model restoreIterators(); return member; } - + void List::setItem(FixtureLock &fixture, std::size_t i, ObjectPtr lang_value) { if (i >= size()) { @@ -107,10 +108,10 @@ namespace db0::object_model } auto [storage_class_value, value] = (*this)[i]; - v_bvector::setItem(i, createListItem(*fixture, type_id, lang_value, storage_class)); + v_bvector::setItem(i, createListItem(*fixture, type_id, lang_value, storage_class, getMemberFlags())); unrefMember(*fixture, storage_class_value, value); } - + List * List::copy(void *at_ptr, db0::swine_ptr &fixture) const { return new (at_ptr) List(fixture, *this); } diff --git a/src/dbzero/object_model/list/List.hpp b/src/dbzero/object_model/list/List.hpp index 3bf330b2..71cc34e4 100644 --- a/src/dbzero/object_model/list/List.hpp +++ b/src/dbzero/object_model/list/List.hpp @@ -39,10 +39,10 @@ namespace db0::object_model // as null placeholder List() = default; - List(db0::swine_ptr &); + List(db0::swine_ptr &, AccessFlags = {}); List(db0::swine_ptr &, const List &); List(tag_no_gc, db0::swine_ptr &, const List &); - List(db0::swine_ptr &, Address); + List(db0::swine_ptr &, Address, AccessFlags = {}); ~List(); void append(FixtureLock &, ObjectSharedPtr lang_value); diff --git a/src/dbzero/object_model/object/Object.cpp b/src/dbzero/object_model/object/Object.cpp index 155ffa09..9e813634 100644 --- a/src/dbzero/object_model/object/Object.cpp +++ b/src/dbzero/object_model/object/Object.cpp @@ -18,6 +18,10 @@ namespace db0::object_model GC0_Define(Object) ObjectInitializerManager Object::m_init_manager; + FlagSet getAccessOptions(const Class &type) { + return type.isNoCache() ? FlagSet { AccessOptions::no_cache } : FlagSet {}; + } + bool isEqual(const KV_Index *kv_ptr_1, const KV_Index *kv_ptr_2) { if (!kv_ptr_1) { @@ -121,11 +125,12 @@ namespace db0::object_model // prepare for initialization m_init_manager.addInitializer(*this, std::move(type_initializer)); } - + Object::Object(db0::swine_ptr &fixture, std::shared_ptr type, - std::pair ref_counts, const PosVT::Data &pos_vt_data, unsigned int pos_vt_offset) + std::pair ref_counts, const PosVT::Data &pos_vt_data, unsigned int pos_vt_offset) : super_t(fixture, type->getClassRef(), ref_counts, - safeCast(type->getNumBases() + 1, "Too many base classes"), pos_vt_data, pos_vt_offset) + safeCast(type->getNumBases() + 1, "Too many base classes"), pos_vt_data, pos_vt_offset, nullptr, nullptr, + getAccessOptions(*type)) , m_type(type) { } @@ -211,7 +216,8 @@ namespace db0::object_model assert(m_type); super_t::init(*fixture, m_type->getClassRef(), initializer.getRefCounts(), safeCast(m_type->getNumBases() + 1, "Too many base classes"), - pos_vt_data, pos_vt_offset, index_vt_data.first, index_vt_data.second + pos_vt_data, pos_vt_offset, index_vt_data.first, index_vt_data.second, + getAccessOptions(*m_type) ); // reference associated class @@ -311,10 +317,12 @@ namespace db0::object_model // remove any existing lo-fi initialization auto loc = member_id.get(2).getIndexAndOffset(); initializer.remove(loc, lofi_store<2>::mask(loc.second)); - } + } // register a regular member with the initializer - initializer.set(member_id.get(0).getIndexAndOffset(), storage_class, - createMember(fixture, type_id, storage_class, obj_ptr) + // NOTE: a new member receives the no-cache flag if set (at the type level) + auto member_flags = type.isNoCache() ? AccessFlags { AccessOptions::no_cache } : AccessFlags(); + initializer.set(member_id.get(0).getIndexAndOffset(), storage_class, + createMember(fixture, type_id, storage_class, obj_ptr, member_flags) ); } else { if (member_id.hasFidelity(0)) { @@ -325,7 +333,9 @@ namespace db0::object_model // For now only fidelity == 2 is supported (lo-fi storage) assert(storage_fidelity == 2); auto loc = member_id.get(storage_fidelity).getIndexAndOffset(); - auto value = lofi_store<2>::create(loc.second, createMember(fixture, type_id, storage_class, obj_ptr).m_store); + // no access flags for lo-fi members + auto value = lofi_store<2>::create(loc.second, + createMember(fixture, type_id, storage_class, obj_ptr, {}).m_store); // register a lo-fi member with the initializer (using mask) initializer.set(loc, storage_class, value, lofi_store<2>::mask(loc.second)); } @@ -778,10 +788,11 @@ namespace db0::object_model member_id = m_type->addField(field_name, storage_fidelity); field_id = member_id.get(storage_fidelity); } - + assert(field_id && member_id); + // NOTE: a new member inherits the parent's no-cache flag // FIXME: value should be destroyed on exception - auto value = createMember(*fixture, type_id, storage_class, lang_value); + auto value = createMember(*fixture, type_id, storage_class, lang_value, getMemberFlags()); // make sure object address is not null assert(!(storage_class == StorageClass::OBJECT_REF && value.cast() == 0)); @@ -1597,7 +1608,7 @@ namespace db0::object_model m_touched = true; } } - + void Object::addExtRef() const { ++m_ext_refs; } @@ -1607,5 +1618,5 @@ namespace db0::object_model assert(m_ext_refs > 0); --m_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 d663bf61..4b426c32 100644 --- a/src/dbzero/object_model/object/Object.hpp +++ b/src/dbzero/object_model/object/Object.hpp @@ -26,11 +26,10 @@ namespace db0::object_model { -DB0_PACKED_BEGIN - class Class; using Fixture = db0::Fixture; - + +DB0_PACKED_BEGIN class DB0_PACKED_ATTR o_object: public db0::o_base { protected: @@ -77,8 +76,9 @@ DB0_PACKED_BEGIN void incRef(bool is_tag); bool hasRefs() const; bool hasAnyRefs() const; - }; - + }; +DB0_PACKED_END + struct FieldLayout { std::vector m_pos_vt_fields; @@ -269,7 +269,7 @@ DB0_PACKED_BEGIN void addExtRef() const; void removeExtRef() const; - + inline std::uint32_t getExtRefs() const { return m_ext_refs; } @@ -384,8 +384,6 @@ DB0_PACKED_BEGIN std::unordered_set &) const; }; -DB0_PACKED_END - } DECLARE_ENUM_VALUES(db0::object_model::ObjectOptions, 2) \ No newline at end of file diff --git a/src/dbzero/object_model/object/Options.cpp b/src/dbzero/object_model/object/Options.cpp new file mode 100644 index 00000000..c977faee --- /dev/null +++ b/src/dbzero/object_model/object/Options.cpp @@ -0,0 +1,3 @@ +#include "Options.hpp" + +DEFINE_ENUM_VALUES(db0::object_model::MemoOptions, "NO_DEFAULT_TAGS", "NO_CACHE") diff --git a/src/dbzero/object_model/object/Options.hpp b/src/dbzero/object_model/object/Options.hpp new file mode 100644 index 00000000..cc0ae8ee --- /dev/null +++ b/src/dbzero/object_model/object/Options.hpp @@ -0,0 +1,22 @@ +#pragma once + +#include +#include + +namespace db0::object_model + +{ + + enum MemoOptions: std::uint16_t + { + // instances of this type opted out of auto-assigned type tags + NO_DEFAULT_TAGS = 0x0001, + // instances of this type opted out of caching + NO_CACHE = 0x0002 + }; + + using MemoFlags = db0::FlagSet; + +} + +DECLARE_ENUM_VALUES(db0::object_model::MemoOptions, 2) \ No newline at end of file diff --git a/src/dbzero/object_model/object/ValueTable.hpp b/src/dbzero/object_model/object/ValueTable.hpp index edd5aa3d..cdcfdd73 100644 --- a/src/dbzero/object_model/object/ValueTable.hpp +++ b/src/dbzero/object_model/object/ValueTable.hpp @@ -13,7 +13,6 @@ namespace db0::object_model { DB0_PACKED_BEGIN - /** * Positionally-encoded value table */ @@ -86,10 +85,12 @@ DB0_PACKED_BEGIN bool operator==(const PosVT &other) const; }; - +DB0_PACKED_END + /** * Indexed value table */ +DB0_PACKED_BEGIN class DB0_PACKED_ATTR IndexVT: public o_base { protected: diff --git a/src/dbzero/object_model/object_header.hpp b/src/dbzero/object_model/object_header.hpp index 446f100d..31a3ee7d 100644 --- a/src/dbzero/object_model/object_header.hpp +++ b/src/dbzero/object_model/object_header.hpp @@ -10,11 +10,10 @@ namespace db0 { -DB0_PACKED_BEGIN - class Fixture; - + /// Common object header +DB0_PACKED_BEGIN struct DB0_PACKED_ATTR o_object_header: public o_fixed { using RefCounterT = o_ref_counter; @@ -40,7 +39,9 @@ DB0_PACKED_BEGIN // check if any references exist (including auto-assigned type tags) bool hasRefs() const; }; - +DB0_PACKED_END + +DB0_PACKED_BEGIN // Unique header for objects with unique instance id struct DB0_PACKED_ATTR o_unique_header: public o_fixed_ext { @@ -58,7 +59,6 @@ DB0_PACKED_BEGIN { } }; - DB0_PACKED_END - + } \ No newline at end of file diff --git a/src/dbzero/object_model/set/Set.cpp b/src/dbzero/object_model/set/Set.cpp index d23e7355..ac57e576 100644 --- a/src/dbzero/object_model/set/Set.cpp +++ b/src/dbzero/object_model/set/Set.cpp @@ -16,15 +16,17 @@ namespace db0::object_model GC0_Define(Set) template o_typed_item createTypedItem(db0::swine_ptr &fixture, - db0::bindings::TypeId type_id, typename LangToolkit::ObjectPtr lang_value, StorageClass storage_class) + db0::bindings::TypeId type_id, typename LangToolkit::ObjectPtr lang_value, StorageClass storage_class, AccessFlags access_mode) { - return { storage_class, createMember(fixture, type_id, storage_class, lang_value) }; + return { storage_class, createMember(fixture, type_id, storage_class, lang_value, access_mode) }; } template set_item createSetItem(db0::swine_ptr &fixture, std::uint64_t key, - db0::bindings::TypeId type_id, typename LangToolkit::ObjectPtr lang_value, StorageClass storage_class) + db0::bindings::TypeId type_id, typename LangToolkit::ObjectPtr lang_value, StorageClass storage_class, AccessFlags access_mode) { - auto item = createTypedItem(fixture, type_id, lang_value, storage_class); + auto item = createTypedItem( + fixture, type_id, lang_value, storage_class, access_mode + ); SetIndex bindex(*fixture, item); return { key, bindex }; } @@ -111,7 +113,9 @@ namespace db0::object_model bool is_modified = false; if (iter == m_index.end()) { ++modify().m_size; - auto set_it = createSetItem(fixture, key, type_id, lang_value, storage_class); + auto set_it = createSetItem( + fixture, key, type_id, lang_value, storage_class, getMemberFlags() + ); m_index.insert(set_it); is_modified = true; } else { @@ -120,7 +124,9 @@ namespace db0::object_model ++modify().m_size; auto [key, address] = *iter; auto bindex = address.getIndex(*fixture); - auto item = createTypedItem(fixture, type_id, lang_value, storage_class); + auto item = createTypedItem( + fixture, type_id, lang_value, storage_class, getMemberFlags() + ); bindex.insert(item); if (bindex.getAddress() != address.m_index_address) { // auto new_typed_index = TypedIndex(new_address, bindex.getIndexType()); diff --git a/src/dbzero/object_model/tuple/Tuple.cpp b/src/dbzero/object_model/tuple/Tuple.cpp index 1ceeaf1a..2810746e 100644 --- a/src/dbzero/object_model/tuple/Tuple.cpp +++ b/src/dbzero/object_model/tuple/Tuple.cpp @@ -34,13 +34,14 @@ namespace db0::object_model } template o_typed_item createTupleItem(db0::swine_ptr &fixture, - db0::bindings::TypeId type_id, typename LangToolkit::ObjectPtr lang_value, StorageClass storage_class) + db0::bindings::TypeId type_id, typename LangToolkit::ObjectPtr lang_value, + StorageClass storage_class, FlagSet access_mode) { - return { storage_class, createMember(fixture, type_id, storage_class, lang_value) }; + return { storage_class, createMember(fixture, type_id, storage_class, lang_value, access_mode) }; } - Tuple::Tuple(db0::swine_ptr &fixture, tag_new_tuple, std::size_t size) - : super_t(fixture, size) + Tuple::Tuple(db0::swine_ptr &fixture, tag_new_tuple, std::size_t size, AccessFlags access_mode) + : super_t(fixture, size, access_mode) { } @@ -53,8 +54,8 @@ namespace db0::object_model } } - Tuple::Tuple(db0::swine_ptr &fixture, Address address) - : super_t(super_t::tag_from_address(), fixture, address) + Tuple::Tuple(db0::swine_ptr &fixture, Address address, AccessFlags access_mode) + : super_t(super_t::tag_from_address(), fixture, address, access_mode) { } @@ -91,7 +92,9 @@ namespace db0::object_model storage_class = db0::getStorageClass(pre_storage_class); } - modify().items()[i] = createTupleItem(*fixture, type_id, *lang_value, storage_class); + modify().items()[i] = createTupleItem( + *fixture, type_id, *lang_value, storage_class, getMemberFlags() + ); } std::size_t Tuple::count(ObjectPtr lang_value) const @@ -106,7 +109,7 @@ namespace db0::object_model } return count; } - + std::size_t Tuple::index(ObjectPtr lang_value) const { std::size_t index = 0; diff --git a/src/dbzero/object_model/tuple/Tuple.hpp b/src/dbzero/object_model/tuple/Tuple.hpp index 7035ab44..2aa1d241 100644 --- a/src/dbzero/object_model/tuple/Tuple.hpp +++ b/src/dbzero/object_model/tuple/Tuple.hpp @@ -20,11 +20,11 @@ namespace db0::object_model { -DB0_PACKED_BEGIN - using Fixture = db0::Fixture; + using AccessFlags = db0::AccessFlags; class TupleIterator; +DB0_PACKED_BEGIN class DB0_PACKED_ATTR o_tuple: public o_base { protected: @@ -62,6 +62,7 @@ DB0_PACKED_BEGIN return m_header.hasRefs(); } }; +DB0_PACKED_END class Tuple: public db0::ObjectBase, StorageClass::DB0_TUPLE> { @@ -77,11 +78,11 @@ DB0_PACKED_BEGIN // as null placeholder Tuple() = default; struct tag_new_tuple {}; - explicit Tuple(db0::swine_ptr &, tag_new_tuple, std::size_t size); + explicit Tuple(db0::swine_ptr &, tag_new_tuple, std::size_t size, AccessFlags = {}); explicit Tuple(tag_no_gc, db0::swine_ptr &, const Tuple &); - explicit Tuple(db0::swine_ptr &, Address address); + explicit Tuple(db0::swine_ptr &, Address address, AccessFlags = {}); ~Tuple(); - + ObjectSharedPtr getItem(std::size_t i) const; void setItem(FixtureLock &, std::size_t i, ObjectSharedPtr lang_value); @@ -103,7 +104,5 @@ DB0_PACKED_BEGIN std::shared_ptr getIterator(ObjectPtr lang_tuple) const; }; - -DB0_PACKED_END } \ No newline at end of file diff --git a/src/dbzero/object_model/value/Member.cpp b/src/dbzero/object_model/value/Member.cpp index 2c74e1a5..4ff265f1 100644 --- a/src/dbzero/object_model/value/Member.cpp +++ b/src/dbzero/object_model/value/Member.cpp @@ -31,7 +31,7 @@ namespace db0::object_model // INTEGER specialization template <> Value createMember(db0::swine_ptr &fixture, - PyObjectPtr obj_ptr, StorageClass) + PyObjectPtr obj_ptr, StorageClass, AccessFlags) { auto int_value = PyLong_AsLongLong(obj_ptr); return db0::binary_cast()(int_value); @@ -39,7 +39,7 @@ namespace db0::object_model // FLOAT specialization template <> Value createMember(db0::swine_ptr &fixture, - PyObjectPtr obj_ptr, StorageClass) + PyObjectPtr obj_ptr, StorageClass, AccessFlags) { auto fp_value = PyFloat_AsDouble(obj_ptr); return db0::binary_cast()(fp_value); @@ -47,15 +47,15 @@ namespace db0::object_model // STRING specialization template <> Value createMember(db0::swine_ptr &fixture, - PyObjectPtr obj_ptr, StorageClass) + PyObjectPtr obj_ptr, StorageClass, AccessFlags access_mode) { // create string-ref member and take its address - return db0::v_object(*fixture, PyUnicode_AsUTF8(obj_ptr)).getAddress(); + return db0::v_object(*fixture, PyUnicode_AsUTF8(obj_ptr), access_mode).getAddress(); } // OBJECT specialization template <> Value createMember(db0::swine_ptr &fixture, - PyObjectPtr obj_ptr, StorageClass) + PyObjectPtr obj_ptr, StorageClass, AccessFlags) { auto &obj = PyToolkit::getTypeManager().extractMutableObject(obj_ptr); assert(obj.hasInstance()); @@ -66,7 +66,7 @@ namespace db0::object_model // LIST specialization template <> Value createMember(db0::swine_ptr &fixture, - PyObjectPtr obj_ptr, StorageClass) + PyObjectPtr obj_ptr, StorageClass, AccessFlags) { auto &list = PyToolkit::getTypeManager().extractMutableList(obj_ptr); assureSameFixture(fixture, list); @@ -76,7 +76,7 @@ namespace db0::object_model // INDEX specialization template <> Value createMember(db0::swine_ptr &fixture, - PyObjectPtr obj_ptr, StorageClass) + PyObjectPtr obj_ptr, StorageClass, AccessFlags) { auto &index = PyToolkit::getTypeManager().extractMutableIndex(obj_ptr); assureSameFixture(fixture, index); @@ -86,7 +86,7 @@ namespace db0::object_model // SET specialization template <> Value createMember(db0::swine_ptr &fixture, - PyObjectPtr obj_ptr, StorageClass) + PyObjectPtr obj_ptr, StorageClass, AccessFlags) { auto &set = PyToolkit::getTypeManager().extractMutableSet(obj_ptr); assureSameFixture(fixture, set); @@ -96,7 +96,7 @@ namespace db0::object_model // DB0 DICT specialization template <> Value createMember(db0::swine_ptr &fixture, - PyObjectPtr obj_ptr, StorageClass) + PyObjectPtr obj_ptr, StorageClass, AccessFlags) { auto &dict = PyToolkit::getTypeManager().extractMutableDict(obj_ptr); assureSameFixture(fixture, dict); @@ -106,7 +106,7 @@ namespace db0::object_model // TUPLE specialization template <> Value createMember(db0::swine_ptr &fixture, - PyObjectPtr obj_ptr, StorageClass) + PyObjectPtr obj_ptr, StorageClass, AccessFlags) { auto &tuple = PyToolkit::getTypeManager().extractMutableTuple(obj_ptr); assureSameFixture(fixture, tuple); @@ -116,9 +116,9 @@ namespace db0::object_model // LIST specialization template <> Value createMember(db0::swine_ptr &fixture, - PyObjectPtr obj_ptr, StorageClass) + PyObjectPtr obj_ptr, StorageClass, AccessFlags access_mode) { - auto list_ptr = db0::python::tryMake_DB0List(fixture, &obj_ptr, 1); + auto list_ptr = db0::python::tryMake_DB0List(fixture, &obj_ptr, 1, access_mode); if (!list_ptr) { THROWF(db0::InputException) << "Failed to create list" << THROWF_END; } @@ -128,9 +128,9 @@ namespace db0::object_model // SET specialization template <> Value createMember(db0::swine_ptr &fixture, - PyObjectPtr obj_ptr, StorageClass) + PyObjectPtr obj_ptr, StorageClass, AccessFlags access_mode) { - auto set = db0::python::tryMake_DB0Set(fixture, &obj_ptr, 1); + auto set = db0::python::tryMake_DB0Set(fixture, &obj_ptr, 1, access_mode); if (!set) { THROWF(db0::InputException) << "Failed to create set" << THROWF_END; } @@ -140,11 +140,11 @@ namespace db0::object_model // DICT specialization template <> Value createMember(db0::swine_ptr &fixture, - PyObjectPtr obj_ptr, StorageClass) + PyObjectPtr obj_ptr, StorageClass, AccessFlags access_mode) { auto args = Py_OWN(PyTuple_New(1)); PySafeTuple_SetItem(*args, 0, Py_BORROW(obj_ptr)); - auto dict = db0::python::tryMake_DB0Dict(fixture, *args, nullptr); + auto dict = db0::python::tryMake_DB0Dict(fixture, *args, nullptr, access_mode); if (!dict) { THROWF(db0::InputException) << "Failed to create dict" << THROWF_END; } @@ -154,91 +154,93 @@ namespace db0::object_model // TUPLE specialization template <> Value createMember(db0::swine_ptr &fixture, - PyObjectPtr obj_ptr, StorageClass) + PyObjectPtr obj_ptr, StorageClass, AccessFlags access_mode) { - auto tuple = db0::python::tryMake_DB0Tuple(fixture, &obj_ptr, 1); + auto tuple = db0::python::tryMake_DB0Tuple(fixture, &obj_ptr, 1, access_mode); tuple.get()->modifyExt().incRef(false); return tuple.get()->ext().getAddress(); } // DATETIME with TIMEZONE specialization template <> Value createMember(db0::swine_ptr &fixture, - PyObjectPtr obj_ptr, StorageClass) + PyObjectPtr obj_ptr, StorageClass, AccessFlags) { return db0::python::pyDateTimeWithTzToUint64(obj_ptr); } // DATETIME specialization template <> Value createMember(db0::swine_ptr &fixture, - PyObjectPtr obj_ptr, StorageClass) + PyObjectPtr obj_ptr, StorageClass, AccessFlags) { return db0::python::pyDateTimeToToUint64(obj_ptr); } - // DATE specialization + // DATE specialization template <> Value createMember(db0::swine_ptr &fixture, - PyObjectPtr obj_ptr, StorageClass) + PyObjectPtr obj_ptr, StorageClass, AccessFlags) { return db0::python::pyDateToUint64(obj_ptr); } // TIME specialization template <> Value createMember(db0::swine_ptr &fixture, - PyObjectPtr obj_ptr, StorageClass) + PyObjectPtr obj_ptr, StorageClass, AccessFlags) { return db0::python::pyTimeToUint64(obj_ptr); } // TIME wit TIMEZONE specialization template <> Value createMember(db0::swine_ptr &fixture, - PyObjectPtr obj_ptr, StorageClass) + PyObjectPtr obj_ptr, StorageClass, AccessFlags) { return db0::python::pyTimeWithTzToUint64(obj_ptr); } // DECIMAL specialization template <> Value createMember(db0::swine_ptr &fixture, - PyObjectPtr obj_ptr, StorageClass) + PyObjectPtr obj_ptr, StorageClass, AccessFlags) { return db0::python::pyDecimalToUint64(obj_ptr); } - Value createBytesMember(db0::swine_ptr &fixture, const std::byte *bytes, std::size_t size) { + Value createBytesMember(db0::swine_ptr &fixture, const std::byte *bytes, std::size_t size, + AccessFlags access_mode) + { // FIXME: implement as ObjectBase and incRef - return db0::v_object(*fixture, bytes, size).getAddress(); + return db0::v_object(*fixture, bytes, size, access_mode).getAddress(); } // BYTES specialization template <> Value createMember(db0::swine_ptr &fixture, - PyObjectPtr obj_ptr, StorageClass) + PyObjectPtr obj_ptr, StorageClass, AccessFlags access_mode) { auto size = PyBytes_GET_SIZE(obj_ptr); auto safe_str = PyBytes_AsString(obj_ptr); - return createBytesMember(fixture, reinterpret_cast(safe_str), size); + return createBytesMember(fixture, reinterpret_cast(safe_str), size, access_mode); } // NONE specialization template <> Value createMember(db0::swine_ptr &fixture, - PyObjectPtr obj_ptr, StorageClass) + PyObjectPtr obj_ptr, StorageClass, AccessFlags) { return Value::NONE; } // OBJECT_ITERABLE specialization (serialized member) template <> Value createMember(db0::swine_ptr &fixture, - PyObjectPtr obj_ptr, StorageClass) + PyObjectPtr obj_ptr, StorageClass, AccessFlags access_mode) { auto &obj_iter = PyToolkit::getTypeManager().extractObjectIterable(obj_ptr); std::vector bytes; // put TypeId as a header db0::serial::write(bytes, TypeId::OBJECT_ITERABLE); obj_iter.serialize(bytes); - return createBytesMember(fixture, bytes.data(), bytes.size()); + return createBytesMember(fixture, bytes.data(), bytes.size(), access_mode); } // ENUM value specialization (serialized member) template <> Value createMember(db0::swine_ptr &fixture, - PyObjectPtr obj_ptr, StorageClass) + PyObjectPtr obj_ptr, StorageClass, AccessFlags) { auto enum_value = PyToolkit::getTypeManager().extractEnumValue(obj_ptr); // make sure value from the same Fixture is assigned @@ -252,7 +254,7 @@ namespace db0::object_model // ENUM value-repr specialization (serialized member) template <> Value createMember(db0::swine_ptr &fixture, - PyObjectPtr obj_ptr, StorageClass) + PyObjectPtr obj_ptr, StorageClass, AccessFlags) { auto &enum_value_repr = PyToolkit::getTypeManager().extractEnumValueRepr(obj_ptr); // convert enum value-repr to enum value @@ -261,15 +263,15 @@ namespace db0::object_model } template <> Value createMember(db0::swine_ptr &fixture, - PyObjectPtr obj_ptr, StorageClass) - { + PyObjectPtr obj_ptr, StorageClass, AccessFlags) + { // irrespective of the storage class return obj_ptr == Py_True ? Value::TRUE : Value::FALSE; } // DB0_BYTES_ARRAY specialization template <> Value createMember(db0::swine_ptr &fixture, - PyObjectPtr obj_ptr, StorageClass) + PyObjectPtr obj_ptr, StorageClass, AccessFlags) { auto &byte_array = PyToolkit::getTypeManager().extractMutableByteArray(obj_ptr); assureSameFixture(fixture, byte_array); @@ -279,7 +281,7 @@ namespace db0::object_model // DB0_WEAK_PROXY specialization template <> Value createMember(db0::swine_ptr &fixture, - PyObjectPtr obj_ptr, StorageClass storage_class) + PyObjectPtr obj_ptr, StorageClass storage_class, AccessFlags) { // NOTE: memo object can be extracted from the weak proxy const auto &obj = PyToolkit::getTypeManager().extractObject(obj_ptr); @@ -294,7 +296,7 @@ namespace db0::object_model // MEMO_TYPE specialization template <> Value createMember(db0::swine_ptr &fixture, - PyObjectPtr obj_ptr, StorageClass) + PyObjectPtr obj_ptr, StorageClass, AccessFlags) { const auto &type_manager = PyToolkit::getTypeManager(); auto lang_type = type_manager.getTypeObject(obj_ptr); @@ -323,7 +325,7 @@ namespace db0::object_model } template <> void registerCreateMemberFunctions( - std::vector &, PyObjectPtr, StorageClass)> &functions) + std::vector &, PyObjectPtr, StorageClass, AccessFlags)> &functions) { functions.resize(static_cast(TypeId::COUNT)); std::fill(functions.begin(), functions.end(), nullptr); @@ -419,7 +421,7 @@ namespace db0::object_model { return PyToolkit::unloadDict(fixture, value.asAddress()); } - + // BYTES specialization template <> typename PyToolkit::ObjectSharedPtr unloadMember( db0::swine_ptr &fixture, Value value, unsigned int) @@ -647,11 +649,11 @@ namespace db0::object_model if (object.decRef(false)) { // NOTE: we'll drop the object immediately on condition it has no language references if (!LangToolkit::hasLangRefs(*obj_ptr)) { - auto unique_addr = object.getUniqueAddress(); + auto unique_addr = object.getUniqueAddress(); // drop dbzero instance, replacing it with a "null" placeholder object.dropInstance(lock); - // might also be removed from lang cache - fixture->getLangCache().erase(unique_addr); + // might also be removed from lang cache + fixture->getLangCache().erase(unique_addr); } } } else { diff --git a/src/dbzero/object_model/value/Member.hpp b/src/dbzero/object_model/value/Member.hpp index 183b3a4f..e7230086 100644 --- a/src/dbzero/object_model/value/Member.hpp +++ b/src/dbzero/object_model/value/Member.hpp @@ -24,19 +24,20 @@ namespace db0::object_model using TypeId = db0::bindings::TypeId; using PyToolkit = db0::python::PyToolkit; using PyObjectPtr = PyToolkit::ObjectPtr; + using AccessFlags = db0::AccessFlags; template Value createMember(db0::swine_ptr &fixture, - typename LangToolkit::ObjectPtr obj_ptr, StorageClass); - - // register TypeId specialized functions + typename LangToolkit::ObjectPtr obj_ptr, StorageClass, AccessFlags); + + // Register TypeId specialized functions template void registerCreateMemberFunctions( - std::vector &, typename LangToolkit::ObjectPtr, StorageClass)> &functions); + std::vector &, typename LangToolkit::ObjectPtr, StorageClass, AccessFlags)> &functions); template Value createMember(db0::swine_ptr &fixture, - TypeId type_id, StorageClass storage_class, typename LangToolkit::ObjectPtr obj_ptr) + TypeId type_id, StorageClass storage_class, typename LangToolkit::ObjectPtr obj_ptr, AccessFlags access_mode) { // create member function pointer - using CreateMemberFunc = Value (*)(db0::swine_ptr &, typename LangToolkit::ObjectPtr, StorageClass); + using CreateMemberFunc = Value (*)(db0::swine_ptr &, typename LangToolkit::ObjectPtr, StorageClass, AccessFlags); static std::vector create_member_functions; if (create_member_functions.empty()) { registerCreateMemberFunctions(create_member_functions); @@ -47,7 +48,7 @@ namespace db0::object_model if (!func_ptr) { THROWF(db0::InternalException) << "Value of TypeID: " << (int)type_id << " cannot be converted to a member" << THROWF_END; } - return func_ptr(fixture, obj_ptr, storage_class); + return func_ptr(fixture, obj_ptr, storage_class, access_mode); } template typename LangToolkit::ObjectSharedPtr unloadMember( diff --git a/tests/unit_tests/CapacityTreeTest.cpp b/tests/unit_tests/CapacityTreeTest.cpp index b4226bad..6e8ad2f4 100644 --- a/tests/unit_tests/CapacityTreeTest.cpp +++ b/tests/unit_tests/CapacityTreeTest.cpp @@ -57,7 +57,7 @@ namespace tests realms.emplace_back(m_bitspace, page_size); realms.emplace_back(m_bitspace, page_size); auto data = db0::tests::getCPData(); - for (const auto &item: data) { + for (const auto &item: data) { // process line unsigned int op_code = std::get<0>(item); unsigned int realm_id = std::get<1>(item); diff --git a/tests/unit_tests/MetaAllocatorTest.cpp b/tests/unit_tests/MetaAllocatorTest.cpp index 880e06a6..3ee14587 100644 --- a/tests/unit_tests/MetaAllocatorTest.cpp +++ b/tests/unit_tests/MetaAllocatorTest.cpp @@ -15,6 +15,21 @@ namespace tests using namespace db0; + // a proxy class to expose protected members for testing + class MetaAllocatorProxy: public MetaAllocator + { + public: + template + MetaAllocatorProxy(Args&&... args) + : MetaAllocator(std::forward(args)...) + { + } + + std::uint32_t getSlabId(Address address) const { + return MetaAllocator::getSlabId(address); + } + }; + class MetaAllocatorTests: public testing::Test { public: @@ -128,7 +143,7 @@ namespace tests { // first assign from a new slab - MetaAllocator cut(m_prefix); + MetaAllocatorProxy cut(m_prefix); std::vector alloc_sizes = { 100, 200, 300, 400, 500, 600, 700, 800, 900 }; for (auto alloc_size: alloc_sizes) { auto ptr = cut.alloc(alloc_size); @@ -137,7 +152,7 @@ namespace tests } // open again and try to allocate - MetaAllocator cut(m_prefix); + MetaAllocatorProxy cut(m_prefix); auto ptr = cut.alloc(100); // the allocation should be in the same slab ASSERT_EQ(cut.getSlabId(ptr), 0); @@ -157,7 +172,7 @@ namespace tests } // open again and try to allocate - MetaAllocator cut(m_prefix); + MetaAllocatorProxy cut(m_prefix); auto ptr = cut.alloc(100); // the allocation should be from the other slab since the 1st is full ASSERT_TRUE(cut.getSlabId(ptr) > 0); @@ -169,7 +184,7 @@ namespace tests SlabRecycler recycler; { // make allocations until the 2 slabs are occupied - MetaAllocator cut(m_prefix, &recycler); + MetaAllocatorProxy cut(m_prefix, &recycler); std::size_t total_allocated = 0; std::vector slab_ids; while (cut.getSlabCount() < 2) { @@ -210,7 +225,7 @@ namespace tests std::vector slab_ids; { // make allocations until the 2 slabs are occupied - MetaAllocator cut(m_prefix, &recycler); + MetaAllocatorProxy cut(m_prefix, &recycler); while (cut.getSlabCount() < 2) { auto ptr = cut.alloc(100); auto slab_id = cut.getSlabId(ptr); @@ -291,7 +306,7 @@ namespace tests auto count = 10000; { // deferred free is disabled - MetaAllocator cut(m_prefix, &recycler, false); + MetaAllocatorProxy cut(m_prefix, &recycler, false); // make random allocations for (int i = 0; i < count; ++i) { auto alloc_size = rand() % 1000 + 1; @@ -372,5 +387,22 @@ namespace tests MetaAllocator cut(m_prefix, &recycler); ASSERT_EQ(cut.alloc(8), cut.getFirstAddress()); } + + TEST_F( MetaAllocatorTests , testMetaAllocatorLocalityAwareAllocation ) + { + MetaAllocator::formatPrefix(m_prefix, PAGE_SIZE, SMALL_SLAB_SIZE); + SlabRecycler recycler; + MetaAllocatorProxy cut(m_prefix, &recycler); + // locality = 0 (default) + auto addr_0 = cut.alloc(8, 0, false, 0); + auto addr_1 = cut.alloc(8, 0, false, 0, 0); + // locality = 1 + auto addr_2 = cut.alloc(8, 0, false, 0, 1); + + // same slab + ASSERT_EQ(cut.getSlabId(addr_0), cut.getSlabId(addr_1)); + // different slabs + ASSERT_NE(cut.getSlabId(addr_0), cut.getSlabId(addr_2)); + } } \ No newline at end of file diff --git a/tests/unit_tests/SGB_TreeTests.cpp b/tests/unit_tests/SGB_TreeTests.cpp index 115085ce..d6f773e5 100644 --- a/tests/unit_tests/SGB_TreeTests.cpp +++ b/tests/unit_tests/SGB_TreeTests.cpp @@ -316,24 +316,24 @@ namespace tests SGB_TreeT::WindowT window; cut.lower_equal_window(7, window); ASSERT_TRUE(window[1].first); - ASSERT_EQ(*window[1].first, 6); + ASSERT_EQ(*window[1].first, 6u); ASSERT_TRUE(window[0].first); - ASSERT_EQ(*window[0].first, 3); + ASSERT_EQ(*window[0].first, 3u); ASSERT_TRUE(window[2].first); - ASSERT_EQ(*window[2].first, 9); + ASSERT_EQ(*window[2].first, 9u); cut.lower_equal_window(0, window); ASSERT_TRUE(window[1].first); - ASSERT_EQ(*window[1].first, 0); + ASSERT_EQ(*window[1].first, 0u); ASSERT_FALSE(window[0].first); ASSERT_TRUE(window[2].first); - ASSERT_EQ(*window[2].first, 3); + ASSERT_EQ(*window[2].first, 3u); } - + TEST_F( SGB_TreeTests , testSGBTreeFindLowerEqualFromTwoNodes ) { using SGB_TreeT = db0::SGB_Tree >; @@ -578,7 +578,7 @@ namespace tests } // store values in headers - int index = 0; + unsigned int index = 0; for (auto node = cut.cbegin_nodes(); node != cut.cend_nodes(); ++node, ++index) { node.modify().header().first = 123 * index; node.modify().header().second = 456 * index; diff --git a/tests/utils/EmbeddedAllocator.cpp b/tests/utils/EmbeddedAllocator.cpp index b1dbc8af..1056b923 100644 --- a/tests/utils/EmbeddedAllocator.cpp +++ b/tests/utils/EmbeddedAllocator.cpp @@ -6,7 +6,7 @@ namespace db0 { std::optional
EmbeddedAllocator::tryAlloc(std::size_t size, std::uint32_t slot_num, - bool aligned, unsigned char) + bool aligned, unsigned char, unsigned char) { auto new_address = Address::fromOffset(4096 * ++m_count); m_allocations[new_address] = size; diff --git a/tests/utils/EmbeddedAllocator.hpp b/tests/utils/EmbeddedAllocator.hpp index 9fef063d..d05e82f5 100644 --- a/tests/utils/EmbeddedAllocator.hpp +++ b/tests/utils/EmbeddedAllocator.hpp @@ -19,7 +19,7 @@ namespace db0 EmbeddedAllocator() = default; std::optional
tryAlloc(std::size_t size, std::uint32_t, - bool aligned = false, unsigned char realm_id = 0) override; + bool aligned = false, unsigned char realm_id = 0, unsigned char locality = 0) override; void free(Address) override;