Lumiera  0.pre.03
advice.cpp File Reference

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.

memory management

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.

when a Provision is copied, this hidden link is not shared with the copy, which therefore behaves as if newly created with the same binding, but without providing Advice.

implementing the allocations

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.

This approach is closely related to the decision to use a single global index table for managing all advice collaborations. An alternative would be to use either a separate table for each type, or to store an additional type descriptor with this memory management information into each "bucket", which can be assumed to manage entries dealing with the same kind of advice data, because each binding automatically includes a type guard. If we ever happen to get a significant amount of advice data, but only a small number of different advice types, we should reconsider this optimisation.
rewrite the allocation to use Lumiera's MPool instead of heap allocations //////TICKET #609


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.

See also

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"


typedef void() DeleterFunc(void *)


 Lumiera public interface.

Typedef Documentation

◆ DeleterFunc

typedef void() DeleterFunc(void *)

Definition at line 108 of file advice.cpp.