Tulip  4.10.0
Better Visualization Through Research
Observable.h
1 /*
2  *
3  * This file is part of Tulip (www.tulip-software.org)
4  *
5  * Authors: David Auber and the Tulip development Team
6  * from LaBRI, University of Bordeaux
7  *
8  * Tulip is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU Lesser General Public License
10  * as published by the Free Software Foundation, either version 3
11  * of the License, or (at your option) any later version.
12  *
13  * Tulip is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
16  * See the GNU General Public License for more details.
17  *
18  */
19 
20 #ifndef OBSERVABLE_H
21 #define OBSERVABLE_H
22 
23 #include <tulip/TulipException.h>
24 #include <tulip/ForEach.h>
25 #include <tulip/vectorgraph.h>
26 #include <set>
27 
28 namespace tlp {
29 
30 class Observable;
31 //=======================================
32 /**
33  * @ingroup Observation
34  * @brief Event is the base class for all events used in the Observation mechanism.
35  *
36  * An Event is characterized by its type. The base Event class only carries information as to the type of event, nothing specific.
37  *
38  * Event::DELETE : send directly to all Observers/Listeners, not affected by Observable::holdObservers().
39  * Event::MODIFICATION : sent to all Observers/Listeners. MODIFICATION are first sent to Observers and then to Listeners.
40  * Event::INFORMATION : sent only to Listeners.
41  * Event::INVALID : never sent, used internally for delaying events.
42  *
43  * @see Listener
44  * @see Observer
45  * @see Observable
46  **/
47 class TLP_SCOPE Event {
48  friend class Observable;
49  friend class Graph;
50  friend class PropertyInterface;
51 public:
52  enum EventType {TLP_DELETE = 0, TLP_MODIFICATION, TLP_INFORMATION, TLP_INVALID};
53  virtual ~Event();
54  Observable* sender() const;
55  Event(const Observable &sender, EventType type);
56  EventType type() const {
57  return _type;
58  }
59 private:
60  Event() {}
61  tlp::node _sender;
62  EventType _type;
63 };
64 //=======================================
65 /**
66  * @ingroup Observation
67  * @class ObservableException
68  * @brief ObservableException is the base class of all exceptions sent by the Observable/Listener/Observer system.
69  *
70  * @see Listener
71  * @see Observer
72  * @see Observable
73  **/
74 class TLP_SCOPE ObservableException : public tlp::TulipException {
75 public:
76  ObservableException(const std::string &desc):tlp::TulipException(desc) {
77  }
78 };
79 
80 /**
81  * @ingroup Observation
82  * @brief The Observable class is the base of Tulip's observation system.
83  *
84  * Each class that wishes to send or receive notifications needs to inherit from Observable.
85  *
86  * Tulip has two separate mechanisms of observation, Observers and Listeners.
87  * These two mechanisms work through the same class, the difference lies
88  * in the way an Observer or a Listener is attached to the object whose events
89  * it will receive.
90  *
91  * The Listener is closer to the original pattern, where events are sent directly to the recipient.
92  * The Observer is a twist for performance purposes, it can receive the events in a delayed fashion, and cannot know if the event was just sent or was delayed.
93  *
94  * The purpose of this twist is to allow algorithms that perform a high number of modifications (e.g. creating a grid to route edges,
95  * creating multiple subgraphs with metrics or layouts) to run smoothly without overcrowding the event queue.
96  * As events are sent for every graph modification (e.g. adding a node, deleting a node, setting a value on a node), the sheer quantity of events sent by these
97  * algorithms would cause a big performance hit.
98  *
99  * This can be avoided by using Observable::holdObservers().
100  * This holds all events in a queue and only sends them when Observable::unholdObservers() is called.
101  *
102  * The only exception to this mechanism is the Event::DELETE kind of event, that is never held back.
103  * Think of it as an unmaskable POSIX signal; whatever the connection to the object and the state of holdObserver, this event will get through.
104  * This is used to prevent crashes in the case where and object is listened or observed and is deleted, as it is likely the recipient
105  * keeps a pointer to the now deleted object.
106  *
107  * The Listener however is not affected by the use of Observable::holdObservers() and Observable::unholdObservers().
108  *
109  * The observable Observers and Listeners are internally store in a Graph structure, allowing to easily visualize their connections.
110  * This eases debugging of Observation-related bugs.
111  *
112  * Events are always sent to Listeners first, and then to Observers, even when there is no hold.
113  *
114  * If you wish to receive events, you must inherit from Observable, and implement at least one of the two virtual functions below.
115  *
116  * If you need to receive events without delay, you will be a Listener, and need to implement treatEvent(const Event &message).
117  * You will attach to the object you wish to receive events from using addListener().
118  *
119  * If you can receive events after a delay, you will be an Observer, and need to implement treatEvents(const std::vector<Event> &events).
120  * You will attach to the object you wish to receive events from using addObserver().
121  *
122  **/
123 class TLP_SCOPE Observable {
124  friend class Event;
125 
126 public:
127  /**
128  * @brief Holds back all events until Observable::unholdObservers() is called.
129  *
130  * Listeners are not affected by this function.
131  * Once this function is called, all events heading to an Observer will be held, except DELETE events.
132  * The events are stored in a queue, and will be sent once Observable::unholdObservers() is called.
133  *
134  * It is possible to nest calls to Observable::holdObservers() and Observable::unholdObservers(),
135  * and in this case the events will only be sent when there
136  * have been as many calls to Observable::unholdObservers() as to Observable::holdObservers().
137  *
138  * It is possible to check whether the events are being help by checking the Observable::observersHoldCounter() function.
139  *
140  * @see unholdObservers
141  * @see observersHoldCounter
142  */
143  static void holdObservers();
144  /**
145  * @brief Sends all held events to the Observers.
146  *
147  * Listeners are not affected by this function.
148  *
149  * In debug mode, if the hold counter is less than one when calling this function, an ObservableException will be raised.
150  *
151  * @see holdObservers
152  * @see observersHoldCounter
153  */
154  static void unholdObservers();
155 
156  /**
157  * @brief observersHoldCounter gives the number of times holdObservers() has been called without a matching unholdObservers() call.
158  * @return the number of times holdObservers() has been called without a matching unholdObservers() call.
159  */
160  static unsigned int observersHoldCounter() {
161  return _oHoldCounter;
162  }
163 
164  /**
165  * @brief Adds an Observer to this object.
166  *
167  * The observer will receive events sent by this object, if there is no hold applied.
168  * @param observer The object that will receive events.
169  */
170  void addObserver(Observable * const observer) const;
171 
172  /**
173  * @brief Adds a Listener to this object.
174  *
175  * The Listener will receive events regardless of the state of holdObservers() and unholdObservers().
176  * @param listener The object that will receive events.
177  */
178  void addListener(Observable * const listener) const;
179 
180  /**
181  * @brief Removes an observer from this object.
182  *
183  * The observer will no longer receive events from this object.
184  * @param observer The observer to remove from this object.
185  */
186  void removeObserver(Observable * const observerver) const;
187 
188  /**
189  * @brief Removes a listener from this object.
190  *
191  * The listener will no longer receive events from this object.
192  * @param listener The listener to remove from this object.
193  */
194  void removeListener(Observable * const listener) const;
195 
196  /**
197  * @brief gets the number of sent events.
198  * @return the number of sent events (0 when compiling with -DNDEBUG).
199  */
200  unsigned int getSent() const;
201  \
202  /**
203  * @brief get the number of received events.
204  * @return the number of received events (0 when compiling with -DNDEBUG).
205  */
206  unsigned int getReceived() const;
207 
208  /**
209  * @brief gets the number of observers attached to this object.
210  * @return the number of observers attached to this object.
211  */
212  unsigned int countObservers() const;
213 
214  /**
215  * @brief gets the number of listeners attached to this object.
216  * @return the number of listeners attached to this object.
217  */
218  unsigned int countListeners() const;
219 
220 public:
221  /**
222  * @brief internal function for debugging purpose
223  * If there is no hold currently applied, or no update ongoing, always returns true.
224  * Checks if the object represented by the node has been deleted.
225  * @param n The node to check for life signs.
226  * @return Whether the node is dead.
227  **/
228  static bool isAlive(tlp::node n);
229 
230  /**
231  * @brief internal function for debugging purpose
232  * If called during an update, it is possible the pointed object has been deleted.
233  * use isAlive() to check for this and avoid a crash.
234  * @param n The node representing the object to retrieve.
235  * @return The object represented by the node.
236  **/
237  static Observable* getObject(tlp::node n);
238 
239  /**
240  * @brief internal function for debugging purpose
241  * getScheduled get the number of scheduled events for this Observable.
242  * @param n The node of an Observable object
243  * @return the number of scheduled events involving this Observable as sender or receiver.
244  */
245  static unsigned int getScheduled(tlp::node n);
246 
247  /**
248  * @brief internal function for debugging purpose
249  * Get the node representing this object in the Observable Graph.
250  * @param obs the Observable object
251  * @return the node of the Observable object in the Observable Graph.
252  */
253  static tlp::node getNode(const tlp::Observable* obs);
254 
255  /**
256  * @brief Gets the observation graph.
257  * @return The graph containing a node for each Observable/Observer/Listener, and an edge between connected objects.
258  */
259  static const tlp::VectorGraph& getObservableGraph();
260 
261 protected:
262  Observable();
263  Observable(const Observable &);
264  virtual ~Observable();
265  Observable& operator=(const Observable &);
266 
267  /**
268  * @brief Sends an event to all the Observers/Listeners.
269  * It is higly recommended to check if there are observers/listeners to send the event to before actually sending it
270  * to avoid the overhead of creating too many objects.
271  *
272  * This can be achieved by using the hasOnLookers() function :
273  *
274  * @code
275  * if (hasOnlookers()) {
276  * sendEvent(GraphEvent(*this, GraphEvent::TLP_ADD_NODE, n));
277  * }
278  * @endcode
279  *
280  * @param message The message to send to the listeners/observers.
281  */
282  void sendEvent(const Event &message);
283 
284  /**
285  * @brief This function is called when events are sent to Observers, and Observers only.
286  *
287  * It is passed a vector of all the events that happened since the last call.
288  * If events were held, this vector can be pretty large, and if events were not held it is likely it only contains a single element.
289  *
290  * @param events The events that happened since the last unHoldObservers().
291  */
292  virtual void treatEvents(const std::vector<Event> &events);
293 
294  /**
295  * @brief This function is called when events are sent to the Listeners, and Listeners only.
296  *
297  * Is it passed a reference to the event that just happened.
298  *
299  * @param message The event that was sent.
300  */
301  virtual void treatEvent(const Event &message);
302 
303  /**
304  * @brief Sends the Event::DELETE before the deletion of the subclass and its internal objects.
305  *
306  * The observation system automatically sends the DELETE event when the Observable is deleted, but
307  * in the case you need to access members of the class inheriting from Observable, you need the event
308  * sent *before* the outermost objects are destroyed.
309  *
310  * To achieve this, you can call this function in your destructor, and the DELETE event will be sent.
311  * This will allow your Listeners/Observers to access your members one last time before deletion.
312  *
313  * @warning This function must be called only once per object.
314  * Make sure no other class in the inheritance tree calls this function before adding this call to your destructor.
315  */
316  void observableDeleted();
317 
318  /**
319  * @brief Checks whether there are Observers/Listeners attached to this object.
320  *
321  * Using this function avoids the creation of events that no-one will see :
322  * @code
323  * if (hasOnlookers()) {
324  * sendEvent(GraphEvent(*this, GraphEvent::TLP_ADD_NODE, n));
325  * }
326  * @endcode
327  *
328  * @return
329  */
330  bool hasOnlookers() const;
331 
332  /**
333  * @brief use for old observer tulip compatibility
334  */
335  _DEPRECATED tlp::Iterator<tlp::Observable *> * getObservables() const;
336 
337  /**
338  * @brief use for old observer tulip compatibility
339  */
340  _DEPRECATED void notifyObservers();
341 
342 private:
343  enum OBSERVABLEEDGETYPE {OBSERVABLE = 0x01, OBSERVER = 0x02, LISTENER = 0x04};
344 
345  /**
346  * @brief deleteMsgSent This allows for calling observableDeleted() multiple times safely.
347  */
348  bool deleteMsgSent;
349 
350  /**
351  * @brief queuedEvent Used to prevent unecessary elements insertion in the set of events.
352  */
353  mutable bool queuedEvent;
354 
355  /**
356  * @brief _n The node that represents this object in the ObservableGraph.
357  */
358  tlp::node _n;
359 #ifndef NDEBUG
360  /**
361  * @brief sent The number of sent events
362  */
363  unsigned int sent;
364 
365  /**
366  * @brief received The number of received events.
367  */
368  unsigned int received;
369 #endif
370 
371  /**
372  * @brief return an Iterator on all Onlookers
373  * @warning adding or removing Onlooker to that Observable will devalidate the iterator
374  * @see StableIterator
375  * @see forEach
376  * @see stableForEach
377  */
378  tlp::Iterator<Observable *> *getOnlookers() const;
379 
380  /**
381  * @brief getInObjects Retrieves Inbound objects (observed objects; i.e. Observable).
382  * @return an iterator on 'in' objects (Observable), the iterator guarantees that all objects are alive (not deleted during hold or notify).
383  */
384  tlp::Iterator<tlp::node> *getInObjects() const;
385 
386  /**
387  * @brief getOutObjects Retrieves Outbound objects (observing objects; i.e. Listeners and Observers).
388  * @return an iterator on out objects (Listener/Observer), the iterator garantees that all objects are alive (not deleted during hold or notify).
389  */
390  tlp::Iterator<tlp::node> *getOutObjects() const;
391 
392  /**
393  * @brief addOnlooker Adds an Observer/Listener to this object.
394  *
395  * The added Onlookers will received the next Event sent by the Observable.
396  * In case of nested unholding (higly unlikely), calling that function inside hold/unhold block
397  * can make the Observer receive an event that has been sent before it was Observing the object.
398  *
399  * @param obs The Observer/Listener to add to this object.
400  * @param type The kind of observation (Listener or Observer).
401  */
402  void addOnlooker(const Observable &obs, OBSERVABLEEDGETYPE type) const;
403 
404  /**
405  * @brief removeOnlooker removes an Observer/Listener from this object.
406  *
407  * In case of nested unholding (higly unlikely), calling that function inside a hold/unhold block could
408  * make the Observer not receive an event that was sent when it was connected.
409  *
410  * @warning removing OnLooker that has been created outside of your code can create serious
411  * problems in your application. Objects that are listening/observing could need to receive
412  * the events to work properly.
413  *
414  * @param obs The Observer/Listener to remove
415  * @param type The kind of connection (Listener or Observer).
416  */
417  void removeOnlooker(const Observable &obs, OBSERVABLEEDGETYPE type) const;
418 
419  /**
420  * @brief getNode Get the node representing this object in the ObservableGraph.
421  * @return the node representing that ObservableObject in the ObservableGraph.
422  */
423  tlp::node getNode() const;
424 
425  //static members and methods
426  /**
427  * @brief _oGraph the graph used to store all observers and connection between them.
428  */
429  static tlp::VectorGraph _oGraph;
430 
431  /**
432  * @brief _oPointer a pointer to the object represented by a node
433  */
434  static tlp::NodeProperty<Observable *> _oPointer;
435 
436  /**
437  * @brief _oAlive whether an object has been deleted or not
438  */
439  static tlp::NodeProperty<bool> _oAlive;
440 
441  /**
442  * @brief _oEventsToTreat the count of events scheduled to be treated by an object
443  * the object's associated node is deleted only when this count is null
444  * in order to prevent the node reuse and ensure the _oAlive associated value
445  */
446  static tlp::NodeProperty<unsigned int> _oEventsToTreat;
447 
448  /**
449  * @brief _oType the type of relation between two Observable Objects
450  */
451  static tlp::EdgeProperty<unsigned char> _oType;
452 
453  /**
454  * @brief _oDelayedDelNode store deleted nodes, to remove them at the end of the notify
455  */
456  static std::vector<tlp::node> _oDelayedDelNode;
457 
458  static std::set<std::pair<tlp::node, tlp::node> > _oDelayedEvents;
459 
460  /**
461  * @brief _oNotifying counter of nested notify calls
462  */
463  static unsigned int _oNotifying;
464 
465  /**
466  * @brief _oUnholding counter of nested unhold calls
467  */
468  static unsigned int _oUnholding;
469 
470  /**
471  * @brief _oHoldCounter counter of nested holds
472  */
473  static unsigned int _oHoldCounter;
474 
475  /**
476  * @brief _oInitialized use to initialize oGraph when the library is loaded (nice hack)
477  */
478  static bool _oInitialized;
479 
480  /**
481  * @brief delete nodes from the ObservableGraph that have been preserved to keep coherency and check bad use of the mechanism.
482  */
483  static void updateObserverGraph();
484 
485  /**
486  * @brief getBoundNode
487  * @return the bound node representing this ObservableObject in the ObservableGraph,
488  * if _n is not valid it is then bound to a new added node
489  */
490  tlp::node getBoundNode();
491  bool isBound() const {
492  return _n.isValid();
493  }
494 
495  /**
496  * @brief Trick to init the ObservableGraph properties (called at the loading of the library, during static initialization).
497  */
498  static bool init();
499 };
500 
501 /**
502  * @brief The ObserverHolder class is a convenience class to automatically hold and unhold observers.
503  * It performs a call to Observable::holdObserver() at its creation and a call to Observable::unholdObserver() at its destruction.
504  * You can use it if you have to hold observers in a function with multiple return points to avoid to call Observable::unholdObserver() for each of them.
505  * @code
506  * void myFunc(){
507  * ObserverHolder holder;//Automatically call Observable::holdObserver()
508  *
509  * if(someTest()){
510  * someOperation1();
511  * return;//No need to call Observable::unholdObserver() it will be called with the destruction of the locker object
512  * }
513  *
514  * }
515  * @endcode
516  *
517  */
518 class TLP_SCOPE ObserverHolder {
519 public :
520  ObserverHolder() {
522  }
523  ~ObserverHolder() {
525  }
526 };
527 
528 // deprecated name of this class
529 _DEPRECATED_TYPEDEF(ObserverHolder, ObserverLocker);
530 
531 }
532 
533 #endif
The ObserverHolder class is a convenience class to automatically hold and unhold observers. It performs a call to Observable::holdObserver() at its creation and a call to Observable::unholdObserver() at its destruction. You can use it if you have to hold observers in a function with multiple return points to avoid to call Observable::unholdObserver() for each of them.
Definition: Observable.h:518
PropertyInterface describes the interface of a graph property.
bool isValid() const
isValid checks if the node is valid. An invalid node is a node whose id is UINT_MAX.
Definition: Node.h:90
static unsigned int observersHoldCounter()
observersHoldCounter gives the number of times holdObservers() has been called without a matching unh...
Definition: Observable.h:160
static void holdObservers()
Holds back all events until Observable::unholdObservers() is called.
The node struct represents a node in a Graph object.
Definition: Node.h:39
Event is the base class for all events used in the Observation mechanism.
Definition: Observable.h:47
ObservableException is the base class of all exceptions sent by the Observable/Listener/Observer syst...
Definition: Observable.h:74
static void unholdObservers()
Sends all held events to the Observers.
The Observable class is the base of Tulip&#39;s observation system.
Definition: Observable.h:123