Skip to content

Commit 0e0b4f7

Browse files
v-einhoffstadt
authored andcommitted
fix: State that affects item creation (last_item and company, container stack, etc.) is now thread-local.
1 parent 36ab190 commit 0e0b4f7

3 files changed

Lines changed: 71 additions & 51 deletions

File tree

src/dearpygui_commands.h

Lines changed: 21 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2519,8 +2519,9 @@ setup_dearpygui(PyObject* self, PyObject* args, PyObject* kwargs)
25192519
return nullptr;
25202520
}
25212521

2522-
while (!GContext->itemRegistry->containers.empty())
2523-
GContext->itemRegistry->containers.pop();
2522+
// Clear the containers stack. Unfortunately std::stack doesn't have a clear() call,
2523+
// but assigning a new empty stack does just the same.
2524+
mvItemRegistry::threadContext.containers = {};
25242525
GContext->started = true;
25252526
GContext->running = true;
25262527
GContext->future = std::async(std::launch::async, []() {return mvRunCallbacks(); });
@@ -2924,18 +2925,19 @@ pop_container_stack(PyObject* self, PyObject* args, PyObject* kwargs)
29242925

29252926
mvPySafeLockGuard lk(GContext->mutex);
29262927

2927-
if (GContext->itemRegistry->containers.empty())
2928+
auto& containers = mvItemRegistry::threadContext.containers;
2929+
if (containers.empty())
29282930
{
29292931
mvThrowPythonError(mvErrorCode::mvContainerStackEmpty, "No container to pop.");
29302932
assert(false);
29312933
return nullptr;
29322934
}
29332935

2934-
mvAppItem* item = GContext->itemRegistry->containers.top();
2935-
GContext->itemRegistry->containers.pop();
2936+
mvAppItem* item = containers.top();
2937+
containers.pop();
29362938

29372939
if (item)
2938-
return ToPyUUID(item->uuid);
2940+
return ToPyUUID(item);
29392941
else
29402942
return GetPyNone();
29412943

@@ -2945,8 +2947,9 @@ static PyObject*
29452947
empty_container_stack(PyObject* self, PyObject* args, PyObject* kwargs)
29462948
{
29472949
mvPySafeLockGuard lk(GContext->mutex);
2948-
while (!GContext->itemRegistry->containers.empty())
2949-
GContext->itemRegistry->containers.pop();
2950+
// Clear the containers stack. Unfortunately std::stack doesn't have a clear() call,
2951+
// but assigning a new empty stack does just the same.
2952+
mvItemRegistry::threadContext.containers = {};
29502953
return GetPyNone();
29512954
}
29522955

@@ -2956,11 +2959,12 @@ top_container_stack(PyObject* self, PyObject* args, PyObject* kwargs)
29562959
mvPySafeLockGuard lk(GContext->mutex);
29572960

29582961
mvAppItem* item = nullptr;
2959-
if (!GContext->itemRegistry->containers.empty())
2960-
item = GContext->itemRegistry->containers.top();
2962+
auto& containers = mvItemRegistry::threadContext.containers;
2963+
if (!containers.empty())
2964+
item = containers.top();
29612965

29622966
if (item)
2963-
return ToPyUUID(item->uuid);
2967+
return ToPyUUID(item);
29642968
else
29652969
return GetPyNone();
29662970
}
@@ -2970,23 +2974,23 @@ last_item(PyObject* self, PyObject* args, PyObject* kwargs)
29702974
{
29712975
mvPySafeLockGuard lk(GContext->mutex);
29722976

2973-
return ToPyUUID(GContext->itemRegistry->lastItemAdded);
2977+
return ToPyUUID(mvItemRegistry::threadContext.lastItemAdded);
29742978
}
29752979

29762980
static PyObject*
29772981
last_container(PyObject* self, PyObject* args, PyObject* kwargs)
29782982
{
29792983
mvPySafeLockGuard lk(GContext->mutex);
29802984

2981-
return ToPyUUID(GContext->itemRegistry->lastContainerAdded);
2985+
return ToPyUUID(mvItemRegistry::threadContext.lastContainerAdded);
29822986
}
29832987

29842988
static PyObject*
29852989
last_root(PyObject* self, PyObject* args, PyObject* kwargs)
29862990
{
29872991
mvPySafeLockGuard lk(GContext->mutex);
29882992

2989-
return ToPyUUID(GContext->itemRegistry->lastRootAdded);
2993+
return ToPyUUID(mvItemRegistry::threadContext.lastRootAdded);
29902994
}
29912995

29922996
static PyObject*
@@ -3006,7 +3010,7 @@ push_container_stack(PyObject* self, PyObject* args, PyObject* kwargs)
30063010
{
30073011
if (DearPyGui::GetEntityDesciptionFlags(parent->type) & MV_ITEM_DESC_CONTAINER)
30083012
{
3009-
GContext->itemRegistry->containers.push(parent);
3013+
mvItemRegistry::threadContext.containers.push(parent);
30103014
return ToPyBool(true);
30113015
}
30123016
}
@@ -4164,8 +4168,8 @@ capture_next_item(PyObject* self, PyObject* args, PyObject* kwargs)
41644168

41654169
mvPySafeLockGuard lk(GContext->mutex);
41664170

4167-
GContext->itemRegistry->captureCallback = mvPyObject(callable == Py_None? nullptr : callable, true);
4168-
GContext->itemRegistry->captureCallbackUserData = mvPyObject(user_data, true);
4171+
mvItemRegistry::threadContext.captureCallback = mvPyObject(callable == Py_None? nullptr : callable, true);
4172+
mvItemRegistry::threadContext.captureCallbackUserData = mvPyObject(user_data, true);
41694173

41704174
return GetPyNone();
41714175
}

src/mvItemRegistry.cpp

Lines changed: 26 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212

1313
#include "mvProfiler.h"
1414

15+
thread_local mvThreadContext mvItemRegistry::threadContext;
16+
1517
mvItemRegistry::mvItemRegistry()
1618
{
1719
// We seldom need to do a GetItem(0), and an explicit check for uuid == 0 in GetItem
@@ -36,17 +38,12 @@ DebugItem(const char* label, const char* item) {
3638
ImGui::TextColored(ImVec4(1.0f, 0.0f, 1.0f, 1.0f), "%s", item);
3739
}
3840

39-
static void
40-
PushParent(mvItemRegistry& registry, mvAppItem* item)
41-
{
42-
registry.containers.push(item);
43-
}
44-
4541
static mvAppItem*
4642
TopParent(mvItemRegistry& registry)
4743
{
48-
if (!registry.containers.empty())
49-
return registry.containers.top();
44+
auto& containers = mvItemRegistry::threadContext.containers;
45+
if (!containers.empty())
46+
return containers.top();
5047
return nullptr;
5148
}
5249

@@ -328,9 +325,10 @@ DeleteItem(mvItemRegistry& registry, mvUUID uuid, b8 childrenOnly, i32 slot)
328325
}
329326

330327
// See if it is the captured item
331-
if (registry.capturedItem && registry.capturedItem.get() == item)
328+
auto& capturedItem = mvItemRegistry::threadContext.capturedItem;
329+
if (capturedItem && capturedItem.get() == item)
332330
{
333-
registry.capturedItem = nullptr;
331+
capturedItem = nullptr;
334332
return true;
335333
}
336334

@@ -352,10 +350,11 @@ MoveItem(mvItemRegistry& registry, mvUUID uuid, mvUUID parent, mvUUID before)
352350

353351
std::shared_ptr<mvAppItem> child = nullptr;
354352

355-
if (registry.capturedItem && registry.capturedItem->uuid == uuid)
353+
auto& capturedItem = mvItemRegistry::threadContext.capturedItem;
354+
if (capturedItem && capturedItem->uuid == uuid)
356355
{
357-
child = registry.capturedItem;
358-
registry.capturedItem = nullptr;
356+
child = capturedItem;
357+
capturedItem = nullptr;
359358
}
360359
else
361360
{
@@ -652,8 +651,9 @@ mvAppItem*
652651
GetItem(mvItemRegistry& registry, mvUUID uuid)
653652
{
654653
// check captured
655-
if (registry.capturedItem && registry.capturedItem->uuid == uuid)
656-
return registry.capturedItem.get();
654+
auto& capturedItem = mvItemRegistry::threadContext.capturedItem;
655+
if (capturedItem && capturedItem->uuid == uuid)
656+
return capturedItem.get();
657657

658658
auto found = registry.allItems.find(uuid);
659659
return found != registry.allItems.end()? found->second : nullptr;
@@ -706,16 +706,18 @@ b8
706706
AddItemWithRuntimeChecks(mvItemRegistry& registry, std::shared_ptr<mvAppItem> item, mvUUID parent, mvUUID before)
707707
{
708708

709-
if(registry.captureCallback)
709+
auto& threadContext = mvItemRegistry::threadContext;
710+
711+
if (threadContext.captureCallback)
710712
{
711713

712714
// this is a unique situation in that the caller always has the GIL
713-
registry.capturedItem = item;
715+
threadContext.capturedItem = item;
714716
// resetting captureCallback in advance in order to avoid recursion (if the callback
715717
// attempts to add another item or move an item)
716-
mvPyObject captureCallback(std::move(registry.captureCallback));
717-
registry.captureCallback = nullptr;
718-
mvRunCallback(captureCallback, nullptr, registry.capturedItem->uuid);
718+
mvPyObject captureCallback(std::move(threadContext.captureCallback));
719+
threadContext.captureCallback = nullptr;
720+
mvRunCallback(captureCallback, nullptr, threadContext.capturedItem->uuid);
719721
return true;
720722
}
721723

@@ -733,13 +735,13 @@ AddItemWithRuntimeChecks(mvItemRegistry& registry, std::shared_ptr<mvAppItem> it
733735
//---------------------------------------------------------------------------
734736
if (DearPyGui::GetEntityDesciptionFlags(item->type) & MV_ITEM_DESC_ROOT)
735737
{
736-
registry.lastRootAdded = item->uuid;
737-
registry.lastContainerAdded = item->uuid;
738+
threadContext.lastRootAdded = item->uuid;
739+
threadContext.lastContainerAdded = item->uuid;
738740
}
739741
else if (DearPyGui::GetEntityDesciptionFlags(item->type) & MV_ITEM_DESC_CONTAINER)
740-
registry.lastContainerAdded = item->uuid;
742+
threadContext.lastContainerAdded = item->uuid;
741743

742-
registry.lastItemAdded = item->uuid;
744+
threadContext.lastItemAdded = item->uuid;
743745

744746
//---------------------------------------------------------------------------
745747
// STEP 1: check if an item with this name exists (NO LONGER NEEDED)

src/mvItemRegistry.h

Lines changed: 24 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,25 @@ b8 AddItemWithRuntimeChecks(mvItemRegistry& registry, std::shared_
4848
void ResetTheme (mvItemRegistry& registry);
4949
b8 ReorderChildren (mvItemRegistry& registry, mvUUID parent, i32 slot, const std::vector<mvUUID>& new_order);
5050

51+
//-----------------------------------------------------------------------------
52+
// mvThreadContext
53+
// - Holds thread-local data used together with mvItemRegistry
54+
//-----------------------------------------------------------------------------
55+
56+
struct mvThreadContext
57+
{
58+
// "last item" state
59+
mvUUID lastItemAdded = 0;
60+
mvUUID lastContainerAdded = 0;
61+
mvUUID lastRootAdded = 0;
62+
63+
std::stack<mvAppItem*> containers; // parent stack, top of stack becomes widget's parent
64+
65+
std::shared_ptr<mvAppItem> capturedItem = nullptr;
66+
mvPyObject captureCallback = nullptr;
67+
mvPyObject captureCallbackUserData = nullptr;
68+
};
69+
5170
//-----------------------------------------------------------------------------
5271
// mvItemRegistry
5372
// - Responsibilities:
@@ -61,21 +80,16 @@ b8 ReorderChildren (mvItemRegistry& registry, mvUUID paren
6180
struct mvItemRegistry
6281
{
6382

64-
// "last item" state
65-
mvUUID lastItemAdded = 0;
66-
mvUUID lastContainerAdded = 0;
67-
mvUUID lastRootAdded = 0;
68-
6983
// misc
70-
std::stack<mvAppItem*> containers; // parent stack, top of stack becomes widget's parent
7184
std::unordered_map<std::string, mvUUID> aliases;
7285
std::unordered_map<mvUUID, mvAppItem*> allItems; // used for quick access to items by UUID
86+
87+
static thread_local mvThreadContext threadContext;
88+
89+
// debug items
7390
b8 showImGuiDebug = false;
7491
b8 showImPlotDebug = false;
75-
std::vector<std::shared_ptr<mvAppItem>> debugWindows;
76-
std::shared_ptr<mvAppItem> capturedItem = nullptr;
77-
mvPyObject captureCallback = nullptr;
78-
mvPyObject captureCallbackUserData = nullptr;
92+
std::vector<std::shared_ptr<mvAppItem>> debugWindows;
7993

8094
// roots
8195
std::vector<std::shared_ptr<mvAppItem>> colormapRoots;

0 commit comments

Comments
 (0)