I'm near to release first component of my project. Below are some details about it. Please, read and comment it. Don't shame me for my English

.
Description
-----------
Lazy Evaluation Engine (LEE) component of Atoqu toolkit is described in the section.
Basics
------
Commonly used Observer design pattern lacks capability to organize nested structures of dependent objects and capability to provide synchronized notification/updating of dependent objects in such structures.
A modified design pattern called LEE Observer (LeeObserver) is proposed to provide lacking capabilities.
The described below implementation is based on pull approach as of propagation of data changes, e.g. all required data are to be queried by dependent observers.
The idea of lazy evaluation (deffered execution) is to remember new values (or changes) of parameters and to perform recalculation (evaluation) of dependent member-data by the values when dependent data are to be used, so if parameters are changed more frequently than dependent data are requested the number of recalculations is minimized.
A simple change of member-data in one observer (called below notifier) can cause changes in other observers (called below syncers).
Moreover if syncers are notifiers for another syncers then cascades of changes must be performed in dependency graph order, so implementation of lazy evaluation must lighten developer's burden to trace and change states of dependent observers.
This is the Lazy Evaluation Engine which solves the task.
Lazy Evaluation Engine (LEE) provides a way:
* to keep state of each stateful observer by a set of boolean flags,
* to perform cascaded notification of syncers in dependence graph order about changes being performed in notifiers,
* to start cascaded evaluation in dependence graph order when evaluation is requested.
Usage
-----
Some steps are to be done to make class to use LEE:
* class is to be inherited from `AQLeeObserver' (encapsulates instance of `AQState' providing core LEE functionality);
* member-data to be recalculated are assigned by a non-zero notification flag:
enum {
InitObserverNF // the flag is raised in constructor to delay object initialization
};
* which is to be added before first usage (e.g. in constructor):
addFlag( InitObserverNF, "InitObserverNF" );
* recalculation of member-data assigned by `InitObserverNF' notification flag is started by respective member-function:
protected:
void init_observer() {
... // member-data initialization
}
* pure virtual `evaluate' function is to be implemented:
protected:
virtual void evaluate( int flag ) {
if ( InitObserverNF == flag ) // run init_observer if respective flag is raised
init_observer();
}
where flag is one of notification flags.
* the function below is to be called after any change of parameters used by member-data assigned by `InitObserverNF' notification flag:
notify( InitObserverNF ); // is called in constructor
* the function below is to be called each time member-data assigned by `InitObserverNF' notification flag are requested (`evaluate' with `InitObserverNF' notification flag is called by the function below):
sync( InitObserverNF ); // is called before first usage of observer
After the actions above modified class can use LEE functionality and has been prepaired to be included in LEE dependency graph by attach/detach if required:
attach( notifier, notifier_flag,
syncer, syncer_flag );
detach( notifier, notifier_flag,
syncer, syncer_flag );
Signature of attach/detach is similar to Qt's connect/disconnect but the meaning differs much.
Qt's connect/disconnect are meant to create/remove unidirectional link between signal emitter and syncer to notify syncer by signal while LEE's attach/detach are meant to create/remove bidirectional link between notifier and syncer to notify syncer about notifier state change and force notifier later to evaluate itself when syncer wants to get changes.
That approach means that:
* LEE syncer can delay notification handling for Push model or even delivering for Pull mode till time it can/want to handle notification (in contrast to Qt's signal which is to be handled by syncer immediately or during processing of queued signal in event loop - in bouth cases independently of syncer wish)
* notifier can delay self evaluation till time one of its syncer requests its evaluation (in contrast to Qt's signal emitting following to self evaluation to provide synchronization)
LEE Observer can be combined with many design patterns as hidden internal pattern providing deffered execution.
NOTE: LEE detaches attached instances during destructor call of notifier or syncer, so it doesn't required to do it manually in destructor.
In some cases two or more notification flags are required:
* cascaded updating is divided on some stages, results of each stage are cached and after change of data of a stage the cached data of a previous stage can be used to update observer without updating on all stages. In the case each stage caching data is assigned with a unique notification flag and a unique update function,
public:
enum {
ScaleNF,
TicksNF
};
AxisModel()
{
addFlag( RangeNF, "RangeNF" );
addFlag( TicksNF, "TicksNF" );
attach( this, RangeNF, this, TicksNF ); // sequential stages
}
protected:
void update_scale();
void update_ticks();
virtual void evaluate( int flag ) {
if ( ScaleNF == flag )
update_scale();
elseif ( TicksNF == flag )
update_ticks();
}
* $-1¨áomposite object depends on some ¨áomponents and after change of a ¨áomponent the respective data of ¨áomposite object are to be updated only. In the case each ¨áomponent is equipped by notification flag, respective unique flag and updating function are added to ¨áomposite and the flags are attached with ¨áomponent as a notifier and ¨áomposite as a syncer,
* observer behaviour depends on observer state (changed with state transition diagram). In the case each state is provided by idividual notification flag and the same member-data are handled with updating member-functions respective states. In the case LEE mustn't be used for state machinary, it must be provided by extra;
Cautions
--------
LEE provides very flexible way to implement dependencies, so it is equipped by built-in checks to prevent errors caused by wrong design:
* any kind of recursive dependency of parameters is prohibited so LEE discovers try to create recursive links during attach (NOTE: notify/sync can be nested for the same instance with different flags),
* LEE rejects try to override added already flag. An error message about the try is sent,
* LEE rejects try to attach unknown flag (not added yet). An error message about the try is sent,
* LEE rejects try to notify/sync with unknown flag. An error message about the try is sent,
* LEE warns about idle notify/sync (when flag is raised or is lowered already by previous notify/sync respectively). The warning _can_ be caused by needless link,
* LEE rejects repeated attach of syncer and notifier with the same flags. A warning about the try is sent;
The more classes dependent each other use lazy evaluation the more effective lazy evaluation is.
Performance gain got by LEE can be negative in a wrong designed project due to service overhead, for instance when:
* top level instance doesn't use LEE and
* sync of instances using LEE and dependent from top level instance is called each time a parameter change of top level instance is performed and
* most changes are changes of top level instance parameters;
in the case lazy evaluation makes no sense and the only effect is decreased performance.
Threaded applications
---------------------
To provide proper locking of attached observers a dependency graph concept is introduced.
Each observer belongs to a dependency graph which is represented by dedicated structure defined in AQLeeState class.
If two observers owned by different dependency graphs are attached then one of graphs is set for bouth observers and abandoned graph is deleted.
Conversly if two observers with the same dependency graph are detached and no links more exist between observers (indirect links are checked too) then new dependency graph is created and set for one of observers and all observers attached to it.
If the thread-safe mode was set for observer during contruction then its dependency graph is locked when the listed functions are called: addFlag, notify, sync, attach, detach, setNotifyMode, ~AQLeeObserver.
The graph can be (un)locked explicitly by lock/tryLock/unlock AQLeeObserver-members.
In common case it is better to design observer dependency graphs accessed in dedicated threads only, so the threads communicate e.g. only by circular buffer shared by communicating threads, in the case waiting for locking is minimized.
The LEE's thread-safe mode is to be used only when the same dependency graph must be accessed from different threads and the design requirement can't be overcome or doesn't reduce application performance.
In thread-safe mode (in contrast to default reentrant mode) LEE provides locking of dependency graph during notify/sync to prohibit concurrent access to attached observers from different threads.
The mode is set during observer creation and can't be changed afterward.
NOTE: The mode is to be used by attached observers accessed from different threads ALWAYS else observer synchronization can be broken.
To lock an observer and its dependency graph manually to prevent concurrent access by notify/sync from other threads some locking functions are provided:
* void lock() - lock dependency graph, so any observer belonging with the graph can be accessed safely by its interface in thread the lock was performed in,
* bool tryLock() - try to lock dependency graph, return true if done successfully, false if failed,
* void unlock() - unlock dependency graph,
Lock() doesn't provide automatic synchronization so sync() is to be called after lock() before to access member-data of locked observer or observer is to be locked by convenience class AQLeeObserverLocker.
FAQ
---
Q: Does LEE propagate changes from notifier to syncer like Qt's signal/slot mechanism does?
A: No it doesn't. LEE provides mechanism to propagate notifications about changes and to call synchronization functions when required if changes took place. It is completely another way to handle changes.
Q: Why isn't LEE based on Qt's signal/slot mechanism?
A: Proposed notification system can be implemented by Qt signal/slot mechanism but there are some reasons to don't implement the system by the mechanism:
* moc doesn't support template classes. The C++ feature is very powerful and useful for data-type independence. LEE was designed for plotting library and evolved with it so data-type independence of derived template classes is a critical issue;
* performance and flexibility suffer much from extra layer in state machinary introduced by signal/slot mechanism;
* current LEE implementation can be decoupled easy from Qt if required, signal/slot based one can't be decoupled easy. Qt's containers were chosen by non-principal reasons.
Q: What is the difference between Pull and Push notify modes?
A: They are equal as of LEE functionality i.e. equal from view point of AQLeeObserver subclasses. The only difference is in way LEE walks through dependency graph nodes during notify and sync what influences performance much according to the circumstances. In Pull mode LEE postpones notification of syncers so has to walk through all notifiers during sync. In Push mode LEE notifies all syncers immediately so has to walk through some notifiers during sync. So the modes supplement each other and must be used properly.
The difference is illustrated numerically by program placed in tests/performance directory:
> ./performance
...
test Pull Push Push/Pull
1) self notify/sync 777163.366 756826.733 0.9738
2) root notify, leave sync (1 level) 519567.327 387721.782 0.7462
3) leave notify, leave sync (1 level) 519370.297 751330.000 1.4466
4) root notify, root sync (1 level) 786658.416 769724.752 0.9785
5) 5 root notify, 1 leave sync (3 level) 226617.822 155592.079 0.6866
6) 1 root notify, 1 leave sync (3 level) 316601.980 192459.406 0.6079
7) 1 root notify, 5 leave sync (3 level) 74957.426 159513.861 2.1281

5 leave notify, 1 leave sync (3 level) 229248.515 405061.386 1.7669
9) 1 leave notify, 1 leave sync (3 level) 310400.990 758176.238 2.4426
10) 1 leave notify, 5 leave sync (3 level) 74608.911 404097.030 5.4162
11) 5 root notify, 1 root sync (3 level) 405002.970 398606.931 0.9842
12) 1 root notify, 1 root sync (3 level) 786636.634 755769.307 0.9608
13) 1 root notify, 5 root sync (3 level) 255324.752 398726.733 1.5616
NOTE: The test is artificial and computations performed during sync() are minimal so Push/Pull relation of some tests can be much more in real examples.
Q: What about direct access to observer state?
A: Write access is prohibited else LEE functioning can be damaged. Read access is prohibited as well because values of notification flags have no sense out of LEE context (the values depend on attached observers, notifying/syncing history and notify mode).
Q: Notification flags are values of enumerations, they are represented and used as integers in argument lists of addFlag/attach/detach/notify/sync, so there is a probability to mix up flags from different enums represented by the same value by mistake. Is there any way to provide flag values unambiguity?
A: A continuous numbering or predefined non-intersecting ranges of values for different enums can be used to represent notification flags in enumerations to provide flag values unambiguity, moreover it is required to provide unique values of flags of base class and derived ones. In any case addFlag() checks uniqueness of values of added flags.
Q: LEE adds extra complexity into execution logics, so extra efforts are required to program by it and extra errors can be added. Is it better to escape to use LEE in complex cases?
A: From other side LEE can provide some performance gain and helps to reveal/escape synchronization errors in design (of course if synchronization is performed with LEE and LEE usage was designed properly).
Q: LEE enforces to lock attached observers shared by threads in multitreaded application, so application performance decreases. Is it better to escape to use LEE in the case?
A: It depends on. For non-shared observers LEE can be used in reentrant mode and performance of each thread may be increased thanks to lazy evaluation. Shared observers could be designed and grouped properly (by attach()) and a resource manager could be used to start syncing of independent observers (owned by different dependency graphs) in separate threads when CPUs/memory/etc are free then overall application performance may be increased if there is redundancy of computational resources (multicore CPUs are upcoming). Of course it can be done without LEE which being used can help to reduce the number of synchronization errors (like a race condition which can be hard disclosed in multithreaded applications).
Q: Are there rules how to use LEE?
A: LEE can be used in any ways which don't damage it (e.g. too deep dependency level may cause program stack overflow). Here are some "rules of thumb":
* First case: recalculation of member-data takes a while and changes requiring recalculation are frequent so deffered execution provides significant performance gain. Second case: object dependency graph is enough complex, may be dynamically changed so it is very easy to implement wrong synchronization. Bouth cases are handled natively by LEE, it provides deffered execution as well as check of loop dependencies.
* if interaction of some parts of a designed system isn't absolutely clear then it's better to "overcall" sync and remove needless syncs later when the interaction will be elaborated,
* you understand design and execution flow in an application/lib enough good and sure that LEE being used in the case can give an advantage, if not then relax and put LEE back in your toolbox. This is the main rule

,
Proposals of other rules proved to be useful in real examples are welcome.