Questions regarding decoupling the global state to allow independent execution contexts
Hello! While a relatively new user of GiNaC, I've been following the long-standing discussions regarding multithreading and the inherent difficulties of implementing thread-safety in a library designed around global state and shared memory. Pardon my ignorance, as I am not yet fully versed on the library's internal implementation. I would like to ask a few conditional questions regarding the possibility of personally implementing a multi-state/context architecture for GiNaC. 1. Is it correct to say that the library currently relies on a single, global "environment" that acts as the sole gatekeeper for all symbolic state? Does this design effectively prevent multiple independent processes from referencing the library's logic (not process objects at runtime in the global thread; the actual GiNaC library) at the same time? 2. Is it possible to decouple the library's core algebraic logic from this singular global state? Could the library be structured so that its functions serve a context provided by the user, rather than relying on a hardcoded global one? 3. If the library were modified to support independent contexts could multiple such worlds coexist within the same program without interfering with one another? 4. Could this be implemented in a way where the current global state simply becomes a default context? Specifically, is it possible to ensure that users who do not need multiple contexts see no change in behavior or performance? 5. In a world of isolated contexts, how might we facilitate the "translation" or "importing" of an expression from one independent world into another? This may merely have to be copies of expressions, but that can be handled at the use-case level. 6. Where are the primary "anchors" of this global state located within the codebase? If I were to personally take on the challenge of implementing such a context-based architecture, what areas of the library should I study first? Again, I am not asking the GiNaC community to implement these changes. I am asking for guidance on whether this is even possible and, if so, how to possibly implement this myself. I believe that by permitting multiple independent instances of a state to exist, the multithreading problem can be solved through isolation rather than the complexity of locks. I appreciate you taking the time to read this. Patrick S. Jacobs
I would like to ask a few conditional questions regarding the
Hi Patrick, On 2/19/26 6:59 AM, Patrick Jacobs wrote: possibility of personally implementing a multi-state/context architecture for GiNaC. It's not an issue of there being some non-thread-safe global state. There is, but it's small, and you could probably make access to it safe in a straightforward way. The real issue is that every GiNaC object is reference-counted. An expression like x^2+2*x*y+y^2 holds symbol x (refcount 2), symbol y (refcount 2), symbol 2 (refcount 3) and then there's the entire add object (refcount 1). And when you manipulate it, a multitude of refcounts get incremented or decremented. That would have to be done in a thread-safe atomic way. And numeric objects hold CLN objects and these are reference-counted, too. Long time ago, there've been discussions about multi-threading here: <https://lists.ginac.de/archives/list/ginac-list@ginac.de/thread/LJ6HYGOKEY6HAOJ26S5GWRYAQTM2VKKQ/> <https://lists.ginac.de/archives/list/ginac-list@ginac.de/thread/RVUTFC3UMYILBHMNPCWO6EIZURMPDDG2/> <https://lists.ginac.de/archives/list/ginac-list@ginac.de/thread/YO3QDSQH4SNKWSQ2Z6A6OR25S2AXNRDG/> That said, I'm not a real expert in multi-threading. All my best, -richard. -- Richard B. Kreckel <https://in.terlu.de/~kreckel/>
I appreciate your response! I have previously read through the attached threads before my first message, and I think I explained myself poorly, linguistically mixed up shared-memory concurrency with isolated parallelism. My suggestion is explicitly because of the reference-counting in GiNaC. When I mention “contexts," I mean isolated object hierarchies. In this model, we would never share the same underlying basic object (memory address) between threads. Instead, when we want to move work to another thread, we would perform a deep copy (or "export/import") of the expression. Ideally, x in Context A and x in Context B would be two completely distinct objects in memory, each with its own independent reference counter. Thread A only touches the refcounts in its pool, and Thread B only touches the refcounts in its pool. My question is essentially: Is the current global state (like the symbol registry or CLN internals) tightly coupled in a way that prevents us from having these two isolated "islands" of objects coexisting in the same process? I'm asking if we can use multiple contexts (worlds) to split up work on expressions and pass equivalent representations of expressions to different contexts. Forgive me if I am making a nonsensical suggestion. Thank you! Patrick S. Jacobs
Hi Patrick, On 2/19/26 5:31 PM, Patrick Jacobs wrote:
I appreciate your response! I have previously read through the attached threads before my first message, and I think I explained myself poorly, linguistically mixed up shared-memory concurrency with isolated parallelism. My suggestion is explicitly because of the reference-counting in GiNaC. When I mention “contexts," I mean isolated object hierarchies. In this model, we would never share the same underlying basic object (memory address) between threads. Instead, when we want to move work to another thread, we would perform a deep copy (or "export/import") of the expression. Ideally, x in Context A and x in Context B would be two completely distinct objects in memory, each with its own independent reference counter. Thread A only touches the refcounts in its pool, and Thread B only touches the refcounts in its pool. My question is essentially: Is the current global state (like the symbol registry or CLN internals) tightly coupled in a way that prevents us from having these two isolated "islands" of objects coexisting in the same process? I'm asking if we can use multiple contexts (worlds) to split up work on expressions and pass equivalent representations of expressions to different contexts. Forgive me if I am making a nonsensical suggestion.
Oh, great question! No, not a nonsensical suggestion at all. Yes, there would be some silent coupling between the pools. Have a look at the the number expressions initialized in the library_init ctor in utils.cpp, for example. But with some reasonable effort, it should be possible, I guess, to split all this into per-thread pools and then use them only per-pool in parallel. It could be worth trying. -richy. -- Richard B. Kreckel <https://in.terlu.de/~kreckel/>
participants (2)
-
Patrick Jacobs
-
Richard B. Kreckel