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.
We don't post a message to the event loop telling it to execute
initializeSalmanoff anymore. We now execute it in the main control
flow.
Also, we've unified the logic to call finalizeReq() in response to
exceptions in the outermost try block.
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.