planning the advice binding index implementation

This commit is contained in:
Fischlurch 2010-04-24 17:31:01 +02:00
parent 5b48b9f864
commit c8ac2b0447
9 changed files with 1482 additions and 31 deletions

File diff suppressed because it is too large Load diff

After

Width:  |  Height:  |  Size: 49 KiB

View file

@ -122,7 +122,7 @@ namespace advice {
/**
* Access point for the advised entity (client).
* Access point for the advising entity (server).
* TODO type comment
*/
template<class AD>

View file

@ -22,6 +22,7 @@
#include "lib/advice.hpp"
#include "lib/advice/index.hpp"
namespace lib {

154
src/lib/advice/index.hpp Normal file
View file

@ -0,0 +1,154 @@
/*
INDEX.hpp - data structure for organising advice solutions and matching
Copyright (C) Lumiera.org
2010, Hermann Vosseler <Ichthyostega@web.de>
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/** @file index.hpp
** Implementation datastructure for use by the Advice system.
** To support the \em Advice collaboration, it is necessary to match advice requests with
** existing advice provisions. Each successful match creates an advice solution, resulting in
** the bound piece of advice (data) to become visible to all the advised entities having placed
** a matching advice request into the advice system.
**
** This header is intended to be incorporated as part of the advice system implementation (advice.cpp).
** It is \em not usable as an external interface. But it is written in a rather self-contained manner,
** in order to be testable in isolation. To this end, the actual PointOfAdvice entities being organised
** by this index datastructure remain abstract (defined as template parameter). As link for dealing
** with those entities, we employ the free functions \c getSolution(POA) and \c setSolution(POA)
** -- expected to be picked up by ADL.
**
** \par implementation notes
** The advice binding index is implemented by two hashtables holding Binding::Matcher entries.
** Each entry associates a back-link to the corresponding POA (PointOfAdvice), which is assumed
** to be maintained outside the index. PointOfAdvice is an type-erased interface baseclass.
** Actually the advice system will have to deal with concrete advice::Request and advice::Provision
** objects, which are templated to a specific advice type; but this specifically typed context
** is kept on the interface level (advice.hpp) and the type information is stripped before
** calling into the actual implementation, so the index can be realised in a generic fashion.
**
** @note as of 4/2010 this is an experimental setup and implemented just enough to work out
** the interfaces. Ichthyo expects this collaboration service to play a central role
** at various places within proc-layer.
** @note for now, \em only the case of a completely constant (ground) pattern is implemented.
** Later we may consider to extend the binding patterns to allow variables. The mechanics
** of the index are designed right from start to support this case (and indeed the index
** could be much simpler if it wasn't to deal with this foreseeable additional complexity.
** When a pattern contains variables, then even within one bucket of the hashtable there
** might be non-matching entries. Each individual pair of (provision, request) has to be
** checked explicitly to determine a match. /////////////////////////TICKET #615
**
** @see advice.hpp
** @see binding.hpp
** @see advice-index-test.cpp
** @see advice-binding-pattern-test.cpp
** @see advice-basics-test.cpp
**
*/
#ifndef LIB_ADVICE_INDEX_H
#define LIB_ADVICE_INDEX_H
#include "lib/error.hpp"
#include "lib/advice/binding.hpp"
//#include "lib/symbol.hpp"
//#include "lib/query.hpp"
//#include <iostream>
//#include <string>
//#include <set>
namespace lib {
namespace advice {
// using std::string;
// LUMIERA_ERROR_DECLARE (BINDING_PATTERN_SYNTAX); ///< Unable to parse the given binding pattern definition
/**
* Index datastructure for organising advice solutions.
* Based on two hashtables for advice provisions and requests,
* the index allows to add, modify and remove entities of these
* two kinds. Each of these mutating operations immediately
* re-computes the advice solutions and publishes the results
* by invoking the free function \c setSolution(POA) for the
* corresponding PointOfAdvice entity.
*
* @note the diagnostic API is mainly intended for unit testing
* and \em not implemented with focus on performance.
*/
template<class POA>
class Index
{
public:
void
clear ()
{
UNIMPLEMENTED ("clear all index tables");
}
/* == diagnostics == */
/** validity self-check */
bool isValid() const;
size_t
size() const
{
UNIMPLEMENTED ("sum up size of both tables");
}
private:
/** internal: parse into atoms, and insert them */
};
/* === equality comparison and matching === */
/* == diagnostics == */
template<class POA>
bool
Index<POA>::isValid() const
{
UNIMPLEMENTED ("verify the index invariant");
}
}} // namespace lib::advice
#endif

View file

@ -207,8 +207,8 @@ namespace test {
overwriting_and_retracting()
{
#if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #605
TheAdvised client1 ("topic1()");
TheAdvised client2 ("topic2()");
TheAdvised client1 ("topic1");
TheAdvised client2 ("topic2");
CHECK (client1.got(0));
CHECK (client2.got(0));
@ -237,7 +237,7 @@ namespace test {
CHECK (client2.got(r2));
{
TheAdvisor anotherServer("topic1()");
TheAdvisor anotherServer("topic1");
CHECK (client1.got(0));
CHECK (client2.got(r2));
@ -250,7 +250,7 @@ namespace test {
CHECK (client2.got(r2));
{
TheAdvisor yetAnotherServer("topic2()");
TheAdvisor yetAnotherServer("topic2");
CHECK (client1.got(r1));
CHECK (client2.got(r2));
@ -258,7 +258,7 @@ namespace test {
CHECK (client1.got(r1));
CHECK (client2.got(r1));
yetAnotherserver.rebind("topic1()");
yetAnotherserver.rebind("topic1");
CHECK (client1.got(r1));
CHECK (client2.got(0));
@ -266,7 +266,7 @@ namespace test {
CHECK (client1.got(0));
CHECK (client2.got(0));
yetAnotherserver.rebind("topic2()");
yetAnotherserver.rebind("topic2");
CHECK (client1.got(0));
CHECK (client2.got(0));
@ -278,11 +278,11 @@ namespace test {
CHECK (client1.got(0));
CHECK (client2.got(r1));
client1.rebind("topic2()");
client1.rebind("topic2");
CHECK (client1.got(r1));
CHECK (client2.got(r1));
client2.rebind("nonExistingTopic()");
client2.rebind("nonExistingTopic");
CHECK (client1.got(r1));
CHECK (client2.got(0));
#endif /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #605

View file

@ -24,7 +24,7 @@
#include "lib/test/run.hpp"
//#include "lib/test/test-helper.hpp"
#include "lib/advice.hpp"
#include "lib/advice/index.hpp"
//#include "lib/p.hpp"
//#include "proc/assetmanager.hpp"
//#include "proc/asset/inventory.hpp"
@ -43,7 +43,7 @@
//using util::and_all;
//using util::for_each;
//using util::isnil;
//using lib::Literal;
using lib::Literal;
//using lib::Symbol;
//using lumiera::P;
//using std::string;
@ -56,17 +56,50 @@ namespace lib {
namespace advice {
namespace test {
namespace {
using lib::Literal;
namespace { // test support definitions
struct TestPOA
{
};
/** convenience shortcut for writing testcases inline */
inline Binding::Matcher
_entry (uint id, Literal spec)
{
return Binding(spec).buildMatcher();
}
/** check if the given request got the denoted solution */
inline bool
_hasSolution (uint req, uint prov)
{
UNIMPLEMENTED ("check test solution");
}
/** check if the given request holds a default solution */
inline bool
_hasDefault (uint req)
{
UNIMPLEMENTED ("check pseudo default solution, which is NULL for this test");
}
}
typedef Index<TestPOA> Index;
/*******************************************************************************
* @test the Advice system uses an index datastructure to support matching
* the bindings to get pairs of participants to connect by an individual
* advice channel.
/******************************************************************************************
* @test the Advice system uses an index datastructure to support matching the bindings
* to get pairs of participants to connect by an individual advice channel.
*
* This test covers the properties of this implementation datastucture.
* This test covers the properties of this implementation datastructure in isolation.
* We employ special test entries, different from what is used in the advice system,
* and we create a specific instantiation of the advice::Index template solely
* for this test
*
* @todo partially unimplemented and thus commented out ////////////////////TICKET #608
*
@ -80,58 +113,282 @@ namespace test {
virtual void
run (Arg)
{
checkInit();
addEntry();
checkLookup();
removeEntry();
checkCollisionHandling();
checkCleanup();
Index idx;
buildIndex (idx);
addRequest (idx);
addProvision (idx);
removeRequest (idx);
retractProvision (idx);
modifyRequest (idx);
modifyProvision (idx);
clearIndex (idx);
}
void
checkInit()
buildIndex (Index& idx)
{
CHECK (idx.isValid());
CHECK (0 == idx.size());
#if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #608
idx.addRequest (_entry (1,"cat"));
idx.addRequest (_entry (2,"cat"));
idx.addRequest (_entry (3,"dog"));
CHECK (3 == idx.size());
CHECK (3 == idx.request_count());
CHECK (0 == idx.provision_count());
idx.addProvision (_entry (4,"dog"));
CHECK (4 == idx.size());
CHECK (3 == idx.request_count());
CHECK (1 == idx.provision_count());
CHECK (_hasDefault (1));
CHECK (_hasDefault (2));
CHECK (_hasSolution (3,4));
CHECK (idx.isValid());
#endif /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #608
}
void
addEntry()
addRequest (Index& idx)
{
#if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #608
CHECK (idx.isValid());
uint req_cnt = idx.request_count();
idx.addRequest (_entry (5,"dog"));
idx.addRequest (_entry (6,"cat"));
CHECK (idx.hasRequest (_entry (5,"dog")));
CHECK (idx.hasRequest (_entry (6,"cat")));
CHECK (_hasDefault (6));
CHECK (_hasSolution (5,4));
CHECK (idx.isValid());
CHECK (2 + req_cnt == idx.request_count());
#endif /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #608
}
void
checkLookup()
addProvision (Index& idx)
{
#if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #608
CHECK (idx.isValid());
uint r_cnt = idx.request_count();
uint p_cnt = idx.provision_count();
CHECK (_hasDefault (1));
CHECK (_hasDefault (2));
CHECK (_hasDefault (6));
CHECK (_hasSolution (3,4));
CHECK (_hasSolution (5,4));
idx.addProvision (_entry (7,"cat"));
CHECK (idx.hasProvision (_entry (7,"cat")));
CHECK (_hasSolution (1,7)); // all cats got the cat solution
CHECK (_hasSolution (2,7));
CHECK (_hasSolution (6,7));
CHECK (_hasSolution (3,4)); // dogs unaltered
CHECK (_hasSolution (5,4));
CHECK (idx.isValid());
CHECK (1 + p_cnt == idx.provision_count());
CHECK (0 + r_cnt == idx.request_count());
idx.addProvision (_entry (8,"dog"));
CHECK (_hasSolution (1,7)); // cats remain unaffected
CHECK (_hasSolution (2,7));
CHECK (_hasSolution (6,7));
CHECK (_hasSolution (3,8)); // all dogs got the new solution
CHECK (_hasSolution (5,8));
CHECK (idx.isValid());
CHECK (2 + p_cnt == idx.provision_count());
CHECK (0 + r_cnt == idx.request_count());
#endif /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #608
}
void
removeEntry()
removeRequest (Index& idx)
{
#if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #608
CHECK (idx.isValid());
uint r_cnt = idx.request_count();
uint p_cnt = idx.provision_count();
CHECK (_hasSolution (1,7));
CHECK (_hasSolution (2,7));
CHECK (_hasSolution (6,7));
CHECK (_hasSolution (3,8));
CHECK (_hasSolution (5,8));
CHECK ( idx.hasRequest (_entry (2,"cat")));
idx.removeRequest (_entry (2,"cat"));
CHECK (!idx.hasRequest (_entry (2,"cat")));
CHECK (p_cnt == idx.provision_count());
CHECK (r_cnt-1 == idx.request_count());
CHECK (_hasSolution (1,7)); // no effect on the other requests
CHECK (_hasSolution (6,7));
CHECK (_hasSolution (3,8));
CHECK (_hasSolution (5,8));
idx.removeRequest (_entry (2,"cat")); // idempotent
CHECK (!idx.hasRequest (_entry (2,"cat")));
CHECK (p_cnt == idx.provision_count());
CHECK (r_cnt-1 == idx.request_count());
CHECK (idx.isValid());
#endif /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #608
}
void
checkCollisionHandling()
retractProvision (Index& idx)
{
#if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #608
CHECK (idx.isValid());
uint r_cnt = idx.request_count();
uint p_cnt = idx.provision_count();
CHECK (_hasSolution (1,7));
CHECK (_hasSolution (6,7));
CHECK (_hasSolution (3,8));
CHECK (_hasSolution (5,8));
CHECK ( idx.hasProvision (_entry (4,"dog")));
idx.removeProvision (_entry (4,"dog"));
CHECK (!idx.hasProvision (_entry (4,"dog")));
CHECK (p_cnt-1 == idx.provision_count());
CHECK (r_cnt == idx.request_count());
CHECK (_hasSolution (1,7)); // no effect on the solutions, because of the more recent dog solution 8
CHECK (_hasSolution (6,7));
CHECK (_hasSolution (3,8));
CHECK (_hasSolution (5,8));
CHECK ( idx.hasProvision (_entry (8,"dog")));
idx.removeProvision (_entry (8,"dog"));
CHECK (!idx.hasProvision (_entry (8,"dog")));
CHECK (p_cnt-2 == idx.provision_count());
CHECK (r_cnt == idx.request_count());
CHECK (_hasSolution (1,7)); // no effect on the cat solutions
CHECK (_hasSolution (6,7));
CHECK (!_hasSolution (3,8));
CHECK (!_hasSolution (5,8));
CHECK (_hasDefault (3)); // but all dog requests reverted to default
CHECK (_hasDefault (5));
idx.removeProvision (_entry (8,"dog")); // idempotent
CHECK (!idx.hasProvision (_entry (8,"dog")));
CHECK (p_cnt-2 == idx.provision_count());
CHECK (r_cnt == idx.request_count());
CHECK (idx.isValid());
#endif /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #608
}
void
checkCleanup()
modifyRequest (Index& idx)
{
#if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #608
CHECK (idx.isValid());
uint r_cnt = idx.request_count();
uint p_cnt = idx.provision_count();
CHECK (_hasSolution (1,7));
CHECK (_hasSolution (6,7));
CHECK (_hasDefault (3));
CHECK (_hasDefault (5));
CHECK (!idx.hasRequest (_entry (2,"cat")));
CHECK ( idx.hasRequest (_entry (5,"dog")));
idx.modifyRequest (_entry (5,"dog"), _entry (2,"cat"));
CHECK ( idx.hasRequest (_entry (2,"cat")));
CHECK (!idx.hasRequest (_entry (5,"dog")));
CHECK (p_cnt == idx.provision_count());
CHECK (r_cnt == idx.request_count());
CHECK (_hasSolution (1,7));
CHECK (_hasSolution (6,7));
CHECK (_hasDefault (3));
CHECK (_hasSolution (2,7)); // automatically got the current cat solution
#endif /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #608
}
void
modifyProvision (Index& idx)
{
CHECK (idx.isValid());
#if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #608
uint r_cnt = idx.request_count();
uint p_cnt = idx.provision_count();
CHECK (_hasSolution (1,7));
CHECK (_hasSolution (2,7));
CHECK (_hasSolution (6,7));
CHECK (_hasDefault (3));
CHECK ( idx.hasProvision (_entry (7,"cat")));
CHECK (!idx.hasProvision (_entry (8,"dog")));
idx.modifyProvision (_entry (7,"cat"), _entry (8,"dog"));
CHECK (!idx.hasProvision (_entry (7,"cat")));
CHECK ( idx.hasProvision (_entry (8,"dog")));
CHECK (p_cnt == idx.provision_count());
CHECK (r_cnt == idx.request_count());
CHECK (_hasDefault (1));
CHECK (_hasDefault (2));
CHECK (_hasDefault (3));
CHECK (_hasSolution (3,8));
idx.addProvision (_entry (7,"cat"));
idx.addProvision (_entry (9,"cat"));
CHECK (idx.hasProvision (_entry (7,"cat")));
CHECK (idx.hasProvision (_entry (9,"cat")));
CHECK (_hasSolution (1,9)); // all cats got the second cat solution
CHECK (_hasSolution (2,9));
CHECK (_hasSolution (6,9));
CHECK (_hasSolution (3,8)); // the dog is unaffected
CHECK ( idx.hasProvision (_entry (7,"cat")));
CHECK (!idx.hasProvision (_entry (4,"dog")));
idx.modifyProvision (_entry (7,"cat"), _entry (4,"dog"));
CHECK (!idx.hasProvision (_entry (7,"cat")));
CHECK ( idx.hasProvision (_entry (4,"dog")));
CHECK (p_cnt == idx.provision_count());
CHECK (r_cnt == idx.request_count());
CHECK (_hasSolution (1,9)); // cats unaffected, because we're changing a shadowed cat solution
CHECK (_hasSolution (2,9));
CHECK (_hasSolution (6,9));
CHECK (_hasSolution (3,4)); // but the dog got switched to the transmogrified-into-dog solution,
// because it was added later than the existing solution 8
CHECK (p_cnt == idx.provision_count());
CHECK (r_cnt == idx.request_count());
CHECK (idx.isValid());
#endif /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #608
}
void
clearIndex (Index& idx)
{
idx.clear();
CHECK (idx.isValid());
CHECK (0 == idx.size());
#if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #608
#endif /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #608
}

View file

@ -85,7 +85,7 @@ namespace test {
/** @test run simple pairs of collaborators in multiple threads.
* Intentionally, there should be just a single match per pair,
* but the timings of provision and pickup are choosen randomly
* but the timings of provision and pickup are chosen randomly
*/
void
check_highLoadSimpleMatch()

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

View file

@ -541,7 +541,7 @@ In a more elaborate scheme, the advised entity could provide a signal to be invo
&amp;rarr; AdviceImplementation
</pre>
</div>
<div title="AdviceImplementation" modifier="Ichthyostega" modified="201004160249" created="201004100056" tags="impl draft img" changecount="19">
<div title="AdviceImplementation" modifier="Ichthyostega" modified="201004240054" created="201004100056" tags="impl draft img" changecount="38">
<pre>[&lt;img[Advice solution|uml/fig141573.png]]
@ -559,6 +559,45 @@ This is the tricky part of the whole advice system implementation. A naive imple
The binding patterns are organised by //predicate symbol and the lists are normalised.// A simple normalisation could be lexicographic ordering of the predicate symbols. Then the resulting representation can be //hashed.// When all predicates are constant, a match can be detected by hashtable lookup, otherwise, in case some of the predicates contain variable arguments ({{red{planned extension}}}), the lookup is followed by an unification. For this to work, we'll have to include the arity into the predicate symbols used in the first matching stage. Moreover, we'll create a //matching closure// (functor object), internally holding the arguments for unification. This approach allows for //actual interpretation of the arguments.// It is conceivable that in special cases we'll get multiple instances of the same predicate, just with different arguments. The unification of these terms needs to consider each possible pairwise combination (cartesian product) &amp;mdash; but working out the details of the implementation can safely be deferred until we'll actually hit such a special situation, thanks to the implementation by a functor.
Fortunately, the calculation of this normalised patterns can be separated completely from the actual matching. Indeed, we don't even need to store the binding patterns at all within the binding index &amp;mdash; storing the hash value is sufficient (and in case of patterns with arguments we'll attach the matching closure functor). Yet still we need to store a marker for each successful match, together with back-links, in order to handle changing and retracting of advice.
!!!storage and registrations
We have to provide dedicated storage for the actual advice provisions and for the index entries. Mostly, these objects to be managed are attached through a single link &amp;mdash; and moreover the advice system is considered performance critical, so it doesn't make sense to implement the management of these entries by smart-ptr. This rules out ~TypedAllocationManager and prompts to write a dedicated storage frontend, later to be backed by Lumiera's mpool facility.
* both the advice provision and the advice requests attach to the advice system after fulfilling some prerequisites; they need to detach automatically on destruction.
* in case of the provision, there is a cascaded relation: the externally maintained provision creates an internal provision record, which in turn attaches an index entry.
* both in case of the provision and the request, the relation of the index bears some multiplicity:
** a multitude of advice provisions can attach with the same binding (which could be the same binding pattern terms, but different variable arguments). Each of them could create a separate advice solution (at least when variable arguments are involved); it would be desirable to establish a defined LIFO order for any search for possibly matching advice.
** a multitude of advice requests can attach with the same binding, and each of them needs to be visited in case a match is detected.
* in both cases, any of these entries could be removed any time on de-registration of the corresponding external entity
* we need to track existing advice solutions, because we need to be able to overwrite with new advice and to remove all solutions bound to a given pattern about to leave the system. One provision could create a large number of solutions, while each registration always holds onto exactly one solution (which could be a default/placeholder solution though)
!!!index datastructure
It is clear by now that the implementation datastrucutre has to serve as a kind of //reference count.// Within this datastructure, any constructed advice solution needs to be reflected somehow, to prevent us from discarding an advice provision still accessible. Allowing lock-free access to the advice solution (planned feature) adds an special twist, because in this case we can't even tell for sure if an overwritten old solution is actually gone (or if its still referred from some thread's cached memeory). This could be addressed with an transactional approach (which might be good anyway) &amp;mdash; but I tend to leave this special concern asside for now.
To start with, any advice matching and solution will //always happen within matching buckets of a hash based pattern organisation.// The binding index thus should build on two hashtables (one for the requests and one for the provisions), but using specifically crafted datastructures as buckets. The individual entries within these bucket structures in both cases will be comprised of a binding matcher (to determine if an match actually happens) and a back-link to the registered entitiy (provision or request). Given the special pattern of the advice solutions, existing solutions could be tracked within the entries at the request side.
* Advice provisions are expected to happen only in small numbers; they will be searched stack-like, starting from the newes provisions, until a match is found.
* Each advised entity basically creates an advice request, so there could be a larger number of request entries. In the typical search triggered from the provision side, each request entry will be visited and checked for match, which, if successful, causes a pointer to be set within the ~AdviceRequest object (located outside the realm of the advice system). While &amp;mdash; obviously &amp;mdash; multiple requests with similar binding match could be folded into a sub-list, we need actual timing measurements to determine the weight of these two calculation steps of matching and storing, which together comprise the handling of an advice solution.
The above considerations don't fully solve the question how to represent an computed solution within the index datastructure, candidates being to use the index within the provision list, or a direct pointer to the provision or even just to re-use the pointer stored into the ~AdviceRequest. Besides solutions found by matching, we need //fallback solutions// holding a default constructed piece of advice of the requested type. As these defaults aren't correlated at all to the involved bindings, but only to the advice type as such, it seems reasonable to keep them completely apart, like e.g. placing them into static memory managed by the ~AdviceProvision template instantiations.
!!!interactions to be served by the index
[&gt;img[Advice solution|draw/AdviceBindingIndex1.png]]
;add request
:check existing provisions starting from top until match; use default solution in case no match is found; publish solution into the new request; finally attach the new request entry
;remove request
:just remove the request entry
;modify request
:handle as if newly added
;add provision
:push new provision entry on top; traverse all request entries and check for match with this new provision entry, publish new solution for each match
;retract provision
:remove the provision entry; traverse all request entries to find those using this provision as advice solution, treat these as if they where newly added requests
;modify provision
:add a new (copy of the) provision, followed by retracting the old one; actually these two traversals of all requests can be combined, thus treating a request which used the old provision but doesn't match the new one is treated like a new request
&lt;&lt;&lt;
__Invariant__: each request has a valid soultion pointer set (maybe pointing to a default solution). Whenever such a solution points to a registered provision, there is a match between the index entries and this is the top-most possible match to any provision entry for this request entry
&lt;&lt;&lt;
Clearly, retracting advice (and consequently also the modification) is expensive. After finishing these operations, the old/retracted provision can be discarded (or put asside in case of non-locking advice access). Other operations don't cause de-allocation, as provisions remain within the system, even if the original advising entity is gone.
</pre>
</div>
<div title="AdviceRequirements" modifier="Ichthyostega" modified="201004100149" created="201004060213" tags="design impl" changecount="17">