xref: /haiku/src/kits/app/Looper.cpp (revision 9eb55bc1d104b8fda80898f8b25c94d8000c8255)
1 //------------------------------------------------------------------------------
2 //	Copyright (c) 2001-2002, OpenBeOS
3 //
4 //	Permission is hereby granted, free of charge, to any person obtaining a
5 //	copy of this software and associated documentation files (the "Software"),
6 //	to deal in the Software without restriction, including without limitation
7 //	the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 //	and/or sell copies of the Software, and to permit persons to whom the
9 //	Software is furnished to do so, subject to the following conditions:
10 //
11 //	The above copyright notice and this permission notice shall be included in
12 //	all copies or substantial portions of the Software.
13 //
14 //	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 //	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 //	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 //	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 //	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19 //	FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
20 //	DEALINGS IN THE SOFTWARE.
21 //
22 //	File Name:		Looper.cpp
23 //	Author(s):		Erik Jaesler (erik@cgsoftware.com)
24 //					DarkWyrm (bpmagic@columbus.rr.com)
25 //	Description:	BLooper class spawns a thread that runs a message loop.
26 //------------------------------------------------------------------------------
27 
28 /**
29 	@note	Although I'm implementing "by the book" for now, I would like to
30 			refactor sLooperList and all of the functions that operate on it
31 			into their own class in the BPrivate namespace.
32 
33 			Also considering adding the thread priority when archiving.
34 
35 
36  */
37 
38 // debugging
39 //#define DBG(x) x
40 #define DBG(x)
41 #define OUT	printf
42 
43 // Standard Includes -----------------------------------------------------------
44 #include <stdio.h>
45 
46 // System Includes -------------------------------------------------------------
47 #include <Autolock.h>
48 #include <Looper.h>
49 #include <Message.h>
50 #include <MessageFilter.h>
51 #include <MessageQueue.h>
52 #include <Messenger.h>
53 #include <PropertyInfo.h>
54 
55 // Project Includes ------------------------------------------------------------
56 
57 // Local Includes --------------------------------------------------------------
58 #include <LooperList.h>
59 #include <ObjectLocker.h>
60 #include <TokenSpace.h>
61 
62 // Local Defines ---------------------------------------------------------------
63 #define FILTER_LIST_BLOCK_SIZE	5
64 #define DATA_BLOCK_SIZE			5
65 
66 // Globals ---------------------------------------------------------------------
67 using BPrivate::gDefaultTokens;
68 using BPrivate::gLooperList;
69 using BPrivate::BObjectLocker;
70 using BPrivate::BLooperList;
71 
72 port_id _get_looper_port_(const BLooper* looper);
73 bool _use_preferred_target_(BMessage* msg) { return msg->fPreferred; }
74 int32 _get_message_target_(BMessage* msg) { return msg->fTarget; }
75 
76 uint32			BLooper::sLooperID = (uint32)B_ERROR;
77 team_id			BLooper::sTeamID = (team_id)B_ERROR;
78 
79 enum
80 {
81 	BLOOPER_PROCESS_INTERNALLY = 0,
82 	BLOOPER_HANDLER_BY_INDEX
83 };
84 
85 static property_info gLooperPropInfo[] =
86 {
87 	{
88 		"Handler",
89 			{},
90 			{B_INDEX_SPECIFIER, B_REVERSE_INDEX_SPECIFIER},
91 			NULL, BLOOPER_HANDLER_BY_INDEX,
92 			{},
93 			{},
94 			{}
95 	},
96 	{
97 		"Handlers",
98 			{B_GET_PROPERTY},
99 			{B_DIRECT_SPECIFIER},
100 			NULL, BLOOPER_PROCESS_INTERNALLY,
101 			{B_MESSENGER_TYPE},
102 			{},
103 			{}
104 	},
105 	{
106 		"Handler",
107 			{B_COUNT_PROPERTIES},
108 			{B_DIRECT_SPECIFIER},
109 			NULL, BLOOPER_PROCESS_INTERNALLY,
110 			{B_INT32_TYPE},
111 			{},
112 			{}
113 	},
114 	{}
115 };
116 
117 struct _loop_data_
118 {
119 	BLooper*	looper;
120 	thread_id	thread;
121 };
122 
123 //------------------------------------------------------------------------------
124 BLooper::BLooper(const char* name, int32 priority, int32 port_capacity)
125 	:	BHandler(name)
126 {
127 	InitData(name, priority, port_capacity);
128 }
129 //------------------------------------------------------------------------------
130 BLooper::~BLooper()
131 {
132 	if (fRunCalled && !fTerminating)
133 	{
134 		debugger("You can't call delete on a BLooper object "
135 				 "once it is running.");
136 	}
137 
138 	Lock();
139 
140 	// In case the looper thread calls Quit() fLastMessage is not deleted.
141 	if (fLastMessage)
142 	{
143 		delete fLastMessage;
144 		fLastMessage = NULL;
145 	}
146 
147 	// Close the message port and read and reply to the remaining messages.
148 	if (fMsgPort > 0)
149 	{
150 		close_port(fMsgPort);
151 	}
152 
153 	BMessage* msg;
154 	// Clear the queue so our call to IsMessageWaiting() below doesn't give
155 	// us bogus info
156 	while ((msg = fQueue->NextMessage()))
157 	{
158 		delete msg;			// msg will automagically post generic reply
159 	}
160 
161 	do
162 	{
163 		msg = ReadMessageFromPort(0);
164 		if (msg)
165 		{
166 			delete msg;		// msg will automagically post generic reply
167 		}
168 	} while (IsMessageWaiting());
169 
170 // bonefish: Killing the looper thread doesn't work very well with
171 // BApplication. In fact here it doesn't work too well either. When the
172 // looper thread encounters a _QUIT_ message it deletes the BLooper object
173 // and thus commits suicide at this point. That is, the following cleanup
174 // isn't done.
175 //	kill_thread(fTaskID);
176 	delete fQueue;
177 	delete_port(fMsgPort);
178 
179 	// Clean up our filters
180 	SetCommonFilterList(NULL);
181 
182 	BObjectLocker<BLooperList> ListLock(gLooperList);
183 	RemoveHandler(this);
184 
185 	// Remove all the "child" handlers
186 	BHandler* child;
187 	while (CountHandlers())
188 	{
189 		child = HandlerAt(0);
190 		if (child)
191 		{
192 			RemoveHandler(child);
193 		}
194 	}
195 
196 	UnlockFully();
197 	RemoveLooper(this);
198 	delete_sem(fLockSem);
199 }
200 //------------------------------------------------------------------------------
201 BLooper::BLooper(BMessage* data)
202 	:	BHandler(data)
203 {
204 	int32 portCap;
205 	if (data->FindInt32("_port_cap", &portCap) != B_OK)
206 	{
207 		portCap = B_LOOPER_PORT_DEFAULT_CAPACITY;
208 	}
209 
210 	InitData(Name(), B_NORMAL_PRIORITY, portCap);
211 }
212 //------------------------------------------------------------------------------
213 BArchivable* BLooper::Instantiate(BMessage* data)
214 {
215 	if (validate_instantiation(data, "BLooper"))
216 	{
217 		return new BLooper(data);
218 	}
219 
220 	return NULL;
221 }
222 //------------------------------------------------------------------------------
223 status_t BLooper::Archive(BMessage* data, bool deep) const
224 {
225 	status_t err = BHandler::Archive(data, deep);
226 	if (!err)
227 	{
228 		port_info info;
229 		err = get_port_info(fMsgPort, &info);
230 		if (!err)
231 		{
232 			err = data->AddInt32("_port_cap", info.capacity);
233 		}
234 	}
235 
236 	return err;
237 }
238 //------------------------------------------------------------------------------
239 status_t BLooper::PostMessage(uint32 command)
240 {
241 	BMessage Message(command);
242 	return _PostMessage(&Message, this, NULL);
243 }
244 //------------------------------------------------------------------------------
245 status_t BLooper::PostMessage(BMessage* message)
246 {
247 	return _PostMessage(message, this, NULL);
248 }
249 //------------------------------------------------------------------------------
250 status_t BLooper::PostMessage(uint32 command, BHandler* handler,
251 							  BHandler* reply_to)
252 {
253 	BMessage Message(command);
254 	return _PostMessage(&Message, handler, reply_to);
255 }
256 //------------------------------------------------------------------------------
257 status_t BLooper::PostMessage(BMessage* message, BHandler* handler,
258 							  BHandler* reply_to)
259 {
260 	return _PostMessage(message, handler, reply_to);
261 }
262 //------------------------------------------------------------------------------
263 void BLooper::DispatchMessage(BMessage* message, BHandler* handler)
264 {
265 DBG(OUT("BLooper::DispatchMessage(%.4s)\n", (char*)&message->what));
266 /**
267 	@note	Initially, DispatchMessage() was locking the looper, calling the
268 			filtering API, determining whether to use fPreferred or not, and
269 			deleting the message.  A look at the BeBook, however, reveals that
270 			all this function does is handle its own B_QUIT_REQUESTED messages
271 			and pass everything else to handler->MessageReceived().  Clearly the
272 			rest must be happening in task_looper().  This makes a lot of sense
273 			because otherwise every derived class would have to figure out when
274 			to use fPreferred, handle the locking and filtering and delete the
275 			message.  Even if the BeBook didn't say as much, it would make total
276 			sense to hoist that functionality out of here and into task_looper().
277  */
278 	switch (message->what)
279 	{
280 		case _QUIT_:
281 		{
282 			// Can't call Quit() to do this, because of the slight chance
283 			// another thread with have us locked between now and then.
284 			fTerminating = true;
285 // bonefish: Now it works straight forward: The thread running here is
286 // the looper thread, so after returning from DispatchMessage() it will
287 // quit the dispatching loop, and delete the object in _task0_(), directly
288 // before dying. Even better, this solution is BApplication friendly as
289 // Quit() doesn't delete the application object.
290 // TODO: Remove this.
291 //			delete this;
292 			break;
293 		}
294 
295 		case B_QUIT_REQUESTED:
296 		{
297 			if (handler == this)
298 			{
299 				do_quit_requested(message);
300 				break;
301 			}
302 			else
303 			{
304 				// fall through
305 			}
306 		}
307 
308 		default:
309 		{
310 			handler->MessageReceived(message);
311 			break;
312 		}
313 	}
314 DBG(OUT("BLooper::DispatchMessage() done\n"));
315 }
316 //------------------------------------------------------------------------------
317 void BLooper::MessageReceived(BMessage* msg)
318 {
319 	// TODO: verify
320 	// The BeBook says this "simply calls the inherited function. ...the BLooper
321 	// implementation does nothing of importance."  Which is not the same as
322 	// saying it does nothing.  Investigate.
323 	BHandler::MessageReceived(msg);
324 }
325 //------------------------------------------------------------------------------
326 BMessage* BLooper::CurrentMessage() const
327 {
328 	return fLastMessage;
329 }
330 //------------------------------------------------------------------------------
331 BMessage* BLooper::DetachCurrentMessage()
332 {
333 	Lock();
334 	BMessage* msg = fLastMessage;
335 	fLastMessage = NULL;
336 	fQueue->RemoveMessage(msg);
337 	Unlock();
338 	return msg;
339 }
340 //------------------------------------------------------------------------------
341 BMessageQueue* BLooper::MessageQueue() const
342 {
343 	return fQueue;
344 }
345 //------------------------------------------------------------------------------
346 bool BLooper::IsMessageWaiting() const
347 {
348 	if (!IsLocked())
349 	{
350 		debugger("The Looper must be locked before calling IsMsgWaiting");
351 		return false;
352 	}
353 
354 	if (!fQueue->IsEmpty())
355 	{
356 		return true;
357 	}
358 
359 /**
360 	@note:	What we're doing here differs slightly from the R5 implementation.
361 			It appears that they probably return count != 0, which gives an
362 			incorrect true result when port_buffer_size_etc() would block --
363 			which indicates that the port's buffer is empty, so we should return
364 			false.  Since we don't actually care about what the error is, we
365 			just return count > 0.  This has some interesting consequences in
366 			that we will correctly return 'false' if the port is empty
367 			(B_WOULD_BLOCK), whereas R5 will return true.  We call that a bug
368 			where I come from. ;)
369  */
370 	int32 count;
371 	do
372 	{
373 		count = port_buffer_size_etc(fMsgPort, B_TIMEOUT, 0);
374 	} while (count == B_INTERRUPTED);
375 
376 	return count > 0;
377 }
378 //------------------------------------------------------------------------------
379 void BLooper::AddHandler(BHandler* handler)
380 {
381 	if (!handler)
382 	{
383 		return;
384 	}
385 
386 	if (!IsLocked())
387 	{
388 		debugger("Looper must be locked before calling AddHandler.");
389 	}
390 
391 	if (handler->Looper() == NULL)
392 	{
393 		fHandlers.AddItem(handler);
394 		handler->SetLooper(this);
395 		if (handler != this)	// avoid a cycle
396 			handler->SetNextHandler(this);
397 		BList* Filters = handler->FilterList();
398 		if (Filters)
399 		{
400 			for (int32 i = 0; i < Filters->CountItems(); ++i)
401 			{
402 				((BMessageFilter*)Filters->ItemAt(i))->SetLooper(this);
403 			}
404 		}
405 	}
406 }
407 //------------------------------------------------------------------------------
408 bool BLooper::RemoveHandler(BHandler* handler)
409 {
410 	// R5 implementation didn't bother to check its params, thus NULL handlers
411 	// will seg fault.  Bad form, you know.
412 	if (!handler)
413 	{
414 		return false;
415 	}
416 
417 	// Correction; testing shows the looper *does* need to be locked for this;
418 	// it just doesn't use AssertLocked() for that.
419 	if (!IsLocked())
420 	{
421 		debugger("Looper must be locked before calling RemoveHandler.");
422 	}
423 
424 	if (handler->Looper() == this && fHandlers.RemoveItem(handler))
425 	{
426 		if (handler == fPreferred)
427 		{
428 			fPreferred = NULL;
429 		}
430 
431 		handler->SetNextHandler(NULL);
432 		handler->SetLooper(NULL);
433 		return true;
434 	}
435 
436 	return false;
437 }
438 //------------------------------------------------------------------------------
439 int32 BLooper::CountHandlers() const
440 {
441 	AssertLocked();
442 
443 	return fHandlers.CountItems();
444 }
445 //------------------------------------------------------------------------------
446 BHandler* BLooper::HandlerAt(int32 index) const
447 {
448 	if (!IsLocked())
449 	{
450 		debugger("Looper must be locked before calling HandlerAt.");
451 	}
452 
453 	return (BHandler*)fHandlers.ItemAt(index);
454 }
455 //------------------------------------------------------------------------------
456 int32 BLooper::IndexOf(BHandler* handler) const
457 {
458 	if (!IsLocked())
459 	{
460 		debugger("Looper must be locked before calling IndexOf.");
461 	}
462 
463 	return fHandlers.IndexOf(handler);
464 }
465 //------------------------------------------------------------------------------
466 BHandler* BLooper::PreferredHandler() const
467 {
468 	return fPreferred;
469 }
470 //------------------------------------------------------------------------------
471 void BLooper::SetPreferredHandler(BHandler* handler)
472 {
473 	if (handler && handler->Looper() == this && IndexOf(handler) >= 0)
474 	{
475 		fPreferred = handler;
476 	}
477 	else
478 	{
479 		fPreferred = NULL;
480 	}
481 }
482 //------------------------------------------------------------------------------
483 thread_id BLooper::Run()
484 {
485 	AssertLocked();
486 
487 	if (fRunCalled)
488 	{
489 		// Not allowed to call Run() more than once
490 		debugger("can't call BLooper::Run twice!");
491 	}
492 
493 	fTaskID = spawn_thread(_task0_, Name(), fInitPriority, this);
494 
495 	if (fTaskID == B_NO_MORE_THREADS || fTaskID == B_NO_MEMORY)
496 	{
497 		return fTaskID;
498 	}
499 
500 	if (fMsgPort == B_NO_MORE_PORTS || fMsgPort == B_BAD_VALUE)
501 	{
502 		return fMsgPort;
503 	}
504 
505 	fRunCalled = true;
506 	Unlock();
507 	status_t err = resume_thread(fTaskID);
508 	if (err)
509 	{
510 		return err;
511 	}
512 
513 	return fTaskID;
514 }
515 //------------------------------------------------------------------------------
516 void BLooper::Quit()
517 {
518 DBG(OUT("BLooper::Quit()\n"));
519 	if (!IsLocked())
520 	{
521 		const char* name = Name();
522 		if (!name)
523 		{
524 			name = "no-name";
525 		}
526 		printf("ERROR - you must Lock a looper before calling Quit(), "
527 			   "team=%ld, looper=%s\n", Team(), name);
528 	}
529 
530 	// Try to lock
531 	if (!Lock())
532 	{
533 		// We're toast already
534 		return;
535 	}
536 
537 DBG(OUT("  is locked\n"));
538 
539 	if (!fRunCalled)
540 	{
541 DBG(OUT("  Run() has not been called yet\n"));
542 		fTerminating = true;
543 		delete this;
544 	}
545 	else if (find_thread(NULL) == fTaskID)
546 	{
547 DBG(OUT("  We are the looper thread\n"));
548 		fTerminating = true;
549 		delete this;
550 		exit_thread(0);
551 	}
552 	else
553 	{
554 DBG(OUT("  Run() has already been called and we are not the looper thread\n"));
555 		// As with sem in _Lock(), we need to cache this here in case the looper
556 		// disappears before we get to the wait_for_thread() below
557 		thread_id tid = Thread();
558 
559 		// bonefish: We need to unlock here. Otherwise the looper thread can't
560 		// dispatch the _QUIT_ message we're going to post.
561 //		do {
562 //			Unlock();
563 //		} while (IsLocked());
564 		UnlockFully();
565 
566 		// As per the BeBook, if we've been called by a thread other than
567 		// our own, the rest of the message queue has to get processed.  So
568 		// we put this in the queue, and when it shows up, we'll call Quit()
569 		// from our own thread.
570 		// A little testing with BMessageFilter shows _QUIT_ is being used here.
571 		// I got suspicious when my test QuitRequested() wasn't getting called
572 		// when Quit() was invoked from another thread.  Makes a nice proof that
573 		// this is how it's handled, too.
574 		status_t err;
575 DBG(OUT("  PostMessage(_QUIT_)...\n"));
576 //		err = PostMessage(_QUIT_);
577 
578 BMessage message(_QUIT_);
579 message.AddInt32("testfield", 42);
580 err = PostMessage(&message);
581 DBG(OUT("  ... done: %lx\n", err));
582 
583 		// There's a possibility that PostMessage() will return B_WOULD_BLOCK
584 		// because the port is full, so we'll wait a bit and re-post until
585 		// we won't block.
586 		while (err == B_WOULD_BLOCK)
587 		{
588 			// TODO: test this value; it may be too short
589 			snooze(10000);
590 			err = PostMessage(_QUIT_);
591 		}
592 
593 		// Also as per the BeBook, we have to wait until the looper is done
594 		// processing any remaining messages.
595 		int32 temp;
596 		do
597 		{
598 DBG(OUT("  wait_for_thread(%ld)...\n", tid));
599 			err = wait_for_thread(tid, &temp);
600 		} while (err == B_INTERRUPTED);
601 	}
602 DBG(OUT("BLooper::Quit() done\n"));
603 }
604 //------------------------------------------------------------------------------
605 bool BLooper::QuitRequested()
606 {
607 	return true;
608 }
609 //------------------------------------------------------------------------------
610 bool BLooper::Lock()
611 {
612 	// Defer to global _Lock(); see notes there
613 	return _Lock(this, -1, B_INFINITE_TIMEOUT) == B_OK;
614 }
615 //------------------------------------------------------------------------------
616 void BLooper::Unlock()
617 {
618 DBG(OUT("BLooper::Unlock()\n"));
619 	//	Make sure we're locked to begin with
620 	AssertLocked();
621 
622 	//	Decrement fOwnerCount
623 	--fOwnerCount;
624 DBG(OUT("  fOwnerCount now: %ld\n", fOwnerCount));
625 	//	Check to see if the owner still wants a lock
626 	if (fOwnerCount == 0)
627 	{
628 		//	Set fOwner to invalid thread_id (< 0)
629 		fOwner = -1;
630 
631 		//	Decrement requested lock count (using fAtomicCount for this)
632 /*		int32 atomicCount =*/ atomic_add(&fAtomicCount, -1);
633 DBG(OUT("  fAtomicCount now: %ld\n", fAtomicCount));
634 
635 		//	Check if anyone is waiting for a lock
636 // bonefish: Currently _Lock() always acquires the semaphore.
637 //		if (atomicCount > 0)
638 		{
639 			//	release the lock
640 			release_sem(fLockSem);
641 		}
642 	}
643 DBG(OUT("BLooper::Unlock() done\n"));
644 }
645 //------------------------------------------------------------------------------
646 bool BLooper::IsLocked() const
647 {
648 	// We have to lock the list for the call to IsLooperValid().  Has the side
649 	// effect of not letting the looper get deleted while we're here.
650 	BObjectLocker<BLooperList> ListLock(gLooperList);
651 
652 	if (!ListLock.IsLocked())
653 	{
654 		// If we can't lock the list, our semaphore is probably toast
655 		return false;
656 	}
657 
658 	if (!IsLooperValid(this))
659 	{
660 		// The looper is gone, so of course it's not locked
661 		return false;
662 	}
663 
664 	// Got this from Jeremy's BLocker implementation
665 	return find_thread(NULL) == fOwner;
666 }
667 //------------------------------------------------------------------------------
668 status_t BLooper::LockWithTimeout(bigtime_t timeout)
669 {
670 	return _Lock(this, -1, timeout);
671 }
672 //------------------------------------------------------------------------------
673 thread_id BLooper::Thread() const
674 {
675 	return fTaskID;
676 }
677 //------------------------------------------------------------------------------
678 team_id BLooper::Team() const
679 {
680 	return sTeamID;
681 }
682 //------------------------------------------------------------------------------
683 BLooper* BLooper::LooperForThread(thread_id tid)
684 {
685 	BObjectLocker<BLooperList> ListLock(gLooperList);
686 	if (ListLock.IsLocked())
687 	{
688 		return gLooperList.LooperForThread(tid);
689 	}
690 
691 	return NULL;
692 }
693 //------------------------------------------------------------------------------
694 thread_id BLooper::LockingThread() const
695 {
696 	return fOwner;
697 }
698 //------------------------------------------------------------------------------
699 int32 BLooper::CountLocks() const
700 {
701 	return fOwnerCount;
702 }
703 //------------------------------------------------------------------------------
704 int32 BLooper::CountLockRequests() const
705 {
706 	return fAtomicCount;
707 }
708 //------------------------------------------------------------------------------
709 sem_id BLooper::Sem() const
710 {
711 	return fLockSem;
712 }
713 //------------------------------------------------------------------------------
714 BHandler* BLooper::ResolveSpecifier(BMessage* msg, int32 index,
715 									BMessage* specifier, int32 form,
716 									const char* property)
717 {
718 /**
719 	@note	When I was first dumping the results of GetSupportedSuites() from
720 			various classes, the use of the extra_data field was quite
721 			mysterious to me.  Then I dumped BApplication and compared the
722 			result against the BeBook's docs for scripting BApplication.  A
723 			bunch of it isn't documented, but what is tipped me to the idea
724 			that the extra_data is being used as a quick and dirty way to tell
725 			what scripting "command" has been sent, e.g., for easy use in a
726 			switch statement.  Would certainly be a lot faster than a bunch of
727 			string comparisons -- which wouldn't tell the whole story anyway,
728 			because of the same name being used for multiple properties.
729  */
730 	BPropertyInfo PropertyInfo(gLooperPropInfo);
731 	uint32 data;
732 	status_t err = B_OK;
733 	const char* errMsg = "";
734 	if (PropertyInfo.FindMatch(msg, index, specifier, form, property, &data) >= 0)
735 	{
736 		switch (data)
737 		{
738 			case BLOOPER_PROCESS_INTERNALLY:
739 				return this;
740 
741 			case BLOOPER_HANDLER_BY_INDEX:
742 			{
743 				int32 index = specifier->FindInt32("index");
744 				if (form == B_REVERSE_INDEX_SPECIFIER)
745 				{
746 					index = CountHandlers() - index;
747 				}
748 				BHandler* target = HandlerAt(index);
749 				if (target)
750 				{
751 					// Specifier has been fully handled
752 					msg->PopSpecifier();
753 					return target;
754 				}
755 				else
756 				{
757 					err = B_BAD_INDEX;
758 					errMsg = "handler index out of range";
759 				}
760 				break;
761 			}
762 
763 			default:
764 				err = B_BAD_SCRIPT_SYNTAX;
765 				errMsg = "Didn't understand the specifier(s)";
766 				break;
767 		}
768 	}
769 	else
770 	{
771 		return BHandler::ResolveSpecifier(msg, index, specifier, form,
772 										  property);
773 	}
774 
775 	BMessage Reply(B_MESSAGE_NOT_UNDERSTOOD);
776 	Reply.AddInt32("error", err);
777 	Reply.AddString("message", errMsg);
778 	msg->SendReply(&Reply);
779 
780 	return NULL;
781 }
782 //------------------------------------------------------------------------------
783 status_t BLooper::GetSupportedSuites(BMessage* data)
784 {
785 	status_t err = B_OK;
786 
787 	if (!data)
788 	{
789 		err = B_BAD_VALUE;
790 	}
791 
792 	if (!err)
793 	{
794 		err = data->AddString("Suites", "suite/vnd.Be-handler");
795 		if (!err)
796 		{
797 			BPropertyInfo PropertyInfo(gLooperPropInfo);
798 			err = data->AddFlat("message", &PropertyInfo);
799 			if (!err)
800 			{
801 				err = BHandler::GetSupportedSuites(data);
802 			}
803 		}
804 	}
805 
806 	return err;
807 }
808 //------------------------------------------------------------------------------
809 void BLooper::AddCommonFilter(BMessageFilter* filter)
810 {
811 	if (!filter)
812 	{
813 		return;
814 	}
815 
816 	if (!IsLocked())
817 	{
818 		debugger("Owning Looper must be locked before calling AddCommonFilter");
819 		return;
820 	}
821 
822 	if (filter->Looper())
823 	{
824 		debugger("A MessageFilter can only be used once.");
825 		return;
826 	}
827 
828 	if (!fCommonFilters)
829 	{
830 		fCommonFilters = new BList(FILTER_LIST_BLOCK_SIZE);
831 	}
832 	filter->SetLooper(this);
833 	fCommonFilters->AddItem(filter);
834 }
835 //------------------------------------------------------------------------------
836 bool BLooper::RemoveCommonFilter(BMessageFilter* filter)
837 {
838 	if (!IsLocked())
839 	{
840 		debugger("Owning Looper must be locked before calling "
841 				 "RemoveCommonFilter");
842 		return false;
843 	}
844 
845 	if (!fCommonFilters)
846 	{
847 		return false;
848 	}
849 
850 	bool result = fCommonFilters->RemoveItem(filter);
851 	if (result)
852 	{
853 		filter->SetLooper(NULL);
854 	}
855 
856 	return result;
857 }
858 //------------------------------------------------------------------------------
859 void BLooper::SetCommonFilterList(BList* filters)
860 {
861 	// We have a somewhat serious problem here.  It is entirely possible in R5
862 	// to assign a given list of filters to *two* BLoopers simultaneously.  This
863 	// becomes problematic when the loopers are destroyed: the last looper
864 	// destroyed will have a problem when it tries to delete a filter list that
865 	// has already been deleted.  In R5, this results in a general protection
866 	// fault.  We fix this by checking the filter list for ownership issues.
867 
868 	if (!IsLocked())
869 	{
870 		debugger("Owning Looper must be locked before calling "
871 				 "SetCommonFilterList");
872 		return;
873 	}
874 
875 	BMessageFilter* filter;
876 	if (filters)
877 	{
878 		// Check for ownership issues
879 		for (int32 i = 0; i < filters->CountItems(); ++i)
880 		{
881 			filter = (BMessageFilter*)filters->ItemAt(i);
882 			if (filter->Looper())
883 			{
884 				debugger("A MessageFilter can only be used once.");
885 				return;
886 			}
887 		}
888 	}
889 
890 	if (fCommonFilters)
891 	{
892 		for (int32 i = 0; i < fCommonFilters->CountItems(); ++i)
893 		{
894 			delete (BMessageFilter*)fCommonFilters->ItemAt(i);
895 		}
896 
897 		delete fCommonFilters;
898 		fCommonFilters = NULL;
899 	}
900 
901 	// Per the BeBook, we take ownership of the list
902 	fCommonFilters = filters;
903 	if (fCommonFilters)
904 	{
905 		for (int32 i = 0; i < fCommonFilters->CountItems(); ++i)
906 		{
907 			filter = (BMessageFilter*)fCommonFilters->ItemAt(i);
908 			filter->SetLooper(this);
909 		}
910 	}
911 }
912 //------------------------------------------------------------------------------
913 BList* BLooper::CommonFilterList() const
914 {
915 	return fCommonFilters;
916 }
917 //------------------------------------------------------------------------------
918 status_t BLooper::Perform(perform_code d, void* arg)
919 {
920 	// This is sort of what we're doing for this function everywhere
921 	return BHandler::Perform(d, arg);
922 }
923 //------------------------------------------------------------------------------
924 BMessage* BLooper::MessageFromPort(bigtime_t timeout)
925 {
926 	return ReadMessageFromPort(timeout);
927 }
928 //------------------------------------------------------------------------------
929 void BLooper::_ReservedLooper1()
930 {
931 }
932 //------------------------------------------------------------------------------
933 void BLooper::_ReservedLooper2()
934 {
935 }
936 //------------------------------------------------------------------------------
937 void BLooper::_ReservedLooper3()
938 {
939 }
940 //------------------------------------------------------------------------------
941 void BLooper::_ReservedLooper4()
942 {
943 }
944 //------------------------------------------------------------------------------
945 void BLooper::_ReservedLooper5()
946 {
947 }
948 //------------------------------------------------------------------------------
949 void BLooper::_ReservedLooper6()
950 {
951 }
952 //------------------------------------------------------------------------------
953 BLooper::BLooper(const BLooper&)
954 {
955 	// Copy construction not allowed
956 }
957 //------------------------------------------------------------------------------
958 BLooper& BLooper::operator=(const BLooper& )
959 {
960 	// Looper copying not allowed
961 	return *this;
962 }
963 //------------------------------------------------------------------------------
964 BLooper::BLooper(int32 priority, port_id port, const char* name)
965 {
966 	// This must be a legacy constructor
967 	fMsgPort = port;
968 	InitData(name, priority, B_LOOPER_PORT_DEFAULT_CAPACITY);
969 }
970 //------------------------------------------------------------------------------
971 status_t BLooper::_PostMessage(BMessage* msg, BHandler* handler,
972 							   BHandler* reply_to)
973 {
974 	BObjectLocker<BLooperList> ListLock(gLooperList);
975 	if (!ListLock.IsLocked())
976 	{
977 		return B_BAD_VALUE;
978 	}
979 
980 	if (!IsLooperValid(this))
981 	{
982 		return B_BAD_VALUE;
983 	}
984 
985 	// Does handler belong to this looper?
986 	if (handler && handler->Looper() != this)
987 	{
988 		return B_MISMATCHED_VALUES;
989 	}
990 
991 	status_t err = B_OK;
992 
993 	BMessenger Messenger(handler, this, &err);
994 
995 	if (!err)
996 	{
997 		err = Messenger.SendMessage(msg, reply_to, 0);
998 	}
999 
1000 	return err;
1001 }
1002 //------------------------------------------------------------------------------
1003 status_t BLooper::_Lock(BLooper* loop, port_id port, bigtime_t timeout)
1004 {
1005 DBG(OUT("BLooper::_Lock(%p, %lx)\n", loop, port));
1006 /**
1007 	@note	The assumption I'm under here is that since we can get the port of
1008 			the BLooper directly from the BLooper itself, the port parameter is
1009 			for identifying BLoopers by port_id when a pointer to the BLooper in
1010 			question is not available.  So this function has two modes:
1011 				o When loop != NULL, use it directly
1012 				o When loop == NULL and port is valid, use the port_id to get
1013 				  the looper
1014 			I scoured the docs to find out what constitutes a valid port_id to
1015 			no avail.  Since create_port uses the standard error values in its
1016 			returned port_id, I'll assume that anything less than zero is a safe
1017 			bet as an *invalid* port_id.  I'm guessing that, like thread and
1018 			semaphore ids, anything >= zero is valid.  So, the short version of
1019 			this reads: if you don't want to find by port_id, make port = -1.
1020 
1021 			Another assumption I'm making is that Lock() and LockWithTimeout()
1022 			are covers for this function.  If it turns out that we don't really
1023 			need this function, I may refactor this code into LockWithTimeout()
1024 			and have Lock() call it instead.  This function could then be
1025 			removed.
1026  */
1027 
1028 	//	Check params (loop, port)
1029 	if (!loop && port < 0)
1030 	{
1031 DBG(OUT("BLooper::_Lock() done 1\n"));
1032 		return B_BAD_VALUE;
1033 	}
1034 
1035 	// forward declared so I can use BAutolock on sLooperListLock
1036 	thread_id curThread;
1037 	sem_id sem;
1038 
1039 /**
1040 	@note	We lock the looper list at the start of the lock operation to
1041 			prevent the looper getting removed from the list while we're
1042 			doing list operations.  Also ensures that the looper doesn't
1043 			get deleted here (since ~BLooper() has to lock the list as
1044 			well to remove itself).
1045  */
1046 	{
1047 		BObjectLocker<BLooperList> ListLock(gLooperList);
1048 		if (!ListLock.IsLocked())
1049 		{
1050 			// If we can't lock, the semaphore is probably
1051 			// gone, which leaves us in no-man's land
1052 DBG(OUT("BLooper::_Lock() done 2\n"));
1053 			return B_BAD_VALUE;
1054 		}
1055 
1056 		//	Look up looper by port_id, if necessary
1057 		if (!loop)
1058 		{
1059 			loop = LooperForPort(port);
1060 			if (!loop)
1061 			{
1062 DBG(OUT("BLooper::_Lock() done 3\n"));
1063 				return B_BAD_VALUE;
1064 			}
1065 		}
1066 		else
1067 		{
1068 			//	Check looper validity
1069 			if (!IsLooperValid(loop))
1070 			{
1071 DBG(OUT("BLooper::_Lock() done 4\n"));
1072 				return B_BAD_VALUE;
1073 			}
1074 		}
1075 
1076 		//	Is the looper trying to lock itself?
1077 		//	Check for nested lock attempt
1078 		curThread = find_thread(NULL);
1079 		if (curThread == loop->fOwner)
1080 		{
1081 			//	Bump fOwnerCount
1082 			++loop->fOwnerCount;
1083 DBG(OUT("BLooper::_Lock() done 5: fOwnerCount: %ld\n", loop->fOwnerCount));
1084 			return B_OK;
1085 		}
1086 
1087 		//	Something external to the looper is attempting to lock
1088 		//	Cache the semaphore
1089 		sem = loop->fLockSem;
1090 
1091 		//	Validate the semaphore
1092 		if (sem < 0)
1093 		{
1094 DBG(OUT("BLooper::_Lock() done 6\n"));
1095 			return B_BAD_VALUE;
1096 		}
1097 
1098 		//	Bump the requested lock count (using fAtomicCount for this)
1099 		atomic_add(&loop->fAtomicCount, 1);
1100 
1101 		// sLooperListLock automatically released here
1102 	}
1103 
1104 /**
1105 	@note	We have to operate with the looper list unlocked during semaphore
1106 			acquisition so that the rest of the application doesn't have to
1107 			wait for this lock to happen.  This is why we cached fLockSem
1108 			earlier -- with the list unlocked, the looper might get deleted
1109 			right out from under us.  This is also why we use a raw semaphore
1110 			instead of the easier-to-deal-with BLocker; you can't cache a
1111 			BLocker.
1112  */
1113 	//	acquire the lock
1114 	status_t err;
1115 	do
1116 	{
1117 		err = acquire_sem_etc(sem, 1, B_RELATIVE_TIMEOUT, timeout);
1118 	} while (err == B_INTERRUPTED);
1119 
1120 	if (!err)
1121 	{
1122 		//		Assign current thread to fOwner
1123 		loop->fOwner = curThread;
1124 		//		Reset fOwnerCount to 1
1125 		loop->fOwnerCount = 1;
1126 	}
1127 
1128 DBG(OUT("BLooper::_Lock() done: %lx\n", err));
1129 	return err;
1130 }
1131 //------------------------------------------------------------------------------
1132 status_t BLooper::_LockComplete(BLooper* loop, int32 old, thread_id this_tid,
1133 								sem_id sem, bigtime_t timeout)
1134 {
1135 	// What is this for?  Hope I'm not missing something conceptually here ...
1136 	return B_ERROR;
1137 }
1138 //------------------------------------------------------------------------------
1139 void BLooper::InitData()
1140 {
1141 	fOwner = B_ERROR;
1142 	fRunCalled = false;
1143 	fQueue = new BMessageQueue();
1144 	fCommonFilters = NULL;
1145 	fLastMessage = NULL;
1146 	fPreferred = NULL;
1147 	fTaskID = B_ERROR;
1148 	fTerminating = false;
1149 	fMsgPort = -1;
1150 
1151 	if (sTeamID == -1)
1152 	{
1153 		thread_info info;
1154 		get_thread_info(find_thread(NULL), &info);
1155 		sTeamID = info.team;
1156 	}
1157 
1158 }
1159 //------------------------------------------------------------------------------
1160 void BLooper::InitData(const char* name, int32 priority, int32 port_capacity)
1161 {
1162 	InitData();
1163 
1164 	fLockSem = create_sem(1, name);
1165 
1166 	if (port_capacity <= 0)
1167 	{
1168 		port_capacity = B_LOOPER_PORT_DEFAULT_CAPACITY;
1169 	}
1170 
1171 	fMsgPort = create_port(port_capacity, name ? name : "LooperPort");
1172 
1173 	fInitPriority = priority;
1174 
1175 	BObjectLocker<BLooperList> ListLock(gLooperList);
1176 	AddLooper(this);
1177 	AddHandler(this);
1178 }
1179 //------------------------------------------------------------------------------
1180 void BLooper::AddMessage(BMessage* msg)
1181 {
1182 	// NOTE: Why is this here?
1183 }
1184 //------------------------------------------------------------------------------
1185 void BLooper::_AddMessagePriv(BMessage* msg)
1186 {
1187 	// NOTE: No, really; why the hell is this here??
1188 }
1189 //------------------------------------------------------------------------------
1190 status_t BLooper::_task0_(void* arg)
1191 {
1192 DBG(OUT("LOOPER: _task0_()\n"));
1193 	BLooper* obj = (BLooper*)arg;
1194 
1195 DBG(OUT("LOOPER: locking looper...\n"));
1196 	if (obj->Lock())
1197 	{
1198 DBG(OUT("LOOPER: looper locked\n"));
1199 		obj->task_looper();
1200 		delete obj;
1201 	}
1202 
1203 DBG(OUT("LOOPER: _task0_() done: thread %ld\n", find_thread(NULL)));
1204 	return B_OK;
1205 }
1206 //------------------------------------------------------------------------------
1207 void* BLooper::ReadRawFromPort(int32* msgcode, bigtime_t tout)
1208 {
1209 DBG(OUT("BLooper::ReadRawFromPort()\n"));
1210 	int8* msgbuffer = NULL;
1211 	ssize_t buffersize;
1212 	ssize_t bytesread;
1213 
1214 	if (tout == B_INFINITE_TIMEOUT)
1215 	{
1216 		buffersize = port_buffer_size(fMsgPort);
1217 DBG(OUT("BLooper::ReadRawFromPort(): buffersize: %ld\n", buffersize));
1218 	}
1219 	else
1220 	{
1221 		buffersize = port_buffer_size_etc(fMsgPort, 0, tout);
1222 		if (buffersize == B_TIMED_OUT || buffersize == B_BAD_PORT_ID ||
1223 			buffersize == B_WOULD_BLOCK)
1224 		{
1225 DBG(OUT("BLooper::ReadRawFromPort() done 1\n"));
1226 			return NULL;
1227 		}
1228 	}
1229 
1230 	if (buffersize > 0)
1231 		msgbuffer = new int8[buffersize];
1232 
1233 	if (tout == B_INFINITE_TIMEOUT)
1234 	{
1235 DBG(OUT("read_port()...\n"));
1236 		bytesread = read_port(fMsgPort, msgcode, msgbuffer, buffersize);
1237 DBG(OUT("read_port() done: %ld\n", bytesread));
1238 DBG(OUT("BLooper::ReadRawFromPort() read: %.4s\n", (char*)msgcode));
1239 	}
1240 	else
1241 	{
1242 		bytesread = read_port_etc(fMsgPort, msgcode, msgbuffer, buffersize,
1243 								  B_TIMEOUT, tout);
1244 	}
1245 
1246 DBG(OUT("BLooper::ReadRawFromPort() done: %p\n", msgbuffer));
1247 	return msgbuffer;
1248 }
1249 //------------------------------------------------------------------------------
1250 BMessage* BLooper::ReadMessageFromPort(bigtime_t tout)
1251 {
1252 DBG(OUT("BLooper::ReadMessageFromPort()\n"));
1253 	int32 msgcode;
1254 	BMessage* bmsg;
1255 
1256 	void* msgbuffer = ReadRawFromPort(&msgcode, tout);
1257 
1258 	bmsg = ConvertToMessage(msgbuffer, msgcode);
1259 
1260 	if (msgbuffer)
1261 	{
1262 		delete[] (int8*)msgbuffer;
1263 	}
1264 
1265 DBG(OUT("BLooper::ReadMessageFromPort() done: %p\n", bmsg));
1266 	return bmsg;
1267 }
1268 //------------------------------------------------------------------------------
1269 BMessage* BLooper::ConvertToMessage(void* raw, int32 code)
1270 {
1271 DBG(OUT("BLooper::ConvertToMessage()\n"));
1272 	BMessage* bmsg = new BMessage(code);
1273 
1274 	if (raw != NULL)
1275 	{
1276 		if (bmsg->Unflatten((const char*)raw) != B_OK)
1277 		{
1278 DBG(OUT("BLooper::ConvertToMessage(): unflattening message failed\n"));
1279 			delete bmsg;
1280 			bmsg = NULL;
1281 		}
1282 	}
1283 
1284 DBG(OUT("BLooper::ConvertToMessage(): %p\n", bmsg));
1285 	return bmsg;
1286 }
1287 //------------------------------------------------------------------------------
1288 void BLooper::task_looper()
1289 {
1290 DBG(OUT("BLooper::task_looper()\n"));
1291 	//	Check that looper is locked (should be)
1292 	AssertLocked();
1293 	//	Unlock the looper
1294 	Unlock();
1295 
1296 	//	loop: As long as we are not terminating.
1297 	while (!fTerminating)
1298 	{
1299 DBG(OUT("LOOPER: outer loop\n"));
1300 		// TODO: timeout determination algo
1301 		//	Read from message port (how do we determine what the timeout is?)
1302 DBG(OUT("LOOPER: MessageFromPort()...\n"));
1303 		BMessage* msg = MessageFromPort();
1304 DBG(OUT("LOOPER: ...done\n"));
1305 
1306 		//	Did we get a message?
1307 		if (msg)
1308 		{
1309 DBG(OUT("LOOPER: got message\n"));
1310 			//	Add to queue
1311 			fQueue->AddMessage(msg);
1312 		}
1313 else
1314 DBG(OUT("LOOPER: got no message\n"));
1315 
1316 		//	Get message count from port
1317 		int32 msgCount = port_count(fMsgPort);
1318 		for (int32 i = 0; i < msgCount; ++i)
1319 		{
1320 			//	Read 'count' messages from port (so we will not block)
1321 			//	We use zero as our timeout since we know there is stuff there
1322 			msg = MessageFromPort(0);
1323 			//	Add messages to queue
1324 			if (msg)
1325 			{
1326 				fQueue->AddMessage(msg);
1327 			}
1328 		}
1329 
1330 		//	loop: As long as there are messages in the queue and the port is
1331 		//		  empty... and we are not terminating, of course.
1332 		bool dispatchNextMessage = true;
1333 		while (!fTerminating && dispatchNextMessage)
1334 		{
1335 DBG(OUT("LOOPER: inner loop\n"));
1336 			//	Get next message from queue (assign to fLastMessage)
1337 			fLastMessage = fQueue->NextMessage();
1338 
1339 			//	Lock the looper
1340 			Lock();
1341 			if (!fLastMessage)
1342 			{
1343 				// No more messages: Unlock the looper and terminate the
1344 				// dispatch loop.
1345 				dispatchNextMessage = false;
1346 			}
1347 			else
1348 			{
1349 DBG(OUT("LOOPER: fLastMessage: 0x%lx: %.4s\n", fLastMessage->what,
1350 (char*)&fLastMessage->what));
1351 DBG(fLastMessage->PrintToStream());
1352 				//	Get the target handler
1353 				//	Use BMessage friend functions to determine if we are using the
1354 				//	preferred handler, or if a target has been specified
1355 				BHandler* handler;
1356 				if (_use_preferred_target_(fLastMessage))
1357 				{
1358 DBG(OUT("LOOPER: use preferred target\n"));
1359 					handler = fPreferred;
1360 				}
1361 				else
1362 				{
1363 DBG(OUT("LOOPER: don't use preferred target\n"));
1364 					/**
1365 						@note	Here is where all the token stuff starts to
1366 								make sense.  How, exactly, do we determine
1367 								what the target BHandler is?  If we look at
1368 								BMessage, we see an int32 field, fTarget.
1369 								Amazingly, we happen to have a global mapping
1370 								of BHandler pointers to int32s!
1371 					 */
1372 DBG(OUT("LOOPER: use: %ld\n", _get_message_target_(fLastMessage)));
1373 					 gDefaultTokens.GetToken(_get_message_target_(fLastMessage),
1374 					 						 B_HANDLER_TOKEN,
1375 					 						 (void**)&handler);
1376 DBG(OUT("LOOPER: handler: %p, this: %p\n", handler, this));
1377 				}
1378 
1379 				if (!handler)
1380 				{
1381 DBG(OUT("LOOPER: no target handler, use this\n"));
1382 					handler = this;
1383 				}
1384 
1385 				//	Is this a scripting message? (BMessage::HasSpecifiers())
1386 				if (fLastMessage->HasSpecifiers())
1387 				{
1388 					int32 index = 0;
1389 					// Make sure the current specifier is kosher
1390 					if (fLastMessage->GetCurrentSpecifier(&index) == B_OK)
1391 					{
1392 						handler = resolve_specifier(handler, fLastMessage);
1393 					}
1394 				}
1395 				else
1396 				{
1397 					DBG(OUT("LOOPER: no scripting message\n"));
1398 				}
1399 
1400 				if (handler)
1401 				{
1402 					//	Do filtering
1403 					handler = top_level_filter(fLastMessage, handler);
1404 DBG(OUT("LOOPER: top_level_filter(): %p\n", handler));
1405 					if (handler && handler->Looper() == this)
1406 					{
1407 						DispatchMessage(fLastMessage, handler);
1408 					}
1409 				}
1410 			}
1411 
1412 			//	Unlock the looper
1413 			Unlock();
1414 
1415 			//	Delete the current message (fLastMessage)
1416 			if (fLastMessage)
1417 			{
1418 				delete fLastMessage;
1419 				fLastMessage = NULL;
1420 			}
1421 
1422 			//	Are any messages on the port?
1423 			if (port_count(fMsgPort) > 0)
1424 			{
1425 				//	Do outer loop
1426 				dispatchNextMessage = false;
1427 			}
1428 		}
1429 	}
1430 DBG(OUT("BLooper::task_looper() done\n"));
1431 }
1432 //------------------------------------------------------------------------------
1433 void BLooper::do_quit_requested(BMessage* msg)
1434 {
1435 /**
1436 	@note	I couldn't figure out why do_quit_requested() was necessary; why not
1437 			just call Quit()?  Then, while writing the PostMessage() code, I
1438 			realized that the sender of the B_QUIT_REQUESTED message just might
1439 			be waiting for a reply.  A quick test, and yes, we get a reply
1440 			which consists of:
1441 				what: B_REPLY
1442 				"result" (bool) return of QuitRequested()
1443 				"thread" (int32) the looper's thread id
1444 
1445 			While Quit() could use fLastMessage, it makes more sense that
1446 			do_quit_requested() would handle it since it gets passed the
1447 			message.
1448  */
1449 
1450 	bool isQuitting = QuitRequested();
1451 
1452 	if (msg->IsSourceWaiting())
1453 	{
1454 		BMessage ReplyMsg(B_REPLY);
1455 		ReplyMsg.AddBool("result", isQuitting);
1456 		ReplyMsg.AddInt32("thread", fTaskID);
1457 		msg->SendReply(&ReplyMsg);
1458 	}
1459 
1460 	if (isQuitting)
1461 	{
1462 		Quit();
1463 	}
1464 }
1465 //------------------------------------------------------------------------------
1466 bool BLooper::AssertLocked() const
1467 {
1468 	if (!IsLocked())
1469 	{
1470 		debugger("looper must be locked before proceeding\n");
1471 		return false;
1472 	}
1473 
1474 	return true;
1475 }
1476 //------------------------------------------------------------------------------
1477 BHandler* BLooper::top_level_filter(BMessage* msg, BHandler* target)
1478 {
1479 	if (msg)
1480 	{
1481 		// Apply the common filters first
1482 		target = apply_filters(CommonFilterList(), msg, target);
1483 		if (target)
1484 		{
1485 			if (target->Looper() != this)
1486 			{
1487 				debugger("Targeted handler does not belong to the looper.");
1488 				target = NULL;
1489 			}
1490 			else
1491 			{
1492 				// Now apply handler-specific filters
1493 				target = handler_only_filter(msg, target);
1494 			}
1495 		}
1496 	}
1497 
1498 	return target;
1499 }
1500 //------------------------------------------------------------------------------
1501 BHandler* BLooper::handler_only_filter(BMessage* msg, BHandler* target)
1502 {
1503 	// Keep running filters until our handler is NULL, or until the filtering
1504 	// handler returns itself as the designated handler
1505 	BHandler* oldTarget = NULL;
1506 	while (target && (target != oldTarget))
1507 	{
1508 		oldTarget = target;
1509 		target = apply_filters(oldTarget->FilterList(), msg, oldTarget);
1510 		if (target && (target->Looper() != this))
1511 		{
1512 			debugger("Targeted handler does not belong to the looper.");
1513 			target = NULL;
1514 		}
1515 	}
1516 
1517 	return target;
1518 }
1519 //------------------------------------------------------------------------------
1520 BHandler* BLooper::apply_filters(BList* list, BMessage* msg, BHandler* target)
1521 {
1522 	// This is where the action is!
1523 	// Check the parameters
1524 	if (!list || !msg)
1525 	{
1526 		return target;
1527 	}
1528 
1529 	// For each filter in the provided list
1530 	BMessageFilter* filter = NULL;
1531 	for (int32 i = 0; i < list->CountItems(); ++i)
1532 	{
1533 		filter = (BMessageFilter*)list->ItemAt(i);
1534 
1535 		// Check command conditions
1536 		if (filter->FiltersAnyCommand() || (filter->Command() == msg->what))
1537 		{
1538 			// Check delivery conditions
1539 			message_delivery delivery = filter->MessageDelivery();
1540 			bool dropped = msg->WasDropped();
1541 			if (delivery == B_ANY_DELIVERY ||
1542 				((delivery == B_DROPPED_DELIVERY) && dropped) ||
1543 				((delivery == B_PROGRAMMED_DELIVERY) && !dropped))
1544 			{
1545 				// Check source conditions
1546 				message_source source = filter->MessageSource();
1547 				bool remote = msg->IsSourceRemote();
1548 				if (source == B_ANY_SOURCE ||
1549 					((source == B_REMOTE_SOURCE) && remote) ||
1550 					((source == B_LOCAL_SOURCE) && !remote))
1551 				{
1552 					filter_result result;
1553 					// Are we using an "external" function?
1554 					filter_hook func = filter->FilterFunction();
1555 					if (func)
1556 					{
1557 						result = func(msg, &target, filter);
1558 					}
1559 					else
1560 					{
1561 						result = filter->Filter(msg, &target);
1562 					}
1563 
1564 					// Is further processing allowed?
1565 					if (result == B_SKIP_MESSAGE)
1566 					{
1567 						// No; time to bail out
1568 						return NULL;
1569 					}
1570 				}
1571 			}
1572 		}
1573 	}
1574 
1575 	return target;
1576 }
1577 //------------------------------------------------------------------------------
1578 void BLooper::check_lock()
1579 {
1580 	// NOTE: any use for this?
1581 }
1582 //------------------------------------------------------------------------------
1583 BHandler* BLooper::resolve_specifier(BHandler* target, BMessage* msg)
1584 {
1585 	// Check params
1586 	if (!target || !msg)
1587 	{
1588 		return NULL;
1589 	}
1590 
1591 	int32 index;
1592 	BMessage specifier;
1593 	int32 form;
1594 	const char* property;
1595 	status_t err = B_OK;
1596 	BHandler* newTarget = target;
1597 	//	Loop to deal with nested specifiers
1598 	//	(e.g., the 3rd button on the 4th view)
1599 	do
1600 	{
1601 		err = msg->GetCurrentSpecifier(&index, &specifier, &form, &property);
1602 		if (err)
1603 		{
1604 			BMessage reply(B_REPLY);
1605 			reply.AddInt32("error", err);
1606 			msg->SendReply(&reply);
1607 			return NULL;
1608 		}
1609 		//	Current target gets what was the new target
1610 		target = newTarget;
1611 		newTarget = target->ResolveSpecifier(msg, index, &specifier, form,
1612 											 property);
1613 		//	Check that new target is owned by looper;
1614 		//	use IndexOf() to avoid dereferencing newTarget
1615 		//	(possible race condition with object destruction
1616 		//	by another looper)
1617 		if (!newTarget || IndexOf(newTarget) < 0)
1618 		{
1619 			return NULL;
1620 		}
1621 
1622 		//	Get current specifier index (may change in ResolveSpecifier())
1623 		msg->GetCurrentSpecifier(&index);
1624 	} while (newTarget && newTarget != target && index >= 0);
1625 
1626 	return newTarget;
1627 }
1628 //------------------------------------------------------------------------------
1629 void BLooper::UnlockFully()
1630 {
1631 	AssertLocked();
1632 
1633 /**
1634 	@note	What we're doing here is completely undoing the current owner's lock
1635 			on the looper.  This is actually pretty easy, since the owner only
1636 			has a single aquisition on the semaphore; every subsequent "lock"
1637 			is just an increment to the owner count.  The whole thing is quite
1638 			similar to Unlock(), except that we clear the ownership variables,
1639 			rather than merely decrementing them.
1640  */
1641 	// Clear the owner count
1642 	fOwnerCount = 0;
1643 	// Nobody owns the lock now
1644 	fOwner = -1;
1645 	// There is now one less thread holding a lock on this looper
1646 // bonefish: Currently _Lock() always acquires the semaphore.
1647 /*	long atomicCount = */atomic_add(&fAtomicCount, -1);
1648 //	if (atomicCount > 0)
1649 	{
1650 		release_sem(fLockSem);
1651 	}
1652 }
1653 //------------------------------------------------------------------------------
1654 void BLooper::AddLooper(BLooper* loop)
1655 {
1656 	if (gLooperList.IsLocked())
1657 	{
1658 		gLooperList.AddLooper(loop);
1659 	}
1660 }
1661 //------------------------------------------------------------------------------
1662 bool BLooper::IsLooperValid(const BLooper* l)
1663 {
1664 	if (gLooperList.IsLocked())
1665 	{
1666 		return gLooperList.IsLooperValid(l);
1667 	}
1668 
1669 	return false;
1670 }
1671 //------------------------------------------------------------------------------
1672 void BLooper::RemoveLooper(BLooper* l)
1673 {
1674 	if (gLooperList.IsLocked())
1675 	{
1676 		gLooperList.RemoveLooper(l);
1677 	}
1678 }
1679 //------------------------------------------------------------------------------
1680 void BLooper::GetLooperList(BList* list)
1681 {
1682 	BObjectLocker<BLooperList> ListLock(gLooperList);
1683 	if (ListLock.IsLocked())
1684 	{
1685 		gLooperList.GetLooperList(list);
1686 	}
1687 }
1688 //------------------------------------------------------------------------------
1689 BLooper* BLooper::LooperForName(const char* name)
1690 {
1691 	if (gLooperList.IsLocked())
1692 	{
1693 		return gLooperList.LooperForName(name);
1694 	}
1695 
1696 	return NULL;
1697 }
1698 //------------------------------------------------------------------------------
1699 BLooper* BLooper::LooperForPort(port_id port)
1700 {
1701 	if (gLooperList.IsLocked())
1702 	{
1703 		return gLooperList.LooperForPort(port);
1704 	}
1705 
1706 	return NULL;
1707 }
1708 //------------------------------------------------------------------------------
1709 
1710 
1711 //------------------------------------------------------------------------------
1712 port_id _get_looper_port_(const BLooper* looper)
1713 {
1714 	return looper->fMsgPort;
1715 }
1716 //------------------------------------------------------------------------------
1717 
1718 /*
1719  * $Log $
1720  *
1721  * $Id  $
1722  *
1723  */
1724 
1725