xref: /haiku/src/kits/app/Looper.cpp (revision 3d4a601414d90d3029e2a5c6d83580d628ef648f)
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()...\n"));
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 	return B_OK;
1195 }
1196 //------------------------------------------------------------------------------
1197 void* BLooper::ReadRawFromPort(int32* msgcode, bigtime_t tout)
1198 {
1199 DBG(OUT("BLooper::ReadRawFromPort()\n"));
1200 	int8* msgbuffer = NULL;
1201 	ssize_t buffersize;
1202 	ssize_t bytesread;
1203 
1204 	if (tout == B_INFINITE_TIMEOUT)
1205 	{
1206 		buffersize = port_buffer_size(fMsgPort);
1207 DBG(OUT("BLooper::ReadRawFromPort(): buffersize: %ld\n", buffersize));
1208 	}
1209 	else
1210 	{
1211 		buffersize = port_buffer_size_etc(fMsgPort, 0, tout);
1212 		if (buffersize == B_TIMED_OUT || buffersize == B_BAD_PORT_ID ||
1213 			buffersize == B_WOULD_BLOCK)
1214 		{
1215 DBG(OUT("BLooper::ReadRawFromPort() done 1\n"));
1216 			return NULL;
1217 		}
1218 	}
1219 
1220 	if (buffersize > 0)
1221 		msgbuffer = new int8[buffersize];
1222 
1223 	if (tout == B_INFINITE_TIMEOUT)
1224 	{
1225 DBG(OUT("read_port()...\n"));
1226 		bytesread = read_port(fMsgPort, msgcode, msgbuffer, buffersize);
1227 DBG(OUT("read_port() done: %ld\n", bytesread));
1228 DBG(OUT("BLooper::ReadRawFromPort() read: %.4s\n", (char*)msgcode));
1229 	}
1230 	else
1231 	{
1232 		bytesread = read_port_etc(fMsgPort, msgcode, msgbuffer, buffersize,
1233 								  B_TIMEOUT, tout);
1234 	}
1235 
1236 DBG(OUT("BLooper::ReadRawFromPort() done: %p\n", msgbuffer));
1237 	return msgbuffer;
1238 }
1239 //------------------------------------------------------------------------------
1240 BMessage* BLooper::ReadMessageFromPort(bigtime_t tout)
1241 {
1242 DBG(OUT("BLooper::ReadMessageFromPort()\n"));
1243 	int32 msgcode;
1244 	BMessage* bmsg;
1245 
1246 	void* msgbuffer = ReadRawFromPort(&msgcode, tout);
1247 
1248 	bmsg = ConvertToMessage(msgbuffer, msgcode);
1249 
1250 	if (msgbuffer)
1251 	{
1252 		delete[] msgbuffer;
1253 	}
1254 
1255 DBG(OUT("BLooper::ReadMessageFromPort() done: %p\n", bmsg));
1256 	return bmsg;
1257 }
1258 //------------------------------------------------------------------------------
1259 BMessage* BLooper::ConvertToMessage(void* raw, int32 code)
1260 {
1261 DBG(OUT("BLooper::ConvertToMessage()\n"));
1262 	BMessage* bmsg = new BMessage(code);
1263 
1264 	if (raw != NULL)
1265 	{
1266 		if (bmsg->Unflatten((const char*)raw) != B_OK)
1267 		{
1268 DBG(OUT("BLooper::ConvertToMessage(): unflattening message failed\n"));
1269 			delete bmsg;
1270 			bmsg = NULL;
1271 		}
1272 	}
1273 
1274 DBG(OUT("BLooper::ConvertToMessage(): %p\n", bmsg));
1275 	return bmsg;
1276 }
1277 //------------------------------------------------------------------------------
1278 void BLooper::task_looper()
1279 {
1280 DBG(OUT("BLooper::task_looper()\n"));
1281 	//	Check that looper is locked (should be)
1282 	AssertLocked();
1283 	//	Unlock the looper
1284 	Unlock();
1285 
1286 	//	loop: As long as we are not terminating.
1287 	while (!fTerminating)
1288 	{
1289 DBG(OUT("LOOPER: outer loop\n"));
1290 		// TODO: timeout determination algo
1291 		//	Read from message port (how do we determine what the timeout is?)
1292 DBG(OUT("LOOPER: MessageFromPort()...\n"));
1293 		BMessage* msg = MessageFromPort();
1294 DBG(OUT("LOOPER: ...done\n"));
1295 
1296 		//	Did we get a message?
1297 		if (msg)
1298 		{
1299 DBG(OUT("LOOPER: got message\n"));
1300 			//	Add to queue
1301 			fQueue->AddMessage(msg);
1302 		}
1303 else
1304 DBG(OUT("LOOPER: got no message\n"));
1305 
1306 		//	Get message count from port
1307 		int32 msgCount = port_count(fMsgPort);
1308 		for (int32 i = 0; i < msgCount; ++i)
1309 		{
1310 			//	Read 'count' messages from port (so we will not block)
1311 			//	We use zero as our timeout since we know there is stuff there
1312 			msg = MessageFromPort(0);
1313 			//	Add messages to queue
1314 			if (msg)
1315 			{
1316 				fQueue->AddMessage(msg);
1317 			}
1318 		}
1319 
1320 		//	loop: As long as there are messages in the queue and the port is
1321 		//		  empty... and we are not terminating, of course.
1322 		bool dispatchNextMessage = true;
1323 		while (!fTerminating && dispatchNextMessage)
1324 		{
1325 DBG(OUT("LOOPER: inner loop\n"));
1326 			//	Get next message from queue (assign to fLastMessage)
1327 			fLastMessage = fQueue->NextMessage();
1328 
1329 			//	Lock the looper
1330 			Lock();
1331 			if (!fLastMessage)
1332 			{
1333 				// No more messages: Unlock the looper and terminate the
1334 				// dispatch loop.
1335 				dispatchNextMessage = false;
1336 			}
1337 			else
1338 			{
1339 DBG(OUT("LOOPER: fLastMessage: 0x%lx: %.4s\n", fLastMessage->what,
1340 (char*)&fLastMessage->what));
1341 DBG(fLastMessage->PrintToStream());
1342 				//	Get the target handler
1343 				//	Use BMessage friend functions to determine if we are using the
1344 				//	preferred handler, or if a target has been specified
1345 				BHandler* handler;
1346 				if (_use_preferred_target_(fLastMessage))
1347 				{
1348 DBG(OUT("LOOPER: use preferred target\n"));
1349 					handler = fPreferred;
1350 				}
1351 				else
1352 				{
1353 DBG(OUT("LOOPER: don't use preferred target\n"));
1354 					/**
1355 						@note	Here is where all the token stuff starts to
1356 								make sense.  How, exactly, do we determine
1357 								what the target BHandler is?  If we look at
1358 								BMessage, we see an int32 field, fTarget.
1359 								Amazingly, we happen to have a global mapping
1360 								of BHandler pointers to int32s!
1361 					 */
1362 DBG(OUT("LOOPER: use: %ld\n", _get_message_target_(fLastMessage)));
1363 					 gDefaultTokens.GetToken(_get_message_target_(fLastMessage),
1364 					 						 B_HANDLER_TOKEN,
1365 					 						 (void**)&handler);
1366 DBG(OUT("LOOPER: handler: %p, this: %p\n", handler, this));
1367 				}
1368 
1369 				if (!handler)
1370 				{
1371 DBG(OUT("LOOPER: no target handler, use this\n"));
1372 					handler = this;
1373 				}
1374 
1375 				//	Is this a scripting message? (BMessage::HasSpecifiers())
1376 				if (fLastMessage->HasSpecifiers())
1377 				{
1378 					int32 index = 0;
1379 					// Make sure the current specifier is kosher
1380 					if (fLastMessage->GetCurrentSpecifier(&index) == B_OK)
1381 					{
1382 						handler = resolve_specifier(handler, fLastMessage);
1383 					}
1384 				}
1385 else
1386 DBG(OUT("LOOPER: no scripting message\n"));
1387 
1388 				if (handler)
1389 				{
1390 					//	Do filtering
1391 					handler = top_level_filter(fLastMessage, handler);
1392 DBG(OUT("LOOPER: top_level_filter(): %p\n", handler));
1393 					if (handler && handler->Looper() == this)
1394 					{
1395 						DispatchMessage(fLastMessage, handler);
1396 					}
1397 				}
1398 			}
1399 
1400 			//	Unlock the looper
1401 			Unlock();
1402 
1403 			//	Delete the current message (fLastMessage)
1404 			if (fLastMessage)
1405 			{
1406 				delete fLastMessage;
1407 				fLastMessage = NULL;
1408 			}
1409 
1410 			//	Are any messages on the port?
1411 			if (port_count(fMsgPort) > 0)
1412 			{
1413 				//	Do outer loop
1414 				dispatchNextMessage = false;
1415 			}
1416 		}
1417 	}
1418 DBG(OUT("BLooper::task_looper() done\n"));
1419 }
1420 //------------------------------------------------------------------------------
1421 void BLooper::do_quit_requested(BMessage* msg)
1422 {
1423 /**
1424 	@note	I couldn't figure out why do_quit_requested() was necessary; why not
1425 			just call Quit()?  Then, while writing the PostMessage() code, I
1426 			realized that the sender of the B_QUIT_REQUESTED message just might
1427 			be waiting for a reply.  A quick test, and yes, we get a reply
1428 			which consists of:
1429 				what: B_REPLY
1430 				"result" (bool) return of QuitRequested()
1431 				"thread" (int32) the looper's thread id
1432 
1433 			While Quit() could use fLastMessage, it makes more sense that
1434 			do_quit_requested() would handle it since it gets passed the
1435 			message.
1436  */
1437 
1438 	bool isQuitting = QuitRequested();
1439 
1440 	if (msg->IsSourceWaiting())
1441 	{
1442 		BMessage ReplyMsg(B_REPLY);
1443 		ReplyMsg.AddBool("result", isQuitting);
1444 		ReplyMsg.AddInt32("thread", fTaskID);
1445 		msg->SendReply(&ReplyMsg);
1446 	}
1447 
1448 	if (isQuitting)
1449 	{
1450 		Quit();
1451 	}
1452 }
1453 //------------------------------------------------------------------------------
1454 bool BLooper::AssertLocked() const
1455 {
1456 	if (!IsLocked())
1457 	{
1458 		debugger("looper must be locked before proceeding\n");
1459 		return false;
1460 	}
1461 
1462 	return true;
1463 }
1464 //------------------------------------------------------------------------------
1465 BHandler* BLooper::top_level_filter(BMessage* msg, BHandler* target)
1466 {
1467 	if (msg)
1468 	{
1469 		// Apply the common filters first
1470 		target = apply_filters(CommonFilterList(), msg, target);
1471 		if (target)
1472 		{
1473 			if (target->Looper() != this)
1474 			{
1475 				// TODO: debugger message?
1476 				target = NULL;
1477 			}
1478 			else
1479 			{
1480 				// Now apply handler-specific filters
1481 				target = handler_only_filter(msg, target);
1482 			}
1483 		}
1484 	}
1485 
1486 	return target;
1487 }
1488 //------------------------------------------------------------------------------
1489 BHandler* BLooper::handler_only_filter(BMessage* msg, BHandler* target)
1490 {
1491 	// Keep running filters until our handler is NULL, or until the filtering
1492 	// handler returns itself as the designated handler
1493 	BHandler* oldTarget = NULL;
1494 	while (target && (target != oldTarget))
1495 	{
1496 		oldTarget = target;
1497 		target = apply_filters(oldTarget->FilterList(), msg, oldTarget);
1498 		if (target && (target->Looper() != this))
1499 		{
1500 			// TODO: debugger message?
1501 			target = NULL;
1502 		}
1503 	}
1504 
1505 	return target;
1506 }
1507 //------------------------------------------------------------------------------
1508 BHandler* BLooper::apply_filters(BList* list, BMessage* msg, BHandler* target)
1509 {
1510 	// This is where the action is!
1511 	// Check the parameters
1512 	if (!list || !msg)
1513 	{
1514 		return target;
1515 	}
1516 
1517 	// For each filter in the provided list
1518 	BMessageFilter* filter = NULL;
1519 	for (int32 i = 0; i < list->CountItems(); ++i)
1520 	{
1521 		filter = (BMessageFilter*)list->ItemAt(i);
1522 
1523 		// Check command conditions
1524 		if (filter->FiltersAnyCommand() || (filter->Command() == msg->what))
1525 		{
1526 			// Check delivery conditions
1527 			message_delivery delivery = filter->MessageDelivery();
1528 			bool dropped = msg->WasDropped();
1529 			if (delivery == B_ANY_DELIVERY ||
1530 				((delivery == B_DROPPED_DELIVERY) && dropped) ||
1531 				((delivery == B_PROGRAMMED_DELIVERY) && !dropped))
1532 			{
1533 				// Check source conditions
1534 				message_source source = filter->MessageSource();
1535 				bool remote = msg->IsSourceRemote();
1536 				if (source == B_ANY_SOURCE ||
1537 					((source == B_REMOTE_SOURCE) && remote) ||
1538 					((source == B_LOCAL_SOURCE) && !remote))
1539 				{
1540 					filter_result result;
1541 					// Are we using an "external" function?
1542 					filter_hook func = filter->FilterFunction();
1543 					if (func)
1544 					{
1545 						result = func(msg, &target, filter);
1546 					}
1547 					else
1548 					{
1549 						result = filter->Filter(msg, &target);
1550 					}
1551 
1552 					// Is further processing allowed?
1553 					if (result == B_SKIP_MESSAGE)
1554 					{
1555 						// No; time to bail out
1556 						return NULL;
1557 					}
1558 				}
1559 			}
1560 		}
1561 	}
1562 
1563 	return target;
1564 }
1565 //------------------------------------------------------------------------------
1566 void BLooper::check_lock()
1567 {
1568 	// NOTE: any use for this?
1569 }
1570 //------------------------------------------------------------------------------
1571 BHandler* BLooper::resolve_specifier(BHandler* target, BMessage* msg)
1572 {
1573 	// TODO: implement
1574 	return NULL;
1575 }
1576 //------------------------------------------------------------------------------
1577 void BLooper::UnlockFully()
1578 {
1579 	AssertLocked();
1580 
1581 /**
1582 	@note	What we're doing here is completely undoing the current owner's lock
1583 			on the looper.  This is actually pretty easy, since the owner only
1584 			has a single aquisition on the semaphore; every subsequent "lock"
1585 			is just an increment to the owner count.  The whole thing is quite
1586 			similar to Unlock(), except that we clear the ownership variables,
1587 			rather than merely decrementing them.
1588  */
1589 	// Clear the owner count
1590 	fOwnerCount = 0;
1591 	// Nobody owns the lock now
1592 	fOwner = -1;
1593 	// There is now one less thread holding a lock on this looper
1594 	long atomicCount = atomic_add(&fAtomicCount, -1);
1595 	if (atomicCount > 0)
1596 	{
1597 		release_sem(fLockSem);
1598 	}
1599 }
1600 //------------------------------------------------------------------------------
1601 void BLooper::AddLooper(BLooper* loop)
1602 {
1603 	if (gLooperList.IsLocked())
1604 	{
1605 		gLooperList.AddLooper(loop);
1606 	}
1607 }
1608 //------------------------------------------------------------------------------
1609 bool BLooper::IsLooperValid(const BLooper* l)
1610 {
1611 	if (gLooperList.IsLocked())
1612 	{
1613 		return gLooperList.IsLooperValid(l);
1614 	}
1615 
1616 	return false;
1617 }
1618 //------------------------------------------------------------------------------
1619 void BLooper::RemoveLooper(BLooper* l)
1620 {
1621 	if (gLooperList.IsLocked())
1622 	{
1623 		gLooperList.RemoveLooper(l);
1624 	}
1625 }
1626 //------------------------------------------------------------------------------
1627 void BLooper::GetLooperList(BList* list)
1628 {
1629 	BObjectLocker<BLooperList> ListLock(gLooperList);
1630 	if (ListLock.IsLocked())
1631 	{
1632 		gLooperList.GetLooperList(list);
1633 	}
1634 }
1635 //------------------------------------------------------------------------------
1636 BLooper* BLooper::LooperForName(const char* name)
1637 {
1638 	if (gLooperList.IsLocked())
1639 	{
1640 		return gLooperList.LooperForName(name);
1641 	}
1642 
1643 	return NULL;
1644 }
1645 //------------------------------------------------------------------------------
1646 BLooper* BLooper::LooperForPort(port_id port)
1647 {
1648 	if (gLooperList.IsLocked())
1649 	{
1650 		return gLooperList.LooperForPort(port);
1651 	}
1652 
1653 	return NULL;
1654 }
1655 //------------------------------------------------------------------------------
1656 
1657 
1658 //------------------------------------------------------------------------------
1659 port_id _get_looper_port_(const BLooper* looper)
1660 {
1661 	return looper->fMsgPort;
1662 }
1663 //------------------------------------------------------------------------------
1664 
1665 /*
1666  * $Log $
1667  *
1668  * $Id  $
1669  *
1670  */
1671 
1672