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
51status_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
69ObservableLooper::~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
79ObservableLooper::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
89ObservableLooper::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
103bool ObservableLooper::isReleased() const {
104	return m_quitting;
105}
106
107// ---------------------------------------------------------------- //
108// *** hooks
109// ---------------------------------------------------------------- //
110
111// sends M_OBSERVER_ADDED to the newly-added observer
112void 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
121void 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
133status_t ObservableLooper::notify(
134	BMessage*								message) {
135	ASSERT(IsLocked());
136
137	return Invoke(message);
138}
139
140// sends M_RELEASE_OBSERVABLE
141void ObservableLooper::notifyRelease() {
142	BMessage m(M_RELEASE_OBSERVABLE);
143	m.AddMessenger("target", BMessenger(this));
144	notify(&m);
145}
146
147// ---------------------------------------------------------------- //
148// *** BLooper
149// ---------------------------------------------------------------- //
150
151void ObservableLooper::Quit() {
152	ASSERT(IsLocked());
153
154	if(QuitRequested()) {
155		releaseComplete();
156		_inherited::Quit();
157	}
158	else
159		Unlock();
160}
161
162bool 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
195void 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
225status_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
247void 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
288void 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