If even one step in Body.initializeReq was executed at all, then
whether or not it succeeded, we consider the body component to have
been initialized, at least with respect to whether finalizeReq
ought to run.
We now run body.finalizeReq even if body.init wasn't called. We'll
do a finer-grained check on each aspect of Body that needs to be
finalized now. This check was too large-grained.
This makes the initialization sequence much cleaner and conceptually
well encapsulated.
We also now dynamically allocate the Mind objects. They're allocated
dynamically by Mrntt inside of initializeReq. This means that we no
longer have to worry about jolting and cleaning up the running threads
of global mind object even when we never explicitly called
Mind.initializeReq.
Along with other conceptual improvements to our abstractions, this
patch also gets us to a real "end of program initialization" point
for the first time.
We now allocate globalMind locally inside of marionetteMain. Why?
Before now, we had an asymmetric threading situation where the
globalMind's threads were initialized at during global constructor
invocation and not on demand. This meant that we had to shut down
those threads even if we had never got to the point of calling
Mind::initializeReq.
This significantly complicated our shutdown sequence since we had
to factor in the lifetime of the std::thread objects inside of the
ComponentThreads which were inside of the globalMind object.
Now, if we hadn't called Mind::initializeReq, we don't have to
perform any Mind::finalizeReq or adjacent operations. Shutdown is
symmetrically mirrored against the operations we actually performed
during execution.
We introduced some complexity by splitting ComponentThreads into
two derivative types (MindThread and MarionetteThread) but I think
in the long term we'll be able to massage this split into a much
cleaner situation overall.
In Mrntt, we now initialize Mind:: object threads before calling
initializeSalmanoffReq().
We've also propagated the spinscale async pattern into the Mind
class.
This is the culmination of a lot of changes over the last week. We're
making SMO basically fully async in many areas, and then preparing to
implement the spinqueueing mechanism for locking.
Now we have modularized the Mind class to contain all of its
ComponentThreads. This enables us to run multiple mind instances
within the same SMO process, at least in theory.
We probably won't actually do this, but we want to ensure that the
design is clean enough to enable it.
Gave me some ideas about how things should be structured. Apparently
merely using region-data-locked threads doesn't eliminate the need
for synchronization/locking. It just means your synchronization is much
lighter, in the form of localized variables.
It seems we'll need to maintain boolean trackers for certain
operations that shouldn't be performed concomitantly, and deny
the caller access to those operations in order to preserve
data sanity.
I guess we still ended up using locking after all. Tbh, I'm not even
sure this will make things end up being lighter: we may have to bounce
requests off, or perhaps re-enqueue them into the queue?
So maybe instead of bouncing requests off, we could re-add them to the
rear of the queue when they conflict with an ongoing request.
This language is used broadly to specify how to attach (and thus
also how to detach) devices to/from Salmanoff. The next bit of work
we'll do is split off the DSL parsing from the management of the
list of parsed binary attached spec objects.
We'll be creating a PipeDeviceAttachmentParser, and later on when
we support URDF, we'll create a URDFDeviceAttachmentParser.
This is generally frowned upon but it makes this code 10x cleaner.
We handle commandLine usage msg printing by using exceptions for
control flow. This allows us to centralize the logic for killing
the Mind threads in one place. At least with respect to printing
the usage msg.
We moved the instance of smo::Mind to global scope. I suppose we'll
only support one instance of Mind per SMO process at least for now.
We now track the state of Mind threads' JOLT-waiting. This allows us
to centralize the Mind thread shutdown logic. Mind::finalizeReq()
now takes care of all Mind thread shutdown state logic by tracking
whether Mind threads need to be JOLTed first before being told to
exit.
This allows us to execute an op on all mind threads without having
to repeatedly write loops. We've implemented wrappers to handle
start, pause, resume, exit and JOLT sequences.
These differ from the MentalEntity class hierarchy in that
MentalExistents are a narrower subset of MentalEntities. MentalEntities
refer to all mental content that needs to be persistently stored to
represent cognitive and personna state.
MentalExistents are content of the mind that specifically represents
existents. I.e: perceptual data, whether structural or implicative.
We moved initializeSalmanoff and shutdownSalmanoff into
salmanoff.cpp. Now we also invoke shutdownSalmanoff when exiting
to destroy subsystems and components gracefully.
This fixes the segfault that was thrown on every program exit
when xcbWindow had captured a window.
Mrntt now has the event loop structure required to orderly shut itself
down when it itself generates an exception. We can now post a message
within the catch{} blocks for Mrntt's event loop, telling Mrntt
to shut down the Mind threads and then shut itself down.
We also split the code to initialize threads etc out of mrntt and
put it into the Mind:: namespace.
This commit significantly restructures the way we setup threading in
SMO. We now don't use the CRT main() thread at all. It's only used
as a mechanism to ensure that Marionette doesn't execute before
global constructors have been executed.
JOLTing:
This is a simple ASIO post()ed message that makes each thread setup
its thread-local data pointer to its own ComponentThread object,
and then enter its main ASIO run() loop to await commands from
Marionette.
Exception bubbling:
We now cleanly cause mind threads to report their exceptions
to marionette, so that marionette can cleanly shut the mind down
in an orderly fashion.
Thread Control messaging API:
A namespace of asynchronous messages to be post()ed to threads to
control them. It enables us to pause and resume threads. This will
be very useful for Marionette when we add the ability for it to
suspend Salmanoff's running mind, inject new goals, inspect current
state, etc; and then resume the mind's execution.