source: src/Observer/Relay.cpp@ 9e619e

Last change on this file since 9e619e was 959c82, checked in by Frederik Heber <heber@…>, 10 years ago

Extracted all static Observable maps (and mutex) into singleton GlobalObservableInfo.

  • this way we may safely control is destruction, i.e. it is always valid as it boils down to a primitive void pointer which does not need to be destroyed or constructed.
  • Minimized code where mutex is locked.
  • Property mode set to 100644
File size: 10.3 KB
RevLine 
[e2e035e]1/*
2 * Project: MoleCuilder
3 * Description: creates and alters molecular systems
4 * Copyright (C) 2010 University of Bonn. All rights reserved.
5 * Please see the LICENSE file or "Copyright notice" in builder.cpp for details.
6 */
7
8/*
9 * Relay.cpp
10 *
11 * Created on: Dec 1, 2011
12 * Author: heber
13 */
14
15// include config.h
16#ifdef HAVE_CONFIG_H
17#include <config.h>
18#endif
19
[9b8fa4]20#include "CodePatterns/MemDebug.hpp"
[e2e035e]21
[9b8fa4]22#include "CodePatterns/Observer/Relay.hpp"
[e2e035e]23
[9b8fa4]24#include "CodePatterns/Assert.hpp"
25#include "CodePatterns/Observer/Channels.hpp"
26#include "CodePatterns/Observer/Notification.hpp"
[d85532]27
[959c82]28#include <boost/thread/locks.hpp>
29#include <boost/thread/recursive_mutex.hpp>
[e2e035e]30
31/** Constructor for class Relay.
32 */
[d85532]33Relay::Relay(std::string name) :
34 Observable(name),
35 Updater(NULL)
[e2e035e]36{
37#ifdef LOG_OBSERVER
38 observerLog().addName(this,name);
[2c11c1]39 observerLog().addMessage() << "++ Creating Relay " << observerLog().getName(this);
[e2e035e]40#endif
41}
42
43/** Destructor for class Relay.
44 * When an observable is deleted, we let all our observers know. \sa Relay::subjectKilled().
45 */
46Relay::~Relay()
47{
48#ifdef LOG_OBSERVER
[2c11c1]49 observerLog().addMessage() << "-- Destroying Relay " << observerLog().getName(this);
[e2e035e]50#endif
[b760ac5]51 // killing subjects is done by Observables' dstor
[e2e035e]52}
53
54
[b760ac5]55
56/** Sign on an Observer to this Observable.
[959c82]57 * Puts \a *target into Observable::(GlobalObservableInfo::getInstance().getcallTable()) list.
[b760ac5]58 * \param *target Observer
59 * \param priority number in [-20,20]
60 */
[959c82]61void Relay::signOn(Observer *target, GlobalObservableInfo::PriorityLevel priority) const
[b760ac5]62{
63#ifdef LOG_OBSERVER
64 observerLog().addMessage() << "@@ Signing on " << observerLog().getName(target)
65 << " to "
66 << observerLog().getName(const_cast<Observable *>(static_cast<const Observable * const>(this)));
67#endif
68 bool res = false;
[959c82]69 boost::recursive_mutex::scoped_lock lock(GlobalObservableInfo::getInstance().getObservablesMapMutex());
70 GlobalObservableInfo::callees_t &callees = (GlobalObservableInfo::getInstance().getcallTable())[const_cast<Observable *>(static_cast<const Observable * const>(this))];
[b760ac5]71
[959c82]72 GlobalObservableInfo::callees_t::iterator iter;
[b760ac5]73 for(iter=callees.begin();iter!=callees.end();++iter){
74 res |= ((*iter).second == target);
75 }
76 if(!res)
77 callees.insert(std::pair<int,Observer*>(priority.level,target));
78}
79
80/** Sign off an Observer from this Observable.
[959c82]81 * Removes \a *target from Observable::(GlobalObservableInfo::getInstance().getcallTable()) list.
[b760ac5]82 * \param *target Observer
83 */
84void Relay::signOff(Observer *target) const
85{
[959c82]86 boost::recursive_mutex::scoped_lock lock(GlobalObservableInfo::getInstance().getObservablesMapMutex());
87 GlobalObservableInfo::calltable_t &callTable = GlobalObservableInfo::getInstance().getcallTable();
[b760ac5]88 ASSERT(callTable.count(const_cast<Observable *>(static_cast<const Observable * const>(this))),
89 "Relay::signOff() - called for an Observable without Observers.");
90#ifdef LOG_OBSERVER
91 observerLog().addMessage() << "** Signing off " << observerLog().getName(target)
92 << " from "
93 << observerLog().getName(const_cast<Observable *>(static_cast<const Observable * const>(this)));
94#endif
[959c82]95 GlobalObservableInfo::callees_t &callees = callTable[const_cast<Observable *>(static_cast<const Observable * const>(this))];
[b760ac5]96
[959c82]97 GlobalObservableInfo::callees_t::iterator iter;
98 GlobalObservableInfo::callees_t::iterator deliter;
[b760ac5]99 for(iter=callees.begin();iter!=callees.end();) {
100 if((*iter).second == target) {
101 callees.erase(iter++);
102 }
103 else {
104 ++iter;
105 }
106 }
107 if(callees.empty()){
108 callTable.erase(const_cast<Observable *>(static_cast<const Observable * const>(this)));
109 }
110}
111
[959c82]112void Relay::signOn(Observer *target, size_t channelno, GlobalObservableInfo::PriorityLevel priority) const
[b760ac5]113{
114 Notification_ptr notification = getChannel(channelno);
[454bc54]115 notification->addObserver(target, priority.level);
[b760ac5]116}
117
118void Relay::signOff(Observer *target, size_t channelno) const
119{
120 Notification_ptr notification = getChannel(channelno);
121 notification->removeObserver(target);
122}
123
[e2e035e]124/** Notify all Observers of changes.
[959c82]125 * Puts \a *this into Relay::(GlobalObservableInfo::getInstance().getbusyObservables()), calls Observer::update() for all in callee_t
[e2e035e]126 * and removes from busy list.
127 */
128void Relay::notifyAll() {
[d85532]129 ASSERT(Updater != NULL,
130 "Relay::notifyAll() called while Updater is NULL.");
[e2e035e]131 // we are busy notifying others right now
132 // add ourselves to the list of busy subjects to enable circle detection
[959c82]133 {
134 boost::recursive_mutex::scoped_lock lock(GlobalObservableInfo::getInstance().getObservablesMapMutex());
135 (GlobalObservableInfo::getInstance().getbusyObservables()).insert(this);
136 }
[e2e035e]137 // see if anyone has signed up for observation
138 // and call all observers
139 try {
[959c82]140 boost::recursive_mutex::scoped_lock lock(GlobalObservableInfo::getInstance().getObservablesMapMutex());
141 GlobalObservableInfo::calltable_t& callTable = GlobalObservableInfo::getInstance().getcallTable();
142 const bool callTable_contains = callTable.count(this);
143 if(callTable_contains) {
[e2e035e]144 // elements are stored sorted by keys in the multimap
145 // so iterating over it gives us a the callees sorted by
146 // the priorities
[959c82]147 // copy such that signOff() within receiving update() does not affect iterating
148 // this is because within the same thread and with the updateKilled() signOff() may be
149 // called and when executed it modifies targets
150 boost::recursive_mutex::scoped_lock lock(GlobalObservableInfo::getInstance().getObservablesMapMutex());
151 GlobalObservableInfo::callees_t callees = callTable[this];
152 GlobalObservableInfo::callees_t::iterator iter;
[e2e035e]153 for(iter=callees.begin();iter!=callees.end();++iter){
154#ifdef LOG_OBSERVER
155 observerLog().addMessage() << "-> " << observerLog().getName(this)
156 << " is relaying update from " << observerLog().getName(Updater)
157 << " to " << observerLog().getName((*iter).second)
[2c11c1]158 << " (priority=" << (*iter).first << ")";
[e2e035e]159#endif
160 (*iter).second->update(Updater);
161 }
162 }
163 }
164 ASSERT_NOCATCH("Exception thrown from Observer Update");
165
[959c82]166 // send out all (GlobalObservableInfo::getInstance().getnotifications()) that need to be done
167 {
168 boost::recursive_mutex::scoped_lock lock(GlobalObservableInfo::getInstance().getObservablesMapMutex());
169 GlobalObservableInfo::notificationSet currentNotifications =
170 (GlobalObservableInfo::getInstance().getnotifications())[Updater];
171 for(GlobalObservableInfo::notificationSet::iterator it = currentNotifications.begin();
172 it != currentNotifications.end();++it){
173 (*it)->notifyAll(Updater);
174 }
[e2e035e]175 }
176
[959c82]177 {
178 boost::recursive_mutex::scoped_lock lock(GlobalObservableInfo::getInstance().getObservablesMapMutex());
179 (GlobalObservableInfo::getInstance().getnotifications()).erase(Updater);
[e2e035e]180
[959c82]181 // done with notification, we can leave the set of busy subjects
182 (GlobalObservableInfo::getInstance().getbusyObservables()).erase(this);
183 }
[e2e035e]184}
185
186
187/** Handles passing on updates from sub-Relays.
188 * Mimicks basically the Observer::update() function.
189 *
190 * \param *publisher The \a *this we observe.
191 */
192void Relay::update(Observable *publisher) {
193 // circle detection
[959c82]194 bool circle_present = false;
195 {
196 boost::recursive_mutex::scoped_lock lock(GlobalObservableInfo::getInstance().getObservablesMapMutex());
197 std::set<Observable*>& busyObservables = GlobalObservableInfo::getInstance().getbusyObservables();
198 circle_present = busyObservables.find(this)!=busyObservables.end();
199 }
200 if(circle_present) {
[e2e035e]201 // somehow a circle was introduced... we were busy notifying our
202 // observers, but still we are called by one of our sub-Relays
203 // we cannot be sure observation will still work at this point
204 ASSERT(0,"Circle detected in observation-graph.\n"
205 "Observation-graph always needs to be a DAG to work correctly!\n"
206 "Please check your observation code and fix this!\n");
207 return;
208 }
209 else {
210 // see if we are in the process of changing ourselves
211 // if we are changing ourselves at the same time our sub-observables change
212 // we do not need to publish all the changes at each time we are called
[959c82]213 boost::recursive_mutex::scoped_lock lock(GlobalObservableInfo::getInstance().getObservablesMapMutex());
214 std::map<Observable*, int>& depth = GlobalObservableInfo::getInstance().getdepth();
215 const bool depth_contains = depth.find(this)==depth.end();
216 if(depth_contains) {
[e2e035e]217#ifdef LOG_OBSERVER
218 observerLog().addMessage() << "-* Update from " << observerLog().getName(publisher)
[d85532]219 << " relayed by " << observerLog().getName(this);
[e2e035e]220#endif
221 Updater = publisher;
222 notifyAll();
[b760ac5]223 Updater = NULL;
[e2e035e]224 }
225 else{
226#ifdef LOG_OBSERVER
227 observerLog().addMessage() << "-| Update from " << observerLog().getName(publisher)
[d85532]228 << " not relayed by " << observerLog().getName(this);
[e2e035e]229#endif
230 }
231 }
232}
233
[959c82]234/** Method for receiving specialized (GlobalObservableInfo::getInstance().getnotifications()).
[b760ac5]235 *
236 * \param *publisher The \a *this we observe.
237 * \param notification type of notification
238 */
239void Relay::recieveNotification(Observable *publisher, Notification_ptr notification)
240{
241 Updater = publisher;
[959c82]242 bool contains_channels = false;
243 {
244 boost::recursive_mutex::scoped_lock lock(GlobalObservableInfo::getInstance().getObservablesMapMutex());
245 contains_channels = NotificationChannels.find(this) != NotificationChannels.end();
246 }
247 if (contains_channels) {
[444a2f]248 const size_t channelno = notification->getChannelNo();
[959c82]249 Notification *mynotification = NULL;
250 {
251 boost::recursive_mutex::scoped_lock lock(GlobalObservableInfo::getInstance().getObservablesMapMutex());
252 ChannelMap::const_iterator iter = NotificationChannels.find(this);
253 const Channels *myChannels = iter->second;
254 mynotification = myChannels->getChannel(channelno);
255 }
[444a2f]256 ASSERT(mynotification != NULL,
257 "Relay::recieveNotification() - this relay does not have a notification no "+toString(channelno)+".");
258 mynotification->notifyAll(Updater);
259 Updater = NULL;
260 } else {
261 // note that this relay does not seem to have any channels
262 }
[b760ac5]263}
[e2e035e]264
265/** Handles sub-observables that just got killed
266 * when an sub-observerable dies we usually don't need to do anything
267 * \param *publisher Sub-Relay.
268 */
[959c82]269void Relay::subjectKilled(Observable *publisher)
270{
[e2e035e]271}
272
Note: See TracBrowser for help on using the repository browser.