xref: /haiku/src/apps/cortex/support/ObservableLooper.cpp (revision 850f2d1e58cc443f77353c7fc0ce0c158c1fd328)
1 /*
2  * Copyright (c) 1999-2000, Eric Moon.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions, and the following disclaimer.
11  *
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions, and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * 3. The name of the author may not be used to endorse or promote products
17  *    derived from this software without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR
20  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
21  * OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
23  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
26  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
27  * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  */
30 
31 
32 // ObservableLooper.cpp
33 
34 #include "ObservableLooper.h"
35 
36 #include <Debug.h>
37 #include <MessageRunner.h>
38 
39 __USE_CORTEX_NAMESPACE
40 
41 
42 // ---------------------------------------------------------------- //
43 // *** deletion
44 // ---------------------------------------------------------------- //
45 
46 // clients must call release() rather than deleting,
47 // to ensure that all observers are notified of the
48 // object's demise.  if the object has already been
49 // released, return an error.
50 
51 status_t ObservableLooper::release() {
52 
53 	// +++++ what if I'm not running?
54 	// +++++ is a lock necessary?
55 
56 	if(isReleased())
57 		return B_NOT_ALLOWED;
58 
59 	// send request through proper channels
60 	BMessenger(this).SendMessage(B_QUIT_REQUESTED);
61 
62 	return B_OK;
63 }
64 
65 // ---------------------------------------------------------------- //
66 // *** ctor/dtor
67 // ---------------------------------------------------------------- //
68 
69 ObservableLooper::~ObservableLooper() {
70 	if(CountTargets()) {
71 		PRINT((
72 			"*** ~ObservableLooper() '%s': %" B_PRId32 " observers remain\n",
73 			Name(), CountTargets()));
74 	}
75 	if(m_executioner)
76 		delete m_executioner;
77 }
78 
79 ObservableLooper::ObservableLooper(
80 	const char*							name,
81 	int32										priority,
82 	int32										portCapacity,
83 	bigtime_t								quitTimeout) :
84 	BLooper(name, priority, portCapacity),
85 	m_quitTimeout(quitTimeout),
86 	m_executioner(0),
87 	m_quitting(false) {}
88 
89 ObservableLooper::ObservableLooper(
90 	BMessage*								archive) :
91 	BLooper(archive),
92 	m_quitTimeout(B_INFINITE_TIMEOUT),
93 	m_executioner(0),
94 	m_quitting(false) {
95 
96 	archive->FindInt64("quitTimeout", (int64*)&m_quitTimeout);
97 }
98 
99 // ---------------------------------------------------------------- //
100 // *** accessors
101 // ---------------------------------------------------------------- //
102 
103 bool ObservableLooper::isReleased() const {
104 	return m_quitting;
105 }
106 
107 // ---------------------------------------------------------------- //
108 // *** hooks
109 // ---------------------------------------------------------------- //
110 
111 // sends M_OBSERVER_ADDED to the newly-added observer
112 void ObservableLooper::observerAdded(
113 	const BMessenger&				observer) {
114 
115 	BMessage m(M_OBSERVER_ADDED);
116 	m.AddMessenger("target", BMessenger(this));
117 	observer.SendMessage(&m);
118 }
119 
120 // sends M_OBSERVER_REMOVED to the newly-removed observer
121 void ObservableLooper::observerRemoved(
122 	const BMessenger&				observer) {
123 
124 	BMessage m(M_OBSERVER_REMOVED);
125 	m.AddMessenger("target", BMessenger(this));
126 	observer.SendMessage(&m);
127 }
128 
129 // ---------------------------------------------------------------- //
130 // *** internal operations
131 // ---------------------------------------------------------------- //
132 
133 status_t ObservableLooper::notify(
134 	BMessage*								message) {
135 	ASSERT(IsLocked());
136 
137 	return Invoke(message);
138 }
139 
140 // sends M_RELEASE_OBSERVABLE
141 void ObservableLooper::notifyRelease() {
142 	BMessage m(M_RELEASE_OBSERVABLE);
143 	m.AddMessenger("target", BMessenger(this));
144 	notify(&m);
145 }
146 
147 // ---------------------------------------------------------------- //
148 // *** BLooper
149 // ---------------------------------------------------------------- //
150 
151 void ObservableLooper::Quit() {
152 	ASSERT(IsLocked());
153 
154 	if(QuitRequested()) {
155 		releaseComplete();
156 		_inherited::Quit();
157 	}
158 	else
159 		Unlock();
160 }
161 
162 bool ObservableLooper::QuitRequested() {
163 
164 	if(CountTargets()) {
165 		if(!m_quitting) {
166 			m_quitting = true;
167 
168 			// no release request yet sent
169 			notifyRelease();
170 
171 			if(m_quitTimeout != B_INFINITE_TIMEOUT) {
172 				// Initiate a timer to force quit -- if an observer
173 				// has died, it shouldn't take me down with it.
174 				ASSERT(!m_executioner);
175 				m_executioner = new BMessageRunner(
176 					BMessenger(this),
177 					new BMessage(M_KILL_OBSERVABLE),
178 					m_quitTimeout,
179 					1);
180 			}
181 		}
182 
183 		// targets remain, so don't quit.
184 		return false;
185 	}
186 
187 	// okay to quit
188 	return true;
189 }
190 
191 // ---------------------------------------------------------------- //
192 // *** BHandler
193 // ---------------------------------------------------------------- //
194 
195 void ObservableLooper::MessageReceived(
196 	BMessage*								message) {
197 
198 //	PRINT((
199 //		"### ObservableLooper::MessageReceived()\n"));
200 //	message->PrintToStream();
201 
202 	switch(message->what) {
203 		case M_ADD_OBSERVER:
204 			_handleAddObserver(message);
205 			break;
206 
207 		case M_REMOVE_OBSERVER:
208 			_handleRemoveObserver(message);
209 			break;
210 
211 		case M_KILL_OBSERVABLE:
212 			releaseComplete();
213 			BLooper::Quit();
214 			break;
215 
216 		default:
217 			_inherited::MessageReceived(message);
218 	}
219 }
220 
221 // ---------------------------------------------------------------- //
222 // *** BArchivable
223 // ---------------------------------------------------------------- //
224 
225 status_t ObservableLooper::Archive(
226 	BMessage*								archive,
227 	bool										deep) const {
228 
229 	ASSERT(IsLocked());
230 
231 	// can't archive an object in limbo
232 	if(m_quitting)
233 		return B_NOT_ALLOWED;
234 
235 	status_t err = _inherited::Archive(archive, deep);
236 	if(err < B_OK)
237 		return err;
238 
239 	archive->AddInt64("quitTimeout", m_quitTimeout);
240 	return B_OK;
241 }
242 
243 // ---------------------------------------------------------------- //
244 // implementation
245 // ---------------------------------------------------------------- //
246 
247 void ObservableLooper::_handleAddObserver(
248 	BMessage*								message) {
249 
250 	BMessage reply;
251 
252 	BMessenger observer;
253 	status_t err = message->FindMessenger(
254 		"observer", &observer);
255 	if(err < B_OK) {
256 		PRINT((
257 			"* ObservableLooper::_handleAddObserver(): no observer specified!\n"));
258 		// send reply? +++++
259 		return;
260 	}
261 
262 	// at this point, a reply of some sort will be sent
263 	reply.AddMessenger("target", BMessenger(this));
264 	reply.AddMessenger("observer", observer);
265 
266 	if(m_quitting) {
267 		// already quitting
268 		reply.what = M_BAD_TARGET;
269 	}
270 	else if(IndexOfTarget(observer.Target(0)) != -1) {
271 		// observer already added
272 		reply.what = M_BAD_OBSERVER;
273 	}
274 	else {
275 		// add it
276 		err = AddTarget(observer.Target(0));
277 		ASSERT(err == B_OK);
278 		reply.what = M_OBSERVER_ADDED;
279 	}
280 
281 	// send reply
282 	message->SendReply(&reply);
283 
284 	// call hook
285 	observerAdded(observer);
286 }
287 
288 void ObservableLooper::_handleRemoveObserver(
289 	BMessage*								message) {
290 
291 //	PRINT(("ObservableLooper::_handleRemoveObserver():\n"
292 //		"  %ld targets\n", CountTargets()));
293 	BMessage reply;
294 
295 	BMessenger observer;
296 	status_t err = message->FindMessenger(
297 		"observer", &observer);
298 	if(err < B_OK) {
299 		PRINT((
300 			"* ObservableLooper::_handleRemoveObserver(): no observer specified!\n"));
301 		// send reply? +++++
302 		return;
303 	}
304 
305 	// at this point, a reply of some sort will be sent
306 	reply.AddMessenger("target", BMessenger(this));
307 	reply.AddMessenger("observer", observer);
308 
309 	int32 index = IndexOfTarget(observer.Target(0));
310 	if(index == -1) {
311 		reply.what = M_BAD_OBSERVER;
312 	}
313 	else {
314 		RemoveTarget(index);
315 		reply.what = M_OBSERVER_REMOVED;
316 	}
317 
318 	message->SendReply(&reply);
319 
320 	// call hook
321 	observerRemoved(observer);
322 
323 	// time to shut down?
324 	if(m_quitting && !CountTargets()) {
325 		releaseComplete();
326 		BLooper::Quit();
327 	}
328 }
329 
330 // END -- ObservableLooper.cpp --
331