Tulip  5.3.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
152  * called.
153  *
154  * It is possible to nest calls to Observable::holdObservers() and Observable::unholdObservers(),
155  * and in this case the events will only be sent when there
156  * have been as many calls to Observable::unholdObservers() as to Observable::holdObservers().
157  *
158  * It is possible to check whether the events are being help by checking the
159  * Observable::observersHoldCounter() function.
160  *
161  * @see unholdObservers
162  * @see observersHoldCounter
163  */
164  static void holdObservers();
165  /**
166  * @brief Sends all held events to the Observers.
167  *
168  * Listeners are not affected by this function.
169  *
170  * In debug mode, if the hold counter is less than one when calling this function, an
171  * ObservableException will be raised.
172  *
173  * @see holdObservers
174  * @see observersHoldCounter
175  */
176  static void unholdObservers();
177 
178  /**
179  * @brief observersHoldCounter gives the number of times holdObservers() has been called without a
180  * matching unholdObservers() call.
181  * @return the number of times holdObservers() has been called without a matching
182  * unholdObservers() call.
183  */
184  static unsigned int observersHoldCounter() {
185  return _oHoldCounter;
186  }
187 
188  /**
189  * @brief disable the whole event notification mechanism
190  * Until a call to enableEventNotification(),
191  * all sent events will be lost,
192  * except TLP_DELETE events which are synchronously processed,
193  * in order to void dangling pointers.
194  * @warning this function is a first step to allow parallel
195  * computation (of Tulip properties, for example). Use it at your
196  * own risk and avoid the sent of TLP_DELETE events whose management
197  * is not thread safe.
198  */
199  inline static void disableEventNotification() {
200  _oDisabled = true;
201  }
202 
203  /**
204  * @brief re-enable the whole event notification mechanism
205  * All events sent since a previous call to enableEventNotification(),
206  * are definitively lost.
207  */
208  inline static void enableEventNotification() {
209  _oDisabled = false;
210  }
211 
212  /**
213  * @brief Adds an Observer to this object.
214  *
215  * The observer will receive events sent by this object, if there is no hold applied.
216  * @param observer The object that will receive events.
217  */
218  void addObserver(Observable *const observer) const;
219 
220  /**
221  * @brief Adds a Listener to this object.
222  *
223  * The Listener will receive events regardless of the state of holdObservers() and
224  * unholdObservers().
225  * @param listener The object that will receive events.
226  */
227  void addListener(Observable *const listener) const;
228 
229  /**
230  * @brief Removes an observer from this object.
231  *
232  * The observer will no longer receive events from this object.
233  * @param observer The observer to remove from this object.
234  */
235  void removeObserver(Observable *const observerver) const;
236 
237  /**
238  * @brief Removes a listener from this object.
239  *
240  * The listener will no longer receive events from this object.
241  * @param listener The listener to remove from this object.
242  */
243  void removeListener(Observable *const listener) const;
244 
245  /**
246  * @brief gets the number of sent events.
247  * @return the number of sent events (0 when compiling with -DNDEBUG).
248  */
249  unsigned int getSent() const;
250 
251  /**
252  * @brief get the number of received events.
253  * @return the number of received events (0 when compiling with -DNDEBUG).
254  */
255  unsigned int getReceived() const;
256 
257  /**
258  * @brief gets the number of observers attached to this object.
259  * @return the number of observers attached to this object.
260  */
261  unsigned int countObservers() const;
262 
263  /**
264  * @brief gets the number of listeners attached to this object.
265  * @return the number of listeners attached to this object.
266  */
267  unsigned int countListeners() const;
268 
269 public:
270  /**
271  * @brief internal function for debugging purpose
272  * If there is no hold currently applied, or no update ongoing, always returns true.
273  * Checks if the object represented by the node has been deleted.
274  * @param n The node to check for life signs.
275  * @return Whether the node is dead.
276  **/
277  static bool isAlive(tlp::node n);
278 
279  /**
280  * @brief internal function for debugging purpose
281  * If called during an update, it is possible the pointed object has been deleted.
282  * use isAlive() to check for this and avoid a crash.
283  * @param n The node representing the object to retrieve.
284  * @return The object represented by the node.
285  **/
286  static Observable *getObject(tlp::node n);
287 
288  /**
289  * @brief internal function for debugging purpose
290  * getScheduled get the number of scheduled events for this Observable.
291  * @param n The node of an Observable object
292  * @return the number of scheduled events involving this Observable as sender or receiver.
293  */
294  static unsigned int getScheduled(tlp::node n);
295 
296  /**
297  * @brief internal function for debugging purpose
298  * Get the node representing this object in the Observable Graph.
299  * @param obs the Observable object
300  * @return the node of the Observable object in the Observable Graph.
301  */
302  static tlp::node getNode(const tlp::Observable *obs);
303 
304  /**
305  * @brief Gets the observation graph.
306  * @return The graph containing a node for each Observable/Observer/Listener, and an edge between
307  * connected objects.
308  */
309  static const tlp::VectorGraph &getObservableGraph();
310 
311 protected:
312  Observable();
313  Observable(const Observable &);
314  virtual ~Observable();
315  Observable &operator=(const Observable &);
316 
317  /**
318  * @brief Sends an event to all the Observers/Listeners.
319  * It is higly recommended to check if there are observers/listeners to send the event to before
320  * actually sending it
321  * to avoid the overhead of creating too many objects.
322  *
323  * This can be achieved by using the hasOnLookers() function :
324  *
325  * @code
326  * if (hasOnlookers()) {
327  * sendEvent(GraphEvent(*this, GraphEvent::TLP_ADD_NODE, n));
328  * }
329  * @endcode
330  *
331  * @param message The message to send to the listeners/observers.
332  */
333  void sendEvent(const Event &message);
334 
335  /**
336  * @brief This function is called when events are sent to Observers, and Observers only.
337  *
338  * It is passed a vector of all the events that happened since the last call.
339  * If events were held, this vector can be pretty large, and if events were not held it is likely
340  * it only contains a single element.
341  *
342  * @param events The events that happened since the last unHoldObservers().
343  */
344  virtual void treatEvents(const std::vector<Event> &events);
345 
346  /**
347  * @brief This function is called when events are sent to the Listeners, and Listeners only.
348  *
349  * Is it passed a reference to the event that just happened.
350  *
351  * @param message The event that was sent.
352  */
353  virtual void treatEvent(const Event &message);
354 
355  /**
356  * @brief Sends the Event::DELETE before the deletion of the subclass and its internal objects.
357  *
358  * The observation system automatically sends the DELETE event when the Observable is deleted, but
359  * in the case you need to access members of the class inheriting from Observable, you need the
360  * event
361  * sent *before* the outermost objects are destroyed.
362  *
363  * To achieve this, you can call this function in your destructor, and the DELETE event will be
364  * sent.
365  * This will allow your Listeners/Observers to access your members one last time before deletion.
366  *
367  * @warning This function must be called only once per object.
368  * Make sure no other class in the inheritance tree calls this function before adding this call to
369  * your destructor.
370  */
371  void observableDeleted();
372 
373  /**
374  * @brief Checks whether there are Observers/Listeners attached to this object.
375  *
376  * Using this function avoids the creation of events that no-one will see :
377  * @code
378  * if (hasOnlookers()) {
379  * sendEvent(GraphEvent(*this, GraphEvent::TLP_ADD_NODE, n));
380  * }
381  * @endcode
382  *
383  * @return
384  */
385  bool hasOnlookers() const;
386 
387 private:
388  enum OBSERVABLEEDGETYPE { OBSERVABLE = 0x01, OBSERVER = 0x02, LISTENER = 0x04 };
389 
390  /**
391  * @brief deleteMsgSent This allows for calling observableDeleted() multiple times safely.
392  */
393  bool deleteMsgSent;
394 
395  /**
396  * @brief queuedEvent Used to prevent unecessary elements insertion in the set of events.
397  */
398  mutable bool queuedEvent;
399 
400  /**
401  * @brief _n The node that represents this object in the ObservableGraph.
402  */
403  tlp::node _n;
404 #ifndef NDEBUG
405  /**
406  * @brief sent The number of sent events
407  */
408  unsigned int sent;
409 
410  /**
411  * @brief received The number of received events.
412  */
413  unsigned int received;
414 #endif
415 
416  /**
417  * @brief return an Iterator on all Onlookers
418  * @warning adding or removing Onlooker to that Observable will devalidate the iterator
419  * @see StableIterator
420  */
421  tlp::Iterator<Observable *> *getOnlookers() const;
422 
423  /**
424  * @brief getInObjects Retrieves Inbound objects (observed objects; i.e. Observable).
425  * @return an iterator on 'in' objects (Observable), the iterator guarantees that all objects are
426  * alive (not deleted during hold or notify).
427  */
428  tlp::Iterator<tlp::node> *getInObjects() const;
429 
430  /**
431  * @brief getOutObjects Retrieves Outbound objects (observing objects; i.e. Listeners and
432  * Observers).
433  * @return an iterator on out objects (Listener/Observer), the iterator garantees that all objects
434  * are alive (not deleted during hold or notify).
435  */
436  tlp::Iterator<tlp::node> *getOutObjects() const;
437 
438  /**
439  * @brief addOnlooker Adds an Observer/Listener to this object.
440  *
441  * The added Onlookers will received the next Event sent by the Observable.
442  * In case of nested unholding (higly unlikely), calling that function inside hold/unhold block
443  * can make the Observer receive an event that has been sent before it was Observing the object.
444  *
445  * @param obs The Observer/Listener to add to this object.
446  * @param type The kind of observation (Listener or Observer).
447  */
448  void addOnlooker(const Observable &obs, OBSERVABLEEDGETYPE type) const;
449 
450  /**
451  * @brief removeOnlooker removes an Observer/Listener from this object.
452  *
453  * In case of nested unholding (higly unlikely), calling that function inside a hold/unhold block
454  * could
455  * make the Observer not receive an event that was sent when it was connected.
456  *
457  * @warning removing OnLooker that has been created outside of your code can create serious
458  * problems in your application. Objects that are listening/observing could need to receive
459  * the events to work properly.
460  *
461  * @param obs The Observer/Listener to remove
462  * @param type The kind of connection (Listener or Observer).
463  */
464  void removeOnlooker(const Observable &obs, OBSERVABLEEDGETYPE type) const;
465 
466  /**
467  * @brief getNode Get the node representing this object in the ObservableGraph.
468  * @return the node representing that ObservableObject in the ObservableGraph.
469  */
470  tlp::node getNode() const;
471 
472  // static members and methods
473  /**
474  * @brief _oGraph the graph used to store all observers and connection between them.
475  */
476  static tlp::VectorGraph _oGraph;
477 
478  /**
479  * @brief _oPointer a pointer to the object represented by a node
480  */
481  static tlp::NodeProperty<Observable *> _oPointer;
482 
483  /**
484  * @brief _oAlive whether an object has been deleted or not
485  */
486  static tlp::NodeProperty<bool> _oAlive;
487 
488  /**
489  * @brief _oEventsToTreat the count of events scheduled to be treated by an object
490  * the object's associated node is deleted only when this count is null
491  * in order to prevent the node reuse and ensure the _oAlive associated value
492  */
493  static tlp::NodeProperty<unsigned int> _oEventsToTreat;
494 
495  /**
496  * @brief _oType the type of relation between two Observable Objects
497  */
498  static tlp::EdgeProperty<unsigned char> _oType;
499 
500  /**
501  * @brief _oDelayedDelNode store deleted nodes, to remove them at the end of the notify
502  */
503  static std::vector<tlp::node> _oDelayedDelNode;
504 
505  static std::set<std::pair<tlp::node, tlp::node>> _oDelayedEvents;
506 
507  /**
508  * @brief _oNotifying counter of nested notify calls
509  */
510  static unsigned int _oNotifying;
511 
512  /**
513  * @brief _oUnholding counter of nested unhold calls
514  */
515  static unsigned int _oUnholding;
516 
517  /**
518  * @brief _oHoldCounter counter of nested holds
519  */
520  static unsigned int _oHoldCounter;
521 
522  /**
523  * @brief _oInitialized used to initialize oGraph when the library is loaded (nice hack)
524  */
525  static bool _oInitialized;
526 
527  /**
528  * @brief _oDisabled used to disable the events notification
529  */
530  static bool _oDisabled;
531 
532  /**
533  * @brief delete nodes from the ObservableGraph that have been preserved to keep coherency and
534  * check bad use of the mechanism.
535  */
536  static void updateObserverGraph();
537 
538  /**
539  * @brief getBoundNode
540  * @return the bound node representing this ObservableObject in the ObservableGraph,
541  * if _n is not valid it is then bound to a new added node
542  */
543  tlp::node getBoundNode();
544  bool isBound() const {
545  return _n.isValid();
546  }
547 
548  /**
549  * @brief Trick to init the ObservableGraph properties (called at the loading of the library,
550  * during static initialization).
551  */
552  static bool init();
553 };
554 
555 /**
556  * @brief The ObserverHolder class is a convenience class to automatically hold and unhold
557  * observers.
558  * It performs a call to Observable::holdObserver() at its creation and a call to
559  * Observable::unholdObserver() at its destruction.
560  * You can use it if you have to hold observers in a function with multiple return points to avoid
561  * to call Observable::unholdObserver() for each of them.
562  * @code
563  * void myFunc(){
564  * ObserverHolder holder;//Automatically call Observable::holdObserver()
565  *
566  * if(someTest()){
567  * someOperation1();
568  * return;//No need to call Observable::unholdObserver() it will be called with the destruction
569  * of the locker object
570  * }
571  *
572  * }
573  * @endcode
574  *
575  */
576 class TLP_SCOPE ObserverHolder {
577 public:
578  ObserverHolder() {
580  }
581  ~ObserverHolder() {
583  }
584 };
585 } // namespace tlp
586 
587 #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:199
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:576
Interface for Tulip iterators. Allows basic iteration operations only.
Definition: Graph.h:41
PropertyInterface describes the interface of a graph property.
static unsigned int observersHoldCounter()
observersHoldCounter gives the number of times holdObservers() has been called without a matching unh...
Definition: Observable.h:184
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
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 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:208