this is indeed a change of concept.
A 'command instance' can not be found through the official
Command front-end anymore, since we do not create a registration.
This allows us to avoid decorating command IDs with running counters
interesting new twist: we do not even need to decorate with a running number,
since we'll get away with an anonymous command instance, thanks to Command
being a smart-handle
...otherwise our log will be flooded with command definition messages soon
NOTE: to see all command definitions happening, set into environment:
NOBUG_LOG='command:TRACE
this is a prerequisite for command instance management:
We have now an (almost) complete framework for writing actual
command definitions in practice, which will be registered automatically.
This could be complemented (future work) by a script in the build process
to regenerate proc/cmd.hpp based on the IDs of those automatic definitions.
...better make it noncopyable to enforce the builder-style use.
In the recent test, I observed strange behaviour when erroneously passing
the CommandDef by value; the command seemed to be registered just fine,
but afterwards, the registry was empty. I must admit I don't understand
this, just from reading the code in CommandDef and Command it should
work just fine to activate a copy of the originally started CommandDef;
anyway, I didn't care to track that issue down, rather make the
CommandDef noncopyable as it should have been right from start.
The point in question is how to manage these definitions in practice,
since we're about to create a huge lot of them eventually. The solution
attempted here is heavily inspired by the boost-test framework
command processing against the session is not yet implemented,
so to allow for unit testing, we magically recognise all commands
starting with "test." and invoke them directly within the Dispatcher.
With this addition, the basic functionality of the dispatcher works now
...since the session loop will be notified on any change via the
interface, adding a command will activate the loop, and the builder
timeout is handled separately via the dirty state. So there is no
need to spin around the loop in idle state.
As a aside, timeout waiting on a condition variable can be intentional
and should thus not be logged as an error automatically. It is up to the
calling context to decide if a timeout constitutes an exceptional situation.
It is always a trade-off performance vs. readability.
Sometimes a single-threaded implementation of self-contained logic
is preferable to a slightly more performant yet obscure implementation
based on our threadpool and scheduler.
Did a full review of state and locking logic, seems airtight now.
- command processing itself is unimplemented, we log a TODO message for now
- likewise, builder is not implemented
- need to add the deadlock safeguard #1054
Due to object scoping we can conclude reliably that the only one
ever to delete the DispacherLoop object will be the the loop thread
from within this object itself, when invoking the termination callback.
Btw, the lock on the inner object was insufficient and will be
replaced by taking the outer lock
It turns out we *do* support the use of anonymous commands
(while it is not clear yet if we really need this feature).
Basically, client code may either create and register a new
instance from another command used as prototype, by invoking
Command::storeDef(ID). Or, alternatively it may just invoke
newInstance() on the command, which creates a new handle
and a valid new implementation (managed by the handle as
smart-ptr), but never stores this implementation into the
CommandRegistry. In that case, client code may use such a
command just fine, as long as it cares to hold onto that
handle; but it is not possible to retrieve this command
instance later by symbolic ID.
In the light of this (possible) usage pattern, it doesn't
make sense to throw when accessing a command-ID. Rather, we
now return a placeholder-Symbol ("_anonymous_")
after reading some related code, I am leaning towards a design
to mirror the way command messages are sent over the UI-Bus.
Unfortunately this pretty much abandons the possibility to
invoke these operations from a client written in C or any
other hand made language binding. Which pretty much confirms
my initial reservation towards such an excessively open
and generic interface system.