Lumiera
0.pre.03
»edit your freedom«
|
Go to the source code of this file.
Implementation of the AdviceSystem, to support the advice collaboration.
The AdviceSystem is implemented as singleton, but is never accessed directly by clients participating in an advice collaboration. Rather, they use the advice::Request and advice::Provision value classes as a frontend. While these frontend classes are templated on the concrete advice type, the common baseclass AdviceLink isn't, allowing the AdviceSystem to operate on type erased PointOfAdvice entries. The protected access functions within AdviceLink are implemented in this compilation unit and access the AdviceSystem singleton defined here locally.
Advice data, when added by an advice::Provision, is copied into a ActiveProvision, which acts as a value holding buffer. This way, the provided advice data is copied into storage managed by the AdviceSystem, allowing to access the data even after the original advice::Provision went out of scope.
But while the Provision is still alive, it may be used to set new advice, modify the binding or even retract the given piece of advice. Thus we need a mechanism to link the ActiveProvision (value holder) to its origin while the latter is still there. Basically we'll use the PointOfAdvice::resolution_ pointer embedded within advice::Provision to point to the ActiveProvision entry incorporated into the advice system. (For advice::Request, the same pointer is used to link to the ActiveProvision yielding the advice solution, if any). Handling the relation between Provision and ActiveProvision this way entails some kind of "unofficial" ownership and is slightly incorrect, but seems the most straight forward implementation. Thus each Provision cares for "his" advice and just detaches when going away. Consequently, by default, advice provisions remain active during the lifetime of the application. We'll see if this causes problems.
The problem with copying and incorporating the ActiveProvision objects is the undetermined size of these value holders, because the frontend objects are templated on the advice type, while the AdviceSystem doesn't have any knowledge of the specific advice type. This advice type is used to set a type guard predicate into each binding, but there is no way to re-discover the specifically typed context; the type guards can only be checked for a match. Thus we need the help of the frontend objects, which need to provide a deleter function when providing concrete advice data; this deleter function will be saved as function pointer so to be able to deallocate all advice data when the AdviceSystem is shut down.
While the frontend objects are deliberately not threadsafe, the lookup implementation within the AdviceSystem uses a system wide advice::Index table and thus needs locking. Besides the protection against corrupting the index, this also serves as memory barrier, so that when a new advice solution is determined and set as a pointer within the matching requests, this change is actually "committed" from cache to memory. But note, when using advice::Request concurrently, you need to employ an additional read barrier to ensure your thread/CPU picks up such newly determined solutions from main memory. Otherwise you might even try to access superseded and already deleted advice data.
Definition in file advice.cpp.
#include "lib/error.hpp"
#include "lib/nocopy.hpp"
#include "lib/del-stash.hpp"
#include "lib/depend.hpp"
#include "lib/symbol.hpp"
#include "lib/sync.hpp"
#include "lib/util.hpp"
#include "include/logging.h"
#include "common/advice.hpp"
#include "common/advice/index.hpp"
Classes | |
class | AdviceSystem |
the system-wide service to support the implementation of advice collaborations. More... | |
Typedefs | |
typedef void() | DeleterFunc(void *) |
Variables | |
lib::Depend< AdviceSystem > | aSys |
hidden implementation-level access to the AdviceSystem | |
Namespaces | |
lumiera | |
Lumiera public interface. | |