Enable get shared from ptr.#221
Conversation
Signed-off-by: Reece Hagan <drogonmar@tuyuji.com>
|
raw pointers are typically used inside the high performance callbacks, and the idea is that they force you to understand that there is no memory ownership of the pointer, and that the pointer must be externally managed. If you found places where you needed shared but only had raw, it would be worth listing them up for discussion, because basically it means that the "ownership" concept wasn't carried all the way through the implementation. Did you encounter a lot, or just here and there? |
|
I've been making a graph GUI for LabSound and when you traverse the graph using the inputs/outputs you just get raw ptr's auto input = node->input(i);
lab::AudioNode* connectedNode = input->renderingOutput(*renderLock, j)->sourceNode();at this point your only dealing with raw ptr's and no way to get the shared ptr which kinda breaks using the normal thread-safe connect / disconnect functions from the labsound context. |
|
I see. In my graph editor (https://github.com/LabSound/LabSoundGraphToy/tree/main) I don't actually traverse nodes directly, I create a parallel structure (essentially an ECS) that encodes the graph, so unfortunately I haven't got any pattern there you can use. Basically I wrote a life-time context object that owns all the nodes, and the nodegraph asks the context for the nodes. so if a node gets deleted, it is deleted from the context object, and when the graph editor tries to fetch, the context object says "sorry, invalid". the big problem is that nodes have to stay alive until there are no references left in the real time audio callback queue, and it gets really gnarly figuring out when no thread no longer needs a node. that's why there's no shared pointers in the system for nodes at the moment, it's possible to reason about a global scope but it's really hard to reason about threaded scopes that may decrement a reference, or hold a reference past engine shutdown, and so on. The current scheme is known not to have any concurrency issues versus memory management, assuming all nodes are managed in a context object that outlives the engine (the examples all work that way). My biggest worry about introducing a shared pointer is that node lifetime needs to be thread safe, and needs to not cause shutdown deadlocks if the engine goes down before the nodes are released. So I'm not opposed, if we can be confident there are no memory leaks, deadlocks, or double deletes, especially across combinations of engine shutdown, node deletion, and thread terminations. The idea I had to get past all the issues and be rock solid was to have a big block of memory with the "guts" of the nodes, the big block would be an arena that lives as long as the engine and is completely safe, and the nodes would be allocated slots from the arena. Then there'd be no need to have the nodes in the processing threads at all, and shared pointers would work beautifully. I haven't had time to work this idea through though. This long note intertwines a bunch of ideas, sorry for the ramble. To your problem, if you have a context object that manages the lifetime of your nodes, you can keep your node lifetimes safe as long as your context object is the only thing that is allowed to create and delete nodes (again, that's the trick in the GraphToy, since it's the only thing that can new and delete it is able to "reap" nodes that it knows are completely unreferenced and no longer in the render queue). |
|
Seems like I thought it would just return nullptr if the instance wasnt allocated with Anyway, I'll prob ditch my current graph system since its more of a debug of whats happening in the labsound context and ill be making an Audio Graph Asset Editor which wont be dealing with labsound node ptr's at all. I still want a way to traverse the graph and understand what exists while keeping shared ptr's but maybe that could be a new function in context? |
|
I think there is a strong need for a traversal routine. If you have a look at what the rendering call back does, it starts at a destination node, and recurses through all the input/output connections to visit all the nodes. It would be very sweet to have a helper function that performs that utility directly. One way to do it would be to introduce a traversal object that is a structure with a pointer to an audio node and a vector of traversal objects in traversal order. Following the data structures directly is complex, it would be great to have a single way to do it. |
|
Yeah, I'm currently just modifying LabSound as I develop my game and plugin, a lot of my PR's are cherry picks from my personal fork so at some point I'll see about that. |
Problem
A lot of functions in LabSound either want the shared_ptr or the raw ptr,
sometimes you only get access to the raw ptr like when traversing the graph and you want to execute some LS function that requires a shared_ptr but currently theres no way to get the shared_ptr from a raw ptr.
Possible solution
Enable
std::enable_shared_from_this, I'm not 100% sure if this is a great solution but its been working for me.