Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions run_asan_tests.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#!/bin/bash
export PYTHONIOENCODING=utf8
export LD_PRELOAD=$(gcc -print-file-name=libasan.so)

python3 -m pytest -m 'not integration_test' -m 'not stress_test' -c pytest.ini --capture=no "$@" -vv
55 changes: 47 additions & 8 deletions src/dbzero/bindings/python/Memo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,22 @@ namespace db0::python
memo_obj->destroy();
Py_TYPE(memo_obj)->tp_free((PyObject*)memo_obj);
}

template <typename MemoImplT>
int MemoObject_traverse(MemoImplT *self, visitproc visit, void *arg)
{
// No Python object references to traverse in memo objects
// They only contain dbzero native objects
return 0;
}

template <typename MemoImplT>
int MemoObject_clear(MemoImplT *self)
{
// No Python object references to clear in memo objects
// They only contain dbzero native objects
return 0;
}

template <typename MemoImplT>
int PyAPI_MemoObject_init(MemoImplT *self, PyObject* args, PyObject* kwds)
Expand Down Expand Up @@ -481,7 +497,9 @@ namespace db0::python
{Py_tp_getattro, (void *)PyAPI_MemoObject_getattro<MemoObject>},
{Py_tp_setattro, (void *)PyAPI_MemoObject_setattro<MemoObject>},
{Py_tp_richcompare, (void *)PyAPI_MemoObject_rq<MemoObject>},
{Py_tp_hash, (void *)PyAPI_MemoHash},
{Py_tp_hash, (void *)PyAPI_MemoHash},
{Py_tp_traverse, (void *)MemoObject_traverse<MemoObject>},
{Py_tp_clear, (void *)MemoObject_clear<MemoObject>},
{0, 0}
};

Expand All @@ -494,7 +512,9 @@ namespace db0::python
// set available only on pre-initialized objects
{Py_tp_setattro, (void *)PyAPI_MemoObject_setattro<MemoImmutableObject>},
{Py_tp_richcompare, (void *)PyAPI_MemoObject_rq<MemoImmutableObject>},
{Py_tp_hash, (void *)PyAPI_MemoHash},
{Py_tp_hash, (void *)PyAPI_MemoHash},
{Py_tp_traverse, (void *)MemoObject_traverse<MemoImmutableObject>},
{Py_tp_clear, (void *)MemoObject_clear<MemoImmutableObject>},
{0, 0}
};

Expand All @@ -517,7 +537,15 @@ namespace db0::python
// fill-in common slots first
auto slot_ptr = getCommonSlots<MemoImplT>();
while (slot_ptr->slot || slot_ptr->pfunc) {
#if PY_VERSION_HEX < 0x030B0000 // Python < 3.11
// Include all slots including traverse/clear for GC compatibility
slots.push_back(*slot_ptr);
#else
// Skip traverse/clear slots for Python 3.11+ where GC is disabled
if (slot_ptr->slot != Py_tp_traverse && slot_ptr->slot != Py_tp_clear) {
slots.push_back(*slot_ptr);
}
#endif
++slot_ptr;
}

Expand All @@ -529,11 +557,18 @@ namespace db0::python
}

slots.push_back({0, 0});


// Enable GC for Python 3.10 compatibility - required for inheritance hierarchies
std::uint32_t flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE;
#if PY_VERSION_HEX < 0x030B0000 // Python < 3.11
flags |= Py_TPFLAGS_HAVE_GC;
#endif
flags &= ~Py_TPFLAGS_MANAGED_DICT;

auto type_spec = PyType_Spec {
.name = tp_name,
.basicsize = MemoImplT::sizeOf(),
.flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE) & ~(Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_MANAGED_DICT),
.flags = flags,
.slots = slots.data()
};

Expand Down Expand Up @@ -577,17 +612,21 @@ namespace db0::python

auto [type_name, full_type_name] = getMemoTypeName(py_class);
TypeObjectSharedPtr new_type = nullptr;

// For Python 3.10 compatibility: ensure tp_name string persists beyond this scope
// by using pooled string to avoid segfault due to tp_name pointer being copied literally
auto &type_manager = PyToolkit::getTypeManager();
const char* safe_tp_name = type_manager.getPooledString(full_type_name);

// NOTE: MemoObject and MemoImmutableObject have different implementations
if (immutable) {
new_type = Py_OWN(PyMemoType_FromSpec<MemoImmutableObject>(base_class, full_type_name.c_str(), is_singleton));
new_type = Py_OWN(PyMemoType_FromSpec<MemoImmutableObject>(base_class, safe_tp_name, is_singleton));
} else {
new_type = Py_OWN(PyMemoType_FromSpec<MemoObject>(base_class, full_type_name.c_str(), is_singleton));
new_type = Py_OWN(PyMemoType_FromSpec<MemoObject>(base_class, safe_tp_name, is_singleton));
}
if (!new_type) {
return nullptr;
}

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);
Expand Down
7 changes: 3 additions & 4 deletions src/dbzero/bindings/python/PyTypeManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ namespace db0::python

{

std::vector<std::unique_ptr<std::string> > PyTypeManager::m_string_pool;

PyTypeManager::PyTypeManager()
{
if (!Py_IsInitialized()) {
Expand Down Expand Up @@ -96,9 +98,6 @@ namespace db0::python

PyTypeManager::~PyTypeManager()
{
for (auto &str: m_string_pool) {
delete str;
}
if (!Py_IsInitialized()) {
for (auto &pair: m_type_cache) {
pair.second.steal();
Expand Down Expand Up @@ -342,7 +341,7 @@ namespace db0::python

const char *PyTypeManager::getPooledString(std::string str)
{
m_string_pool.push_back(new std::string(str));
m_string_pool.push_back(std::make_unique<std::string>(std::move(str)));
return m_string_pool.back()->c_str();
}

Expand Down
2 changes: 1 addition & 1 deletion src/dbzero/bindings/python/PyTypeManager.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ namespace db0::python
void close();

private:
std::vector<std::string*> m_string_pool;
static std::vector<std::unique_ptr<std::string> > m_string_pool;
// the registry of memo types, used for retrieving type decorators
std::unordered_map<TypeObjectPtr, MemoTypeDecoration> m_type_registry;
std::unordered_map<TypeId, ObjectSharedPtr> m_py_type_map;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ DB0_PACKED_BEGIN
// initialize header by compressing the key item (first item in the node)
// need to append with base, otherwise is_sorted_flag would be erased
base_t::append(comp, this->header().compressFirst(item));
assert(this->size() == 1);
}

static std::size_t measure(const KeyItemT &, CapacityT capacity, const HeapCompT &) {
Expand Down
7 changes: 5 additions & 2 deletions src/dbzero/core/collections/SGB_Tree/SGB_LookupTree.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -272,8 +272,11 @@ DB0_PACKED_BEGIN
if (comp.itemComp(*it, split_item)) {
it += step_;
} else {
other.append(comp, *it);
this->erase_existing(it, comp);
// prevents from leaving an empty node
if (this->size() > 1) {
other.append(comp, *it);
this->erase_existing(it, comp);
}
end_ -= step_;
}
}
Expand Down
26 changes: 17 additions & 9 deletions src/dbzero/core/collections/SGB_Tree/SGB_Tree.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -146,8 +146,10 @@ namespace db0
// erase the max element and create the new node
auto max_item_ptr = node->find_max(m_heap_comp);
auto max_item_index = node->indexOf(max_item_ptr);
auto new_node = super_t::insert_equal(*max_item_ptr, m_node_capacity, m_heap_comp);
auto new_node = super_t::insert_equal(*max_item_ptr, m_node_capacity, m_heap_comp);
node.modify().erase_existing(max_item_index, m_heap_comp);
// must not be empty after removing a single item
assert(!node->empty());
// rebalance the nodes
node.modify().rebalance(new_node.modify(), m_heap_comp);
// append to either of the nodes
Expand All @@ -157,7 +159,7 @@ namespace db0
}
return { node.modify().append(m_heap_comp, std::forward<Args>(args)...), node };
}

/**
* Remove element from the collection if it exists
*
Expand Down Expand Up @@ -190,11 +192,13 @@ namespace db0
{
assert(item.validate());
--(super_t::modify().m_sgb_size);
auto item_index = item.second->indexOf(item.first);
// erase by index since item pointer gets modified (due to CoW)
if (const_cast<sg_tree_const_iterator &>(item.second).modify().erase_existing(item_index, m_heap_comp)) {
// delete the entire node
if (item.second->size() == 1) {
// deleting the only item in the node, remove the entire node
super_t::erase(const_cast<sg_tree_const_iterator &>(item.second));
} else {
auto item_index = item.second->indexOf(item.first);
// erase by index since item pointer gets modified (due to CoW)
const_cast<sg_tree_const_iterator &>(item.second).modify().erase_existing(item_index, m_heap_comp);
}
#ifndef NDEBUG
item.first = nullptr;
Expand Down Expand Up @@ -362,10 +366,14 @@ namespace db0
{
assert(!item.is_end());
--(super_t::modify().m_sgb_size);
auto item_index = item.m_item_it.getIndex();
if (item.m_item_it.second.modify().erase_existing(item_index, m_heap_comp)) {
// delete the entire node
if (item.m_item_it.second->size() == 1) {
// deleting the only item in the node, remove the entire node
super_t::erase(item.m_item_it.second);
item.m_item_it.first = nullptr;
item.m_is_end = true;
} else {
auto item_index = item.m_item_it.getIndex();
item.m_item_it.second.modify().erase_existing(item_index, m_heap_comp);
}
}

Expand Down
35 changes: 22 additions & 13 deletions src/dbzero/core/collections/SGB_Tree/sgb_tree_node.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ DB0_PACKED_BEGIN
return itemComp(rhs, lhs);
}
};

// Notice: header stored as variable-length to allow 0-bytes type (default)
inline HeaderT &header() {
return this->getDynFirst(HeaderT::type());
Expand All @@ -75,14 +75,6 @@ DB0_PACKED_BEGIN
return this->getDynFirst(HeaderT::type());
}

o_sgb_tree_node(CapacityT capacity)
: m_capacity(capacity)
{
// initialize header with default arguments
this->arrangeMembers()
(HeaderT::type());
}

/// Must be initialized with an item
o_sgb_tree_node(const ItemT &item, CapacityT capacity, const HeapCompT &comp = {})
: o_sgb_tree_node(capacity)
Expand All @@ -98,7 +90,7 @@ DB0_PACKED_BEGIN
return capacity;
}

inline ItemT &at(unsigned int index)
inline ItemT &at(unsigned int index)
{
// items stored in the dynamic area
assert(index < m_size);
Expand Down Expand Up @@ -158,7 +150,7 @@ DB0_PACKED_BEGIN
// heapify (as min heap), return pointer to the position of the item
return dheap::push<D>(begin(), end(), comp);
}

/**
* Erase item by key if it exists
*
Expand Down Expand Up @@ -373,6 +365,9 @@ DB0_PACKED_BEGIN
return;
}

assert(size() > other.size());
assert(size() > 0 );
assert(other.size() > 0);
// pick the split point assuming that elements are already approximately sorted (heap sorted)
int iter_max = 2;
while (iter_max-- > 0 && size() > other.size()) {
Expand All @@ -382,8 +377,12 @@ DB0_PACKED_BEGIN
if (comp.itemComp(*it, split_item)) {
++it;
} else {
other.append(comp, *it);
this->erase_existing(it, comp);
if (size() > 1) {
other.append(comp, *it);
this->erase_existing(it, comp);
// must not be empty after removing a single item
assert(!this->empty());
}
--end_;
}
}
Expand All @@ -400,6 +399,16 @@ DB0_PACKED_BEGIN
return sizeof(o_sgb_tree_node) + HeaderT::sizeOf() + item_count * sizeof(ItemT);
}

protected:

o_sgb_tree_node(CapacityT capacity)
: m_capacity(capacity)
{
// initialize header with default arguments
this->arrangeMembers()
(HeaderT::type());
}

private:

/**
Expand Down
2 changes: 1 addition & 1 deletion src/dbzero/core/collections/vector/LimitedVector.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ namespace db0
{
m_root.commit();
for (auto &block: m_cache) {
if (block) {
if (!!block) {
block.commit();
}
}
Expand Down
8 changes: 4 additions & 4 deletions src/dbzero/core/intrusive/base_traits.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ namespace db0

static node_ptr get_parent(const node_ptr &n)
{
if (n) {
if (!!n) {
// NOTE: Address::fromOffset is in case ptr_set is of a regular numeric type
return node_ptr(n.getMemspace().myPtr(Address::fromOffset(n->ptr_set.parent)));
} else {
Expand All @@ -31,7 +31,7 @@ namespace db0

static node_ptr get_left(const node_ptr &n)
{
if (n) {
if (!!n) {
// NOTE: Address::fromOffset is in case ptr_set is of a regular numeric type
return node_ptr(n.getMemspace().myPtr(Address::fromOffset(n->ptr_set.left)));
} else {
Expand All @@ -45,7 +45,7 @@ namespace db0

static node_ptr get_right(const node_ptr &n)
{
if (n) {
if (!!n) {
// NOTE: Address::fromOffset is in case ptr_set is of a regular numeric type
return node_ptr(n.getMemspace().myPtr(Address::fromOffset(n->ptr_set.right)));
} else {
Expand All @@ -61,5 +61,5 @@ namespace db0
n->checkIntegrity(n.getMemspace());
}
};

}
Loading