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