xref: /haiku/src/kits/app/Looper.cpp (revision 7120e97489acbf17d86d3f33e3b2e68974fd4b23)
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 = B_ERROR;
77 team_id			BLooper::sTeamID = 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(%lx)...\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 	if (PropertyInfo.FindMatch(msg, index, specifier, form, property, &data) >= 0)
733 	{
734 		switch (data)
735 		{
736 			case BLOOPER_PROCESS_INTERNALLY:
737 				return this;
738 
739 			case BLOOPER_HANDLER_BY_INDEX:
740 			{
741 				int32 index = specifier->FindInt32("index");
742 				if (form == B_REVERSE_INDEX_SPECIFIER)
743 				{
744 					index = CountHandlers() - index;
745 				}
746 				BHandler* target = HandlerAt(index);
747 				if (target)
748 				{
749 					// Specifier has been fully handled
750 					msg->PopSpecifier();
751 					return target;
752 				}
753 				else
754 				{
755 					// TODO: test and implement
756 				}
757 			}
758 		}
759 	}
760 	else
761 	{
762 		return BHandler::ResolveSpecifier(msg, index, specifier, form,
763 										  property);
764 	}
765 
766 	BMessage Reply(B_MESSAGE_NOT_UNDERSTOOD);
767 	Reply.AddInt32("error", B_BAD_SCRIPT_SYNTAX);
768 	Reply.AddString("message", "Didn't understand the specifier(s)");
769 	msg->SendReply(&Reply);
770 
771 	return NULL;
772 }
773 //------------------------------------------------------------------------------
774 status_t BLooper::GetSupportedSuites(BMessage* data)
775 {
776 	status_t err;
777 	if (!data)
778 	{
779 		err = B_BAD_VALUE;
780 	}
781 
782 	if (!err)
783 	{
784 		err = data->AddString("Suites", "suite/vnd.Be-handler");
785 		if (!err)
786 		{
787 			BPropertyInfo PropertyInfo(gLooperPropInfo);
788 			err = data->AddFlat("message", &PropertyInfo);
789 			if (!err)
790 			{
791 				err = BHandler::GetSupportedSuites(data);
792 			}
793 		}
794 	}
795 
796 	return err;
797 }
798 //------------------------------------------------------------------------------
799 void BLooper::AddCommonFilter(BMessageFilter* filter)
800 {
801 	if (!filter)
802 	{
803 		return;
804 	}
805 
806 	if (!IsLocked())
807 	{
808 		debugger("Owning Looper must be locked before calling AddCommonFilter");
809 		return;
810 	}
811 
812 	if (filter->Looper())
813 	{
814 		debugger("A MessageFilter can only be used once.");
815 		return;
816 	}
817 
818 	if (!fCommonFilters)
819 	{
820 		fCommonFilters = new BList(FILTER_LIST_BLOCK_SIZE);
821 	}
822 	filter->SetLooper(this);
823 	fCommonFilters->AddItem(filter);
824 }
825 //------------------------------------------------------------------------------
826 bool BLooper::RemoveCommonFilter(BMessageFilter* filter)
827 {
828 	if (!IsLocked())
829 	{
830 		debugger("Owning Looper must be locked before calling "
831 				 "RemoveCommonFilter");
832 		return false;
833 	}
834 
835 	if (!fCommonFilters)
836 	{
837 		return false;
838 	}
839 
840 	bool result = fCommonFilters->RemoveItem(filter);
841 	if (result)
842 	{
843 		filter->SetLooper(NULL);
844 	}
845 
846 	return result;
847 }
848 //------------------------------------------------------------------------------
849 void BLooper::SetCommonFilterList(BList* filters)
850 {
851 	// We have a somewhat serious problem here.  It is entirely possible in R5
852 	// to assign a given list of filters to *two* BLoopers simultaneously.  This
853 	// becomes problematic when the loopers are destroyed: the last looper
854 	// destroyed will have a problem when it tries to delete a filter list that
855 	// has already been deleted.  In R5, this results in a general protection
856 	// fault.  We fix this by checking the filter list for ownership issues.
857 
858 	if (!IsLocked())
859 	{
860 		debugger("Owning Looper must be locked before calling "
861 				 "SetCommonFilterList");
862 		return;
863 	}
864 
865 	BMessageFilter* filter;
866 	if (filters)
867 	{
868 		// Check for ownership issues
869 		for (int32 i = 0; i < filters->CountItems(); ++i)
870 		{
871 			filter = (BMessageFilter*)filters->ItemAt(i);
872 			if (filter->Looper())
873 			{
874 				debugger("A MessageFilter can only be used once.");
875 				return;
876 			}
877 		}
878 	}
879 
880 	if (fCommonFilters)
881 	{
882 		for (int32 i = 0; i < fCommonFilters->CountItems(); ++i)
883 		{
884 			delete fCommonFilters->ItemAt(i);
885 		}
886 
887 		delete fCommonFilters;
888 		fCommonFilters = NULL;
889 	}
890 
891 	// Per the BeBook, we take ownership of the list
892 	fCommonFilters = filters;
893 	if (fCommonFilters)
894 	{
895 		for (int32 i = 0; i < fCommonFilters->CountItems(); ++i)
896 		{
897 			filter = (BMessageFilter*)fCommonFilters->ItemAt(i);
898 			filter->SetLooper(this);
899 		}
900 	}
901 }
902 //------------------------------------------------------------------------------
903 BList* BLooper::CommonFilterList() const
904 {
905 	return fCommonFilters;
906 }
907 //------------------------------------------------------------------------------
908 status_t BLooper::Perform(perform_code d, void* arg)
909 {
910 	// This is sort of what we're doing for this function everywhere
911 	return BHandler::Perform(d, arg);
912 }
913 //------------------------------------------------------------------------------
914 BMessage* BLooper::MessageFromPort(bigtime_t timeout)
915 {
916 	return ReadMessageFromPort(timeout);
917 }
918 //------------------------------------------------------------------------------
919 void BLooper::_ReservedLooper1()
920 {
921 }
922 //------------------------------------------------------------------------------
923 void BLooper::_ReservedLooper2()
924 {
925 }
926 //------------------------------------------------------------------------------
927 void BLooper::_ReservedLooper3()
928 {
929 }
930 //------------------------------------------------------------------------------
931 void BLooper::_ReservedLooper4()
932 {
933 }
934 //------------------------------------------------------------------------------
935 void BLooper::_ReservedLooper5()
936 {
937 }
938 //------------------------------------------------------------------------------
939 void BLooper::_ReservedLooper6()
940 {
941 }
942 //------------------------------------------------------------------------------
943 BLooper::BLooper(const BLooper&)
944 {
945 	// Copy construction not allowed
946 }
947 //------------------------------------------------------------------------------
948 BLooper& BLooper::operator=(const BLooper& )
949 {
950 	// Looper copying not allowed
951 	return *this;
952 }
953 //------------------------------------------------------------------------------
954 BLooper::BLooper(int32 priority, port_id port, const char* name)
955 {
956 	// This must be a legacy constructor
957 	fMsgPort = port;
958 	InitData(name, priority, B_LOOPER_PORT_DEFAULT_CAPACITY);
959 }
960 //------------------------------------------------------------------------------
961 status_t BLooper::_PostMessage(BMessage* msg, BHandler* handler,
962 							   BHandler* reply_to)
963 {
964 	BObjectLocker<BLooperList> ListLock(gLooperList);
965 	if (!ListLock.IsLocked())
966 	{
967 		return B_BAD_VALUE;
968 	}
969 
970 	if (!IsLooperValid(this))
971 	{
972 		return B_BAD_VALUE;
973 	}
974 
975 	// Does handler belong to this looper?
976 	if (handler && handler->Looper() != this)
977 	{
978 		return B_MISMATCHED_VALUES;
979 	}
980 
981 	status_t err = B_OK;
982 
983 	BMessenger Messenger(handler, this, &err);
984 
985 	if (!err)
986 	{
987 		err = Messenger.SendMessage(msg, reply_to, 0);
988 	}
989 
990 	return err;
991 }
992 //------------------------------------------------------------------------------
993 status_t BLooper::_Lock(BLooper* loop, port_id port, bigtime_t timeout)
994 {
995 DBG(OUT("BLooper::_Lock(%p, %lx)\n", loop, port));
996 /**
997 	@note	The assumption I'm under here is that since we can get the port of
998 			the BLooper directly from the BLooper itself, the port parameter is
999 			for identifying BLoopers by port_id when a pointer to the BLooper in
1000 			question is not available.  So this function has two modes:
1001 				o When loop != NULL, use it directly
1002 				o When loop == NULL and port is valid, use the port_id to get
1003 				  the looper
1004 			I scoured the docs to find out what constitutes a valid port_id to
1005 			no avail.  Since create_port uses the standard error values in its
1006 			returned port_id, I'll assume that anything less than zero is a safe
1007 			bet as an *invalid* port_id.  I'm guessing that, like thread and
1008 			semaphore ids, anything >= zero is valid.  So, the short version of
1009 			this reads: if you don't want to find by port_id, make port = -1.
1010 
1011 			Another assumption I'm making is that Lock() and LockWithTimeout()
1012 			are covers for this function.  If it turns out that we don't really
1013 			need this function, I may refactor this code into LockWithTimeout()
1014 			and have Lock() call it instead.  This function could then be
1015 			removed.
1016  */
1017 
1018 	//	Check params (loop, port)
1019 	if (!loop && port < 0)
1020 	{
1021 DBG(OUT("BLooper::_Lock() done 1\n"));
1022 		return B_BAD_VALUE;
1023 	}
1024 
1025 	// forward declared so I can use BAutolock on sLooperListLock
1026 	thread_id curThread;
1027 	sem_id sem;
1028 
1029 /**
1030 	@note	We lock the looper list at the start of the lock operation to
1031 			prevent the looper getting removed from the list while we're
1032 			doing list operations.  Also ensures that the looper doesn't
1033 			get deleted here (since ~BLooper() has to lock the list as
1034 			well to remove itself).
1035  */
1036 	{
1037 		BObjectLocker<BLooperList> ListLock(gLooperList);
1038 		if (!ListLock.IsLocked())
1039 		{
1040 			// If we can't lock, the semaphore is probably
1041 			// gone, which leaves us in no-man's land
1042 DBG(OUT("BLooper::_Lock() done 2\n"));
1043 			return B_BAD_VALUE;
1044 		}
1045 
1046 		//	Look up looper by port_id, if necessary
1047 		if (!loop)
1048 		{
1049 			loop = LooperForPort(port);
1050 			if (!loop)
1051 			{
1052 DBG(OUT("BLooper::_Lock() done 3\n"));
1053 				return B_BAD_VALUE;
1054 			}
1055 		}
1056 		else
1057 		{
1058 			//	Check looper validity
1059 			if (!IsLooperValid(loop))
1060 			{
1061 DBG(OUT("BLooper::_Lock() done 4\n"));
1062 				return B_BAD_VALUE;
1063 			}
1064 		}
1065 
1066 		//	Is the looper trying to lock itself?
1067 		//	Check for nested lock attempt
1068 		curThread = find_thread(NULL);
1069 		if (curThread == loop->fOwner)
1070 		{
1071 			//	Bump fOwnerCount
1072 			++loop->fOwnerCount;
1073 DBG(OUT("BLooper::_Lock() done 5: fOwnerCount: %ld\n", loop->fOwnerCount));
1074 			return B_OK;
1075 		}
1076 
1077 		//	Something external to the looper is attempting to lock
1078 		//	Cache the semaphore
1079 		sem = loop->fLockSem;
1080 
1081 		//	Validate the semaphore
1082 		if (sem < 0)
1083 		{
1084 DBG(OUT("BLooper::_Lock() done 6\n"));
1085 			return B_BAD_VALUE;
1086 		}
1087 
1088 		//	Bump the requested lock count (using fAtomicCount for this)
1089 		atomic_add(&loop->fAtomicCount, 1);
1090 
1091 		// sLooperListLock automatically released here
1092 	}
1093 
1094 /**
1095 	@note	We have to operate with the looper list unlocked during semaphore
1096 			acquisition so that the rest of the application doesn't have to
1097 			wait for this lock to happen.  This is why we cached fLockSem
1098 			earlier -- with the list unlocked, the looper might get deleted
1099 			right out from under us.  This is also why we use a raw semaphore
1100 			instead of the easier-to-deal-with BLocker; you can't cache a
1101 			BLocker.
1102  */
1103 	//	acquire the lock
1104 	status_t err;
1105 	do
1106 	{
1107 		err = acquire_sem_etc(sem, 1, B_RELATIVE_TIMEOUT, timeout);
1108 	} while (err == B_INTERRUPTED);
1109 
1110 	if (!err)
1111 	{
1112 		//		Assign current thread to fOwner
1113 		loop->fOwner = curThread;
1114 		//		Reset fOwnerCount to 1
1115 		loop->fOwnerCount = 1;
1116 	}
1117 
1118 DBG(OUT("BLooper::_Lock() done: %lx\n", err));
1119 	return err;
1120 }
1121 //------------------------------------------------------------------------------
1122 status_t BLooper::_LockComplete(BLooper* loop, int32 old, thread_id this_tid,
1123 								sem_id sem, bigtime_t timeout)
1124 {
1125 	// What is this for?  Hope I'm not missing something conceptually here ...
1126 	return B_ERROR;
1127 }
1128 //------------------------------------------------------------------------------
1129 void BLooper::InitData()
1130 {
1131 	fOwner = B_ERROR;
1132 	fRunCalled = false;
1133 	fQueue = new BMessageQueue();
1134 	fCommonFilters = NULL;
1135 	fLastMessage = NULL;
1136 	fPreferred = NULL;
1137 	fTaskID = B_ERROR;
1138 	fTerminating = false;
1139 	fMsgPort = -1;
1140 
1141 	if (sTeamID == -1)
1142 	{
1143 		thread_info info;
1144 		get_thread_info(find_thread(NULL), &info);
1145 		sTeamID = info.team;
1146 	}
1147 
1148 }
1149 //------------------------------------------------------------------------------
1150 void BLooper::InitData(const char* name, int32 priority, int32 port_capacity)
1151 {
1152 	InitData();
1153 
1154 	fLockSem = create_sem(1, name);
1155 
1156 	if (port_capacity <= 0)
1157 	{
1158 		port_capacity = B_LOOPER_PORT_DEFAULT_CAPACITY;
1159 	}
1160 
1161 	fMsgPort = create_port(port_capacity, name ? name : "LooperPort");
1162 
1163 	fInitPriority = priority;
1164 
1165 	BObjectLocker<BLooperList> ListLock(gLooperList);
1166 	AddLooper(this);
1167 	AddHandler(this);
1168 }
1169 //------------------------------------------------------------------------------
1170 void BLooper::AddMessage(BMessage* msg)
1171 {
1172 	// NOTE: Why is this here?
1173 }
1174 //------------------------------------------------------------------------------
1175 void BLooper::_AddMessagePriv(BMessage* msg)
1176 {
1177 	// NOTE: No, really; why the hell is this here??
1178 }
1179 //------------------------------------------------------------------------------
1180 status_t BLooper::_task0_(void* arg)
1181 {
1182 DBG(OUT("LOOPER: _task0_()\n"));
1183 	BLooper* obj = (BLooper*)arg;
1184 
1185 DBG(OUT("LOOPER: locking looper...\n"));
1186 	if (obj->Lock())
1187 	{
1188 DBG(OUT("LOOPER: looper locked\n"));
1189 		obj->task_looper();
1190 		obj->fTerminating = true;
1191 		delete obj;
1192 	}
1193 
1194 DBG(OUT("LOOPER: _task0_() done: thread %ld\n", find_thread(NULL)));
1195 	return B_OK;
1196 }
1197 //------------------------------------------------------------------------------
1198 void* BLooper::ReadRawFromPort(int32* msgcode, bigtime_t tout)
1199 {
1200 DBG(OUT("BLooper::ReadRawFromPort()\n"));
1201 	int8* msgbuffer = NULL;
1202 	ssize_t buffersize;
1203 	ssize_t bytesread;
1204 
1205 	if (tout == B_INFINITE_TIMEOUT)
1206 	{
1207 		buffersize = port_buffer_size(fMsgPort);
1208 DBG(OUT("BLooper::ReadRawFromPort(): buffersize: %ld\n", buffersize));
1209 	}
1210 	else
1211 	{
1212 		buffersize = port_buffer_size_etc(fMsgPort, 0, tout);
1213 		if (buffersize == B_TIMED_OUT || buffersize == B_BAD_PORT_ID ||
1214 			buffersize == B_WOULD_BLOCK)
1215 		{
1216 DBG(OUT("BLooper::ReadRawFromPort() done 1\n"));
1217 			return NULL;
1218 		}
1219 	}
1220 
1221 	if (buffersize > 0)
1222 		msgbuffer = new int8[buffersize];
1223 
1224 	if (tout == B_INFINITE_TIMEOUT)
1225 	{
1226 DBG(OUT("read_port()...\n"));
1227 		bytesread = read_port(fMsgPort, msgcode, msgbuffer, buffersize);
1228 DBG(OUT("read_port() done: %ld\n", bytesread));
1229 DBG(OUT("BLooper::ReadRawFromPort() read: %.4s\n", (char*)msgcode));
1230 	}
1231 	else
1232 	{
1233 		bytesread = read_port_etc(fMsgPort, msgcode, msgbuffer, buffersize,
1234 								  B_TIMEOUT, tout);
1235 	}
1236 
1237 DBG(OUT("BLooper::ReadRawFromPort() done: %p\n", msgbuffer));
1238 	return msgbuffer;
1239 }
1240 //------------------------------------------------------------------------------
1241 BMessage* BLooper::ReadMessageFromPort(bigtime_t tout)
1242 {
1243 DBG(OUT("BLooper::ReadMessageFromPort()\n"));
1244 	int32 msgcode;
1245 	BMessage* bmsg;
1246 
1247 	void* msgbuffer = ReadRawFromPort(&msgcode, tout);
1248 
1249 	bmsg = ConvertToMessage(msgbuffer, msgcode);
1250 
1251 	if (msgbuffer)
1252 	{
1253 		delete[] msgbuffer;
1254 	}
1255 
1256 DBG(OUT("BLooper::ReadMessageFromPort() done: %p\n", bmsg));
1257 	return bmsg;
1258 }
1259 //------------------------------------------------------------------------------
1260 BMessage* BLooper::ConvertToMessage(void* raw, int32 code)
1261 {
1262 DBG(OUT("BLooper::ConvertToMessage()\n"));
1263 	BMessage* bmsg = new BMessage(code);
1264 
1265 	if (raw != NULL)
1266 	{
1267 		if (bmsg->Unflatten((const char*)raw) != B_OK)
1268 		{
1269 DBG(OUT("BLooper::ConvertToMessage(): unflattening message failed\n"));
1270 			delete bmsg;
1271 			bmsg = NULL;
1272 		}
1273 	}
1274 
1275 DBG(OUT("BLooper::ConvertToMessage(): %p\n", bmsg));
1276 	return bmsg;
1277 }
1278 //------------------------------------------------------------------------------
1279 void BLooper::task_looper()
1280 {
1281 DBG(OUT("BLooper::task_looper()\n"));
1282 	//	Check that looper is locked (should be)
1283 	AssertLocked();
1284 	//	Unlock the looper
1285 	Unlock();
1286 
1287 	//	loop: As long as we are not terminating.
1288 	while (!fTerminating)
1289 	{
1290 DBG(OUT("LOOPER: outer loop\n"));
1291 		// TODO: timeout determination algo
1292 		//	Read from message port (how do we determine what the timeout is?)
1293 DBG(OUT("LOOPER: MessageFromPort()...\n"));
1294 		BMessage* msg = MessageFromPort();
1295 DBG(OUT("LOOPER: ...done\n"));
1296 
1297 		//	Did we get a message?
1298 		if (msg)
1299 		{
1300 DBG(OUT("LOOPER: got message\n"));
1301 			//	Add to queue
1302 			fQueue->AddMessage(msg);
1303 		}
1304 else
1305 DBG(OUT("LOOPER: got no message\n"));
1306 
1307 		//	Get message count from port
1308 		int32 msgCount = port_count(fMsgPort);
1309 		for (int32 i = 0; i < msgCount; ++i)
1310 		{
1311 			//	Read 'count' messages from port (so we will not block)
1312 			//	We use zero as our timeout since we know there is stuff there
1313 			msg = MessageFromPort(0);
1314 			//	Add messages to queue
1315 			if (msg)
1316 			{
1317 				fQueue->AddMessage(msg);
1318 			}
1319 		}
1320 
1321 		//	loop: As long as there are messages in the queue and the port is
1322 		//		  empty... and we are not terminating, of course.
1323 		bool dispatchNextMessage = true;
1324 		while (!fTerminating && dispatchNextMessage)
1325 		{
1326 DBG(OUT("LOOPER: inner loop\n"));
1327 			//	Get next message from queue (assign to fLastMessage)
1328 			fLastMessage = fQueue->NextMessage();
1329 
1330 			//	Lock the looper
1331 			Lock();
1332 			if (!fLastMessage)
1333 			{
1334 				// No more messages: Unlock the looper and terminate the
1335 				// dispatch loop.
1336 				dispatchNextMessage = false;
1337 			}
1338 			else
1339 			{
1340 DBG(OUT("LOOPER: fLastMessage: 0x%lx: %.4s\n", fLastMessage->what,
1341 (char*)&fLastMessage->what));
1342 DBG(fLastMessage->PrintToStream());
1343 				//	Get the target handler
1344 				//	Use BMessage friend functions to determine if we are using the
1345 				//	preferred handler, or if a target has been specified
1346 				BHandler* handler;
1347 				if (_use_preferred_target_(fLastMessage))
1348 				{
1349 DBG(OUT("LOOPER: use preferred target\n"));
1350 					handler = fPreferred;
1351 				}
1352 				else
1353 				{
1354 DBG(OUT("LOOPER: don't use preferred target\n"));
1355 					/**
1356 						@note	Here is where all the token stuff starts to
1357 								make sense.  How, exactly, do we determine
1358 								what the target BHandler is?  If we look at
1359 								BMessage, we see an int32 field, fTarget.
1360 								Amazingly, we happen to have a global mapping
1361 								of BHandler pointers to int32s!
1362 					 */
1363 DBG(OUT("LOOPER: use: %ld\n", _get_message_target_(fLastMessage)));
1364 					 gDefaultTokens.GetToken(_get_message_target_(fLastMessage),
1365 					 						 B_HANDLER_TOKEN,
1366 					 						 (void**)&handler);
1367 DBG(OUT("LOOPER: handler: %p, this: %p\n", handler, this));
1368 				}
1369 
1370 				if (!handler)
1371 				{
1372 DBG(OUT("LOOPER: no target handler, use this\n"));
1373 					handler = this;
1374 				}
1375 
1376 				//	Is this a scripting message? (BMessage::HasSpecifiers())
1377 				if (fLastMessage->HasSpecifiers())
1378 				{
1379 					int32 index = 0;
1380 					// Make sure the current specifier is kosher
1381 					if (fLastMessage->GetCurrentSpecifier(&index) == B_OK)
1382 					{
1383 						handler = resolve_specifier(handler, fLastMessage);
1384 					}
1385 				}
1386 else
1387 DBG(OUT("LOOPER: no scripting message\n"));
1388 
1389 				if (handler)
1390 				{
1391 					//	Do filtering
1392 					handler = top_level_filter(fLastMessage, handler);
1393 DBG(OUT("LOOPER: top_level_filter(): %p\n", handler));
1394 					if (handler && handler->Looper() == this)
1395 					{
1396 						DispatchMessage(fLastMessage, handler);
1397 					}
1398 				}
1399 			}
1400 
1401 			//	Unlock the looper
1402 			Unlock();
1403 
1404 			//	Delete the current message (fLastMessage)
1405 			if (fLastMessage)
1406 			{
1407 				delete fLastMessage;
1408 				fLastMessage = NULL;
1409 			}
1410 
1411 			//	Are any messages on the port?
1412 			if (port_count(fMsgPort) > 0)
1413 			{
1414 				//	Do outer loop
1415 				dispatchNextMessage = false;
1416 			}
1417 		}
1418 	}
1419 DBG(OUT("BLooper::task_looper() done\n"));
1420 }
1421 //------------------------------------------------------------------------------
1422 void BLooper::do_quit_requested(BMessage* msg)
1423 {
1424 /**
1425 	@note	I couldn't figure out why do_quit_requested() was necessary; why not
1426 			just call Quit()?  Then, while writing the PostMessage() code, I
1427 			realized that the sender of the B_QUIT_REQUESTED message just might
1428 			be waiting for a reply.  A quick test, and yes, we get a reply
1429 			which consists of:
1430 				what: B_REPLY
1431 				"result" (bool) return of QuitRequested()
1432 				"thread" (int32) the looper's thread id
1433 
1434 			While Quit() could use fLastMessage, it makes more sense that
1435 			do_quit_requested() would handle it since it gets passed the
1436 			message.
1437  */
1438 
1439 	bool isQuitting = QuitRequested();
1440 
1441 	if (msg->IsSourceWaiting())
1442 	{
1443 		BMessage ReplyMsg(B_REPLY);
1444 		ReplyMsg.AddBool("result", isQuitting);
1445 		ReplyMsg.AddInt32("thread", fTaskID);
1446 		msg->SendReply(&ReplyMsg);
1447 	}
1448 
1449 	if (isQuitting)
1450 	{
1451 		Quit();
1452 	}
1453 }
1454 //------------------------------------------------------------------------------
1455 bool BLooper::AssertLocked() const
1456 {
1457 	if (!IsLocked())
1458 	{
1459 		debugger("looper must be locked before proceeding\n");
1460 		return false;
1461 	}
1462 
1463 	return true;
1464 }
1465 //------------------------------------------------------------------------------
1466 BHandler* BLooper::top_level_filter(BMessage* msg, BHandler* target)
1467 {
1468 	if (msg)
1469 	{
1470 		// Apply the common filters first
1471 		target = apply_filters(CommonFilterList(), msg, target);
1472 		if (target)
1473 		{
1474 			if (target->Looper() != this)
1475 			{
1476 				// TODO: debugger message?
1477 				target = NULL;
1478 			}
1479 			else
1480 			{
1481 				// Now apply handler-specific filters
1482 				target = handler_only_filter(msg, target);
1483 			}
1484 		}
1485 	}
1486 
1487 	return target;
1488 }
1489 //------------------------------------------------------------------------------
1490 BHandler* BLooper::handler_only_filter(BMessage* msg, BHandler* target)
1491 {
1492 	// Keep running filters until our handler is NULL, or until the filtering
1493 	// handler returns itself as the designated handler
1494 	BHandler* oldTarget = NULL;
1495 	while (target && (target != oldTarget))
1496 	{
1497 		oldTarget = target;
1498 		target = apply_filters(oldTarget->FilterList(), msg, oldTarget);
1499 		if (target && (target->Looper() != this))
1500 		{
1501 			// TODO: debugger message?
1502 			target = NULL;
1503 		}
1504 	}
1505 
1506 	return target;
1507 }
1508 //------------------------------------------------------------------------------
1509 BHandler* BLooper::apply_filters(BList* list, BMessage* msg, BHandler* target)
1510 {
1511 	// This is where the action is!
1512 	// Check the parameters
1513 	if (!list || !msg)
1514 	{
1515 		return target;
1516 	}
1517 
1518 	// For each filter in the provided list
1519 	BMessageFilter* filter = NULL;
1520 	for (int32 i = 0; i < list->CountItems(); ++i)
1521 	{
1522 		filter = (BMessageFilter*)list->ItemAt(i);
1523 
1524 		// Check command conditions
1525 		if (filter->FiltersAnyCommand() || (filter->Command() == msg->what))
1526 		{
1527 			// Check delivery conditions
1528 			message_delivery delivery = filter->MessageDelivery();
1529 			bool dropped = msg->WasDropped();
1530 			if (delivery == B_ANY_DELIVERY ||
1531 				((delivery == B_DROPPED_DELIVERY) && dropped) ||
1532 				((delivery == B_PROGRAMMED_DELIVERY) && !dropped))
1533 			{
1534 				// Check source conditions
1535 				message_source source = filter->MessageSource();
1536 				bool remote = msg->IsSourceRemote();
1537 				if (source == B_ANY_SOURCE ||
1538 					((source == B_REMOTE_SOURCE) && remote) ||
1539 					((source == B_LOCAL_SOURCE) && !remote))
1540 				{
1541 					filter_result result;
1542 					// Are we using an "external" function?
1543 					filter_hook func = filter->FilterFunction();
1544 					if (func)
1545 					{
1546 						result = func(msg, &target, filter);
1547 					}
1548 					else
1549 					{
1550 						result = filter->Filter(msg, &target);
1551 					}
1552 
1553 					// Is further processing allowed?
1554 					if (result == B_SKIP_MESSAGE)
1555 					{
1556 						// No; time to bail out
1557 						return NULL;
1558 					}
1559 				}
1560 			}
1561 		}
1562 	}
1563 
1564 	return target;
1565 }
1566 //------------------------------------------------------------------------------
1567 void BLooper::check_lock()
1568 {
1569 	// NOTE: any use for this?
1570 }
1571 //------------------------------------------------------------------------------
1572 BHandler* BLooper::resolve_specifier(BHandler* target, BMessage* msg)
1573 {
1574 	// TODO: implement
1575 	return NULL;
1576 }
1577 //------------------------------------------------------------------------------
1578 void BLooper::UnlockFully()
1579 {
1580 	AssertLocked();
1581 
1582 /**
1583 	@note	What we're doing here is completely undoing the current owner's lock
1584 			on the looper.  This is actually pretty easy, since the owner only
1585 			has a single aquisition on the semaphore; every subsequent "lock"
1586 			is just an increment to the owner count.  The whole thing is quite
1587 			similar to Unlock(), except that we clear the ownership variables,
1588 			rather than merely decrementing them.
1589  */
1590 	// Clear the owner count
1591 	fOwnerCount = 0;
1592 	// Nobody owns the lock now
1593 	fOwner = -1;
1594 	// There is now one less thread holding a lock on this looper
1595 // bonefish: Currently _Lock() always acquires the semaphore.
1596 /*	long atomicCount = */atomic_add(&fAtomicCount, -1);
1597 //	if (atomicCount > 0)
1598 	{
1599 		release_sem(fLockSem);
1600 	}
1601 }
1602 //------------------------------------------------------------------------------
1603 void BLooper::AddLooper(BLooper* loop)
1604 {
1605 	if (gLooperList.IsLocked())
1606 	{
1607 		gLooperList.AddLooper(loop);
1608 	}
1609 }
1610 //------------------------------------------------------------------------------
1611 bool BLooper::IsLooperValid(const BLooper* l)
1612 {
1613 	if (gLooperList.IsLocked())
1614 	{
1615 		return gLooperList.IsLooperValid(l);
1616 	}
1617 
1618 	return false;
1619 }
1620 //------------------------------------------------------------------------------
1621 void BLooper::RemoveLooper(BLooper* l)
1622 {
1623 	if (gLooperList.IsLocked())
1624 	{
1625 		gLooperList.RemoveLooper(l);
1626 	}
1627 }
1628 //------------------------------------------------------------------------------
1629 void BLooper::GetLooperList(BList* list)
1630 {
1631 	BObjectLocker<BLooperList> ListLock(gLooperList);
1632 	if (ListLock.IsLocked())
1633 	{
1634 		gLooperList.GetLooperList(list);
1635 	}
1636 }
1637 //------------------------------------------------------------------------------
1638 BLooper* BLooper::LooperForName(const char* name)
1639 {
1640 	if (gLooperList.IsLocked())
1641 	{
1642 		return gLooperList.LooperForName(name);
1643 	}
1644 
1645 	return NULL;
1646 }
1647 //------------------------------------------------------------------------------
1648 BLooper* BLooper::LooperForPort(port_id port)
1649 {
1650 	if (gLooperList.IsLocked())
1651 	{
1652 		return gLooperList.LooperForPort(port);
1653 	}
1654 
1655 	return NULL;
1656 }
1657 //------------------------------------------------------------------------------
1658 
1659 
1660 //------------------------------------------------------------------------------
1661 port_id _get_looper_port_(const BLooper* looper)
1662 {
1663 	return looper->fMsgPort;
1664 }
1665 //------------------------------------------------------------------------------
1666 
1667 /*
1668  * $Log $
1669  *
1670  * $Id  $
1671  *
1672  */
1673 
1674