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