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