xref: /haiku/src/kits/app/Looper.cpp (revision 746cac055adc6ac3308c7bc2d29040fb95689cc9)
1 /*
2  * Copyright 2001-2008, Haiku.
3  * Distributed under the terms of the MIT License.
4  *
5  * Authors:
6  *		Erik Jaesler (erik@cgsoftware.com)
7  *		DarkWyrm (bpmagic@columbus.rr.com)
8  *		Ingo Weinhold, bonefish@@users.sf.net
9  *		Axel Dörfler, axeld@pinc-software.de
10  */
11 
12 /*!	BLooper class spawns a thread that runs a message loop. */
13 
14 #include <AppMisc.h>
15 #include <AutoLocker.h>
16 #include <DirectMessageTarget.h>
17 #include <LooperList.h>
18 #include <MessagePrivate.h>
19 #include <TokenSpace.h>
20 
21 #include <Autolock.h>
22 #include <Looper.h>
23 #include <Message.h>
24 #include <MessageFilter.h>
25 #include <MessageQueue.h>
26 #include <Messenger.h>
27 #include <PropertyInfo.h>
28 
29 #include <new>
30 #include <stdio.h>
31 
32 
33 // debugging
34 //#define DBG(x) x
35 #define DBG(x)	;
36 #define PRINT(x)	DBG({ printf("[%6ld] ", find_thread(NULL)); printf x; })
37 
38 /*
39 #include <Autolock.h>
40 #include <Locker.h>
41 static BLocker sDebugPrintLocker("BLooper debug print");
42 #define PRINT(x)	DBG({						\
43 	BAutolock _(sDebugPrintLocker);				\
44 	debug_printf("[%6ld] ", find_thread(NULL));	\
45 	debug_printf x;								\
46 })
47 */
48 
49 
50 #define FILTER_LIST_BLOCK_SIZE	5
51 #define DATA_BLOCK_SIZE			5
52 
53 // Globals ---------------------------------------------------------------------
54 using BPrivate::gDefaultTokens;
55 using BPrivate::gLooperList;
56 using BPrivate::BLooperList;
57 
58 port_id _get_looper_port_(const BLooper* looper);
59 
60 enum {
61 	BLOOPER_PROCESS_INTERNALLY = 0,
62 	BLOOPER_HANDLER_BY_INDEX
63 };
64 
65 static property_info gLooperPropInfo[] = {
66 	{
67 		"Handler",
68 			{},
69 			{B_INDEX_SPECIFIER, B_REVERSE_INDEX_SPECIFIER},
70 			NULL, BLOOPER_HANDLER_BY_INDEX,
71 			{},
72 			{},
73 			{}
74 	},
75 	{
76 		"Handlers",
77 			{B_GET_PROPERTY},
78 			{B_DIRECT_SPECIFIER},
79 			NULL, BLOOPER_PROCESS_INTERNALLY,
80 			{B_MESSENGER_TYPE},
81 			{},
82 			{}
83 	},
84 	{
85 		"Handler",
86 			{B_COUNT_PROPERTIES},
87 			{B_DIRECT_SPECIFIER},
88 			NULL, BLOOPER_PROCESS_INTERNALLY,
89 			{B_INT32_TYPE},
90 			{},
91 			{}
92 	},
93 	{}
94 };
95 
96 struct _loop_data_ {
97 	BLooper*	looper;
98 	thread_id	thread;
99 };
100 
101 
102 //	#pragma mark -
103 
104 
105 BLooper::BLooper(const char* name, int32 priority, int32 portCapacity)
106 	: BHandler(name)
107 {
108 	_InitData(name, priority, portCapacity);
109 }
110 
111 
112 BLooper::~BLooper()
113 {
114 	if (fRunCalled && !fTerminating) {
115 		debugger("You can't call delete on a BLooper object "
116 			"once it is running.");
117 	}
118 
119 	Lock();
120 
121 	// In case the looper thread calls Quit() fLastMessage is not deleted.
122 	if (fLastMessage) {
123 		delete fLastMessage;
124 		fLastMessage = NULL;
125 	}
126 
127 	// Close the message port and read and reply to the remaining messages.
128 	if (fMsgPort >= 0)
129 		close_port(fMsgPort);
130 
131 	// Clear the queue so our call to IsMessageWaiting() below doesn't give
132 	// us bogus info
133 	fDirectTarget->Close();
134 
135 	BMessage* message;
136 	while ((message = fDirectTarget->Queue()->NextMessage()) != NULL) {
137 		delete message;
138 			// msg will automagically post generic reply
139 	}
140 
141 	do {
142 		delete ReadMessageFromPort(0);
143 			// msg will automagically post generic reply
144 	} while (IsMessageWaiting());
145 
146 	fDirectTarget->Release();
147 	delete_port(fMsgPort);
148 
149 	// Clean up our filters
150 	SetCommonFilterList(NULL);
151 
152 	AutoLocker<BLooperList> ListLock(gLooperList);
153 	RemoveHandler(this);
154 
155 	// Remove all the "child" handlers
156 	int32 count = fHandlers.CountItems();
157 	for (int32 i = 0; i < count; i++) {
158 		BHandler* handler = (BHandler*)fHandlers.ItemAtFast(i);
159 		handler->SetNextHandler(NULL);
160 		handler->SetLooper(NULL);
161 	}
162 	fHandlers.MakeEmpty();
163 
164 	Unlock();
165 	gLooperList.RemoveLooper(this);
166 	delete_sem(fLockSem);
167 }
168 
169 
170 BLooper::BLooper(BMessage* data)
171 	: BHandler(data)
172 {
173 	int32 portCapacity;
174 	if (data->FindInt32("_port_cap", &portCapacity) != B_OK || portCapacity < 0)
175 		portCapacity = B_LOOPER_PORT_DEFAULT_CAPACITY;
176 
177 	int32 priority;
178 	if (data->FindInt32("_prio", &priority) != B_OK)
179 		priority = B_NORMAL_PRIORITY;
180 
181 	_InitData(Name(), priority, portCapacity);
182 }
183 
184 
185 BArchivable*
186 BLooper::Instantiate(BMessage* data)
187 {
188 	if (validate_instantiation(data, "BLooper"))
189 		return new BLooper(data);
190 
191 	return NULL;
192 }
193 
194 
195 status_t
196 BLooper::Archive(BMessage* data, bool deep) const
197 {
198 	status_t status = BHandler::Archive(data, deep);
199 	if (status < B_OK)
200 		return status;
201 
202 	port_info info;
203 	status = get_port_info(fMsgPort, &info);
204 	if (status == B_OK)
205 		status = data->AddInt32("_port_cap", info.capacity);
206 
207 	thread_info threadInfo;
208 	if (get_thread_info(Thread(), &threadInfo) == B_OK)
209 		status = data->AddInt32("_prio", threadInfo.priority);
210 
211 	return status;
212 }
213 
214 
215 status_t
216 BLooper::PostMessage(uint32 command)
217 {
218 	BMessage message(command);
219 	return _PostMessage(&message, this, NULL);
220 }
221 
222 
223 status_t
224 BLooper::PostMessage(BMessage* message)
225 {
226 	return _PostMessage(message, this, NULL);
227 }
228 
229 
230 status_t
231 BLooper::PostMessage(uint32 command, BHandler* handler, BHandler* replyTo)
232 {
233 	BMessage message(command);
234 	return _PostMessage(&message, handler, replyTo);
235 }
236 
237 
238 status_t
239 BLooper::PostMessage(BMessage* message, BHandler* handler, BHandler* replyTo)
240 {
241 	return _PostMessage(message, handler, replyTo);
242 }
243 
244 
245 void
246 BLooper::DispatchMessage(BMessage* message, BHandler* handler)
247 {
248 	PRINT(("BLooper::DispatchMessage(%.4s)\n", (char*)&message->what));
249 
250 	switch (message->what) {
251 		case _QUIT_:
252 			// Can't call Quit() to do this, because of the slight chance
253 			// another thread with have us locked between now and then.
254 			fTerminating = true;
255 
256 			// After returning from DispatchMessage(), the looper will be
257 			// deleted in _task0_()
258 			break;
259 
260 		case B_QUIT_REQUESTED:
261 			if (handler == this) {
262 				_QuitRequested(message);
263 				break;
264 			}
265 
266 			// fall through
267 
268 		default:
269 			handler->MessageReceived(message);
270 			break;
271 	}
272 	PRINT(("BLooper::DispatchMessage() done\n"));
273 }
274 
275 
276 void
277 BLooper::MessageReceived(BMessage* message)
278 {
279 	// TODO: implement scripting support
280 	BHandler::MessageReceived(message);
281 }
282 
283 
284 BMessage*
285 BLooper::CurrentMessage() const
286 {
287 	return fLastMessage;
288 }
289 
290 
291 BMessage*
292 BLooper::DetachCurrentMessage()
293 {
294 	BMessage* message = fLastMessage;
295 	fLastMessage = NULL;
296 	return message;
297 }
298 
299 
300 BMessageQueue*
301 BLooper::MessageQueue() const
302 {
303 	return fDirectTarget->Queue();
304 }
305 
306 
307 bool
308 BLooper::IsMessageWaiting() const
309 {
310 	AssertLocked();
311 
312 	if (!fDirectTarget->Queue()->IsEmpty())
313 		return true;
314 
315 	int32 count;
316 	do {
317 		count = port_buffer_size_etc(fMsgPort, B_RELATIVE_TIMEOUT, 0);
318 	} while (count == B_INTERRUPTED);
319 
320 	return count > 0;
321 }
322 
323 
324 void
325 BLooper::AddHandler(BHandler* handler)
326 {
327 	if (handler == NULL)
328 		return;
329 
330 	AssertLocked();
331 
332 	if (handler->Looper() == NULL) {
333 		fHandlers.AddItem(handler);
334 		handler->SetLooper(this);
335 		if (handler != this)	// avoid a cycle
336 			handler->SetNextHandler(this);
337 	}
338 }
339 
340 
341 bool
342 BLooper::RemoveHandler(BHandler* handler)
343 {
344 	if (handler == NULL)
345 		return false;
346 
347 	AssertLocked();
348 
349 	if (handler->Looper() == this && fHandlers.RemoveItem(handler)) {
350 		if (handler == fPreferred)
351 			fPreferred = NULL;
352 
353 		handler->SetNextHandler(NULL);
354 		handler->SetLooper(NULL);
355 		return true;
356 	}
357 
358 	return false;
359 }
360 
361 
362 int32
363 BLooper::CountHandlers() const
364 {
365 	AssertLocked();
366 
367 	return fHandlers.CountItems();
368 }
369 
370 
371 BHandler*
372 BLooper::HandlerAt(int32 index) const
373 {
374 	AssertLocked();
375 
376 	return (BHandler*)fHandlers.ItemAt(index);
377 }
378 
379 
380 int32
381 BLooper::IndexOf(BHandler* handler) const
382 {
383 	AssertLocked();
384 
385 	return fHandlers.IndexOf(handler);
386 }
387 
388 
389 BHandler*
390 BLooper::PreferredHandler() const
391 {
392 	return fPreferred;
393 }
394 
395 
396 void
397 BLooper::SetPreferredHandler(BHandler* handler)
398 {
399 	if (handler && handler->Looper() == this && IndexOf(handler) >= 0) {
400 		fPreferred = handler;
401 	} else {
402 		fPreferred = NULL;
403 	}
404 }
405 
406 
407 thread_id
408 BLooper::Run()
409 {
410 	AssertLocked();
411 
412 	if (fRunCalled) {
413 		// Not allowed to call Run() more than once
414 		debugger("can't call BLooper::Run twice!");
415 		return fThread;
416 	}
417 
418 	fThread = spawn_thread(_task0_, Name(), fInitPriority, this);
419 	if (fThread < B_OK)
420 		return fThread;
421 
422 	if (fMsgPort < B_OK)
423 		return fMsgPort;
424 
425 	fRunCalled = true;
426 	Unlock();
427 
428 	status_t err = resume_thread(fThread);
429 	if (err < B_OK)
430 		return err;
431 
432 	return fThread;
433 }
434 
435 
436 void
437 BLooper::Quit()
438 {
439 	PRINT(("BLooper::Quit()\n"));
440 
441 	if (!IsLocked()) {
442 		printf("ERROR - you must Lock a looper before calling Quit(), "
443 			"team=%ld, looper=%s\n", Team(), Name() ? Name() : "unnamed");
444 	}
445 
446 	// Try to lock
447 	if (!Lock()) {
448 		// We're toast already
449 		return;
450 	}
451 
452 	PRINT(("  is locked\n"));
453 
454 	if (!fRunCalled) {
455 		PRINT(("  Run() has not been called yet\n"));
456 		fTerminating = true;
457 		delete this;
458 	} else if (find_thread(NULL) == fThread) {
459 		PRINT(("  We are the looper thread\n"));
460 		fTerminating = true;
461 		delete this;
462 		exit_thread(0);
463 	} else {
464 		PRINT(("  Run() has already been called and we are not the looper thread\n"));
465 
466 		// As with sem in _Lock(), we need to cache this here in case the looper
467 		// disappears before we get to the wait_for_thread() below
468 		thread_id thread = Thread();
469 
470 		// We need to unlock here. Otherwise the looper thread can't
471 		// dispatch the _QUIT_ message we're going to post.
472 		UnlockFully();
473 
474 		// As per the BeBook, if we've been called by a thread other than
475 		// our own, the rest of the message queue has to get processed.  So
476 		// we put this in the queue, and when it shows up, we'll call Quit()
477 		// from our own thread.
478 		// QuitRequested() will not be called in this case.
479 		PostMessage(_QUIT_);
480 
481 		// We have to wait until the looper is done processing any remaining
482 		// messages.
483 		status_t status;
484 		while (wait_for_thread(thread, &status) == B_INTERRUPTED)
485 			;
486 	}
487 
488 	PRINT(("BLooper::Quit() done\n"));
489 }
490 
491 
492 bool
493 BLooper::QuitRequested()
494 {
495 	return true;
496 }
497 
498 
499 bool
500 BLooper::Lock()
501 {
502 	// Defer to global _Lock(); see notes there
503 	return _Lock(this, -1, B_INFINITE_TIMEOUT) == B_OK;
504 }
505 
506 
507 void
508 BLooper::Unlock()
509 {
510 PRINT(("BLooper::Unlock()\n"));
511 	//	Make sure we're locked to begin with
512 	AssertLocked();
513 
514 	//	Decrement fOwnerCount
515 	--fOwnerCount;
516 PRINT(("  fOwnerCount now: %ld\n", fOwnerCount));
517 	//	Check to see if the owner still wants a lock
518 	if (fOwnerCount == 0) {
519 		//	Set fOwner to invalid thread_id (< 0)
520 		fOwner = -1;
521 		fCachedStack = 0;
522 
523 #if DEBUG < 1
524 		//	Decrement requested lock count (using fAtomicCount for this)
525 		int32 atomicCount = atomic_add(&fAtomicCount, -1);
526 PRINT(("  fAtomicCount now: %ld\n", fAtomicCount));
527 
528 		// Check if anyone is waiting for a lock
529 		// and release if it's the case
530 		if (atomicCount > 1)
531 #endif
532 			release_sem(fLockSem);
533 	}
534 PRINT(("BLooper::Unlock() done\n"));
535 }
536 
537 
538 bool
539 BLooper::IsLocked() const
540 {
541 	if (!gLooperList.IsLooperValid(this)) {
542 		// The looper is gone, so of course it's not locked
543 		return false;
544 	}
545 
546 	uint32 stack;
547 	return ((uint32)&stack & ~(B_PAGE_SIZE - 1)) == fCachedStack
548 		|| find_thread(NULL) == fOwner;
549 }
550 
551 
552 status_t
553 BLooper::LockWithTimeout(bigtime_t timeout)
554 {
555 	return _Lock(this, -1, timeout);
556 }
557 
558 
559 thread_id
560 BLooper::Thread() const
561 {
562 	return fThread;
563 }
564 
565 
566 team_id
567 BLooper::Team() const
568 {
569 	return BPrivate::current_team();
570 }
571 
572 
573 BLooper*
574 BLooper::LooperForThread(thread_id thread)
575 {
576 	return gLooperList.LooperForThread(thread);
577 }
578 
579 
580 thread_id
581 BLooper::LockingThread() const
582 {
583 	return fOwner;
584 }
585 
586 
587 int32
588 BLooper::CountLocks() const
589 {
590 	return fOwnerCount;
591 }
592 
593 
594 int32
595 BLooper::CountLockRequests() const
596 {
597 	return fAtomicCount;
598 }
599 
600 
601 sem_id
602 BLooper::Sem() const
603 {
604 	return fLockSem;
605 }
606 
607 
608 BHandler*
609 BLooper::ResolveSpecifier(BMessage* msg, int32 index, BMessage* specifier,
610 	int32 form, const char* property)
611 {
612 /**
613 	@note	When I was first dumping the results of GetSupportedSuites() from
614 			various classes, the use of the extra_data field was quite
615 			mysterious to me.  Then I dumped BApplication and compared the
616 			result against the BeBook's docs for scripting BApplication.  A
617 			bunch of it isn't documented, but what is tipped me to the idea
618 			that the extra_data is being used as a quick and dirty way to tell
619 			what scripting "command" has been sent, e.g., for easy use in a
620 			switch statement.  Would certainly be a lot faster than a bunch of
621 			string comparisons -- which wouldn't tell the whole story anyway,
622 			because of the same name being used for multiple properties.
623  */
624  	BPropertyInfo propertyInfo(gLooperPropInfo);
625 	uint32 data;
626 	status_t err = B_OK;
627 	const char* errMsg = "";
628 	if (propertyInfo.FindMatch(msg, index, specifier, form, property, &data)
629 			>= 0) {
630 		switch (data) {
631 			case BLOOPER_PROCESS_INTERNALLY:
632 				return this;
633 
634 			case BLOOPER_HANDLER_BY_INDEX:
635 			{
636 				int32 index = specifier->FindInt32("index");
637 				if (form == B_REVERSE_INDEX_SPECIFIER) {
638 					index = CountHandlers() - index;
639 				}
640 				BHandler* target = HandlerAt(index);
641 				if (target) {
642 					// Specifier has been fully handled
643 					msg->PopSpecifier();
644 					return target;
645 				} else {
646 					err = B_BAD_INDEX;
647 					errMsg = "handler index out of range";
648 				}
649 				break;
650 			}
651 
652 			default:
653 				err = B_BAD_SCRIPT_SYNTAX;
654 				errMsg = "Didn't understand the specifier(s)";
655 				break;
656 		}
657 	} else {
658 		return BHandler::ResolveSpecifier(msg, index, specifier, form,
659 			property);
660 	}
661 
662 	BMessage reply(B_MESSAGE_NOT_UNDERSTOOD);
663 	reply.AddInt32("error", err);
664 	reply.AddString("message", errMsg);
665 	msg->SendReply(&reply);
666 
667 	return NULL;
668 }
669 
670 
671 status_t
672 BLooper::GetSupportedSuites(BMessage* data)
673 {
674 	if (data == NULL)
675 		return B_BAD_VALUE;
676 
677 	status_t status = data->AddString("suites", "suite/vnd.Be-looper");
678 	if (status == B_OK) {
679 		BPropertyInfo PropertyInfo(gLooperPropInfo);
680 		status = data->AddFlat("messages", &PropertyInfo);
681 		if (status == B_OK)
682 			status = BHandler::GetSupportedSuites(data);
683 	}
684 
685 	return status;
686 }
687 
688 
689 void
690 BLooper::AddCommonFilter(BMessageFilter* filter)
691 {
692 	if (!filter)
693 		return;
694 
695 	AssertLocked();
696 
697 	if (filter->Looper()) {
698 		debugger("A MessageFilter can only be used once.");
699 		return;
700 	}
701 
702 	if (!fCommonFilters)
703 		fCommonFilters = new BList(FILTER_LIST_BLOCK_SIZE);
704 
705 	filter->SetLooper(this);
706 	fCommonFilters->AddItem(filter);
707 }
708 
709 
710 bool
711 BLooper::RemoveCommonFilter(BMessageFilter* filter)
712 {
713 	AssertLocked();
714 
715 	if (!fCommonFilters)
716 		return false;
717 
718 	bool result = fCommonFilters->RemoveItem(filter);
719 	if (result)
720 		filter->SetLooper(NULL);
721 
722 	return result;
723 }
724 
725 
726 void
727 BLooper::SetCommonFilterList(BList* filters)
728 {
729 	AssertLocked();
730 
731 	BMessageFilter* filter;
732 	if (filters) {
733 		// Check for ownership issues - a filter can only have one owner
734 		for (int32 i = 0; i < filters->CountItems(); ++i) {
735 			filter = (BMessageFilter*)filters->ItemAt(i);
736 			if (filter->Looper()) {
737 				debugger("A MessageFilter can only be used once.");
738 				return;
739 			}
740 		}
741 	}
742 
743 	if (fCommonFilters) {
744 		for (int32 i = 0; i < fCommonFilters->CountItems(); ++i) {
745 			delete (BMessageFilter*)fCommonFilters->ItemAt(i);
746 		}
747 
748 		delete fCommonFilters;
749 		fCommonFilters = NULL;
750 	}
751 
752 	// Per the BeBook, we take ownership of the list
753 	fCommonFilters = filters;
754 	if (fCommonFilters) {
755 		for (int32 i = 0; i < fCommonFilters->CountItems(); ++i) {
756 			filter = (BMessageFilter*)fCommonFilters->ItemAt(i);
757 			filter->SetLooper(this);
758 		}
759 	}
760 }
761 
762 
763 BList*
764 BLooper::CommonFilterList() const
765 {
766 	return fCommonFilters;
767 }
768 
769 
770 status_t
771 BLooper::Perform(perform_code d, void* arg)
772 {
773 	// This is sort of what we're doing for this function everywhere
774 	return BHandler::Perform(d, arg);
775 }
776 
777 
778 BMessage*
779 BLooper::MessageFromPort(bigtime_t timeout)
780 {
781 	return ReadMessageFromPort(timeout);
782 }
783 
784 
785 void BLooper::_ReservedLooper1() {}
786 void BLooper::_ReservedLooper2() {}
787 void BLooper::_ReservedLooper3() {}
788 void BLooper::_ReservedLooper4() {}
789 void BLooper::_ReservedLooper5() {}
790 void BLooper::_ReservedLooper6() {}
791 
792 
793 BLooper::BLooper(const BLooper& other)
794 {
795 	// Copy construction not allowed
796 }
797 
798 
799 BLooper&
800 BLooper::operator=(const BLooper& other)
801 {
802 	// Looper copying not allowed
803 	return *this;
804 }
805 
806 
807 BLooper::BLooper(int32 priority, port_id port, const char* name)
808 {
809 	// This must be a legacy constructor
810 	fMsgPort = port;
811 	_InitData(name, priority, B_LOOPER_PORT_DEFAULT_CAPACITY);
812 }
813 
814 
815 status_t
816 BLooper::_PostMessage(BMessage *msg, BHandler *handler, BHandler *replyTo)
817 {
818 	AutoLocker<BLooperList> listLocker(gLooperList);
819 	if (!listLocker.IsLocked())
820 		return B_ERROR;
821 
822 	if (!gLooperList.IsLooperValid(this))
823 		return B_BAD_VALUE;
824 
825 	// Does handler belong to this looper?
826 	if (handler && handler->Looper() != this)
827 		return B_MISMATCHED_VALUES;
828 
829 	status_t status;
830 	BMessenger messenger(handler, this, &status);
831 	listLocker.Unlock();
832 	if (status == B_OK)
833 		status = messenger.SendMessage(msg, replyTo, 0);
834 
835 	return status;
836 }
837 
838 
839 /*!
840 	Locks a looper either by port or using a direct pointer to the looper.
841 
842 	\param looper looper to lock, if not NULL
843 	\param port port to identify the looper in case \a looper is NULL
844 	\param timeout timeout for acquiring the lock
845 */
846 status_t
847 BLooper::_Lock(BLooper* looper, port_id port, bigtime_t timeout)
848 {
849 	PRINT(("BLooper::_Lock(%p, %lx)\n", looper, port));
850 
851 	//	Check params (loop, port)
852 	if (looper == NULL && port < 0) {
853 		PRINT(("BLooper::_Lock() done 1\n"));
854 		return B_BAD_VALUE;
855 	}
856 
857 	thread_id currentThread = find_thread(NULL);
858 	int32 oldCount;
859 	sem_id sem;
860 
861 	{
862 		AutoLocker<BLooperList> ListLock(gLooperList);
863 		if (!ListLock.IsLocked())
864 			return B_BAD_VALUE;
865 
866 		// Look up looper by port_id, if necessary
867 		if (looper == NULL) {
868 			looper = gLooperList.LooperForPort(port);
869 			if (looper == NULL) {
870 				PRINT(("BLooper::_Lock() done 3\n"));
871 				return B_BAD_VALUE;
872 			}
873 		} else if (!gLooperList.IsLooperValid(looper)) {
874 			//	Check looper validity
875 			PRINT(("BLooper::_Lock() done 4\n"));
876 			return B_BAD_VALUE;
877 		}
878 
879 		// Check for nested lock attempt
880 		if (currentThread == looper->fOwner) {
881 			++looper->fOwnerCount;
882 			PRINT(("BLooper::_Lock() done 5: fOwnerCount: %ld\n", loop->fOwnerCount));
883 			return B_OK;
884 		}
885 
886 		// Cache the semaphore, so that we can safely access it after having
887 		// unlocked the looper list
888 		sem = looper->fLockSem;
889 		if (sem < 0) {
890 			PRINT(("BLooper::_Lock() done 6\n"));
891 			return B_BAD_VALUE;
892 		}
893 
894 		// Bump the requested lock count (using fAtomicCount for this)
895 		oldCount = atomic_add(&looper->fAtomicCount, 1);
896 	}
897 
898 	return _LockComplete(looper, oldCount, currentThread, sem, timeout);
899 }
900 
901 
902 status_t
903 BLooper::_LockComplete(BLooper *looper, int32 oldCount, thread_id thread,
904 	sem_id sem, bigtime_t timeout)
905 {
906 	status_t err = B_OK;
907 
908 #if DEBUG < 1
909 	if (oldCount > 0) {
910 #endif
911 		do {
912 			err = acquire_sem_etc(sem, 1, B_RELATIVE_TIMEOUT, timeout);
913 		} while (err == B_INTERRUPTED);
914 #if DEBUG < 1
915 	}
916 #endif
917 	if (err == B_OK) {
918 		looper->fOwner = thread;
919 		looper->fCachedStack = (addr_t)&err & ~(B_PAGE_SIZE - 1);
920 		looper->fOwnerCount = 1;
921 	}
922 
923 	PRINT(("BLooper::_LockComplete() done: %lx\n", err));
924 	return err;
925 }
926 
927 
928 void
929 BLooper::_InitData(const char *name, int32 priority, int32 portCapacity)
930 {
931 	fOwner = B_ERROR;
932 	fCachedStack = 0;
933 	fRunCalled = false;
934 	fDirectTarget = new (std::nothrow) BPrivate::BDirectMessageTarget();
935 	fCommonFilters = NULL;
936 	fLastMessage = NULL;
937 	fPreferred = NULL;
938 	fThread = B_ERROR;
939 	fTerminating = false;
940 	fMsgPort = -1;
941 	fAtomicCount = 0;
942 
943 	if (name == NULL)
944 		name = "anonymous looper";
945 
946 #if DEBUG
947 	fLockSem = create_sem(1, name);
948 #else
949 	fLockSem = create_sem(0, name);
950 #endif
951 
952 	if (portCapacity <= 0)
953 		portCapacity = B_LOOPER_PORT_DEFAULT_CAPACITY;
954 
955 	fMsgPort = create_port(portCapacity, name);
956 
957 	fInitPriority = priority;
958 
959 	gLooperList.AddLooper(this);
960 		// this will also lock this looper
961 
962 	AddHandler(this);
963 }
964 
965 
966 void
967 BLooper::AddMessage(BMessage* message)
968 {
969 	_AddMessagePriv(message);
970 
971 	// wakeup looper when being called from other threads if necessary
972 	if (find_thread(NULL) != Thread()
973 		&& fDirectTarget->Queue()->IsNextMessage(message)
974 		&& port_count(fMsgPort) <= 0) {
975 		// there is currently no message waiting, and we need to wakeup the
976 		// looper
977 		write_port_etc(fMsgPort, 0, NULL, 0, B_RELATIVE_TIMEOUT, 0);
978 	}
979 }
980 
981 
982 void
983 BLooper::_AddMessagePriv(BMessage* message)
984 {
985 	// ToDo: if no target token is specified, set to preferred handler
986 	// Others may want to peek into our message queue, so the preferred
987 	// handler must be set correctly already if no token was given
988 
989 	fDirectTarget->Queue()->AddMessage(message);
990 }
991 
992 
993 status_t
994 BLooper::_task0_(void* arg)
995 {
996 	BLooper* looper = (BLooper *)arg;
997 
998 	PRINT(("LOOPER: _task0_()\n"));
999 
1000 	if (looper->Lock()) {
1001 		PRINT(("LOOPER: looper locked\n"));
1002 		looper->task_looper();
1003 
1004 		delete looper;
1005 	}
1006 
1007 	PRINT(("LOOPER: _task0_() done: thread %ld\n", find_thread(NULL)));
1008 	return B_OK;
1009 }
1010 
1011 
1012 void *
1013 BLooper::ReadRawFromPort(int32* msgCode, bigtime_t timeout)
1014 {
1015 	PRINT(("BLooper::ReadRawFromPort()\n"));
1016 	uint8 *buffer = NULL;
1017 	ssize_t bufferSize;
1018 
1019 	do {
1020 		bufferSize = port_buffer_size_etc(fMsgPort, B_RELATIVE_TIMEOUT, timeout);
1021 	} while (bufferSize == B_INTERRUPTED);
1022 
1023 	if (bufferSize < B_OK) {
1024 		PRINT(("BLooper::ReadRawFromPort(): failed: %ld\n", bufferSize));
1025 		return NULL;
1026 	}
1027 
1028 	if (bufferSize > 0)
1029 		buffer = (uint8 *)malloc(bufferSize);
1030 
1031 	// we don't want to wait again here, since that can only mean
1032 	// that someone else has read our message and our bufferSize
1033 	// is now probably wrong
1034 	PRINT(("read_port()...\n"));
1035 	bufferSize = read_port_etc(fMsgPort, msgCode, buffer, bufferSize,
1036 		B_RELATIVE_TIMEOUT, 0);
1037 
1038 	if (bufferSize < B_OK) {
1039 		free(buffer);
1040 		return NULL;
1041 	}
1042 
1043 	PRINT(("BLooper::ReadRawFromPort() read: %.4s, %p (%d bytes)\n", (char *)msgCode, buffer, bufferSize));
1044 	return buffer;
1045 }
1046 
1047 
1048 BMessage*
1049 BLooper::ReadMessageFromPort(bigtime_t timeout)
1050 {
1051 	PRINT(("BLooper::ReadMessageFromPort()\n"));
1052 	int32 msgCode;
1053 	BMessage *message = NULL;
1054 
1055 	void *buffer = ReadRawFromPort(&msgCode, timeout);
1056 	if (!buffer)
1057 		return NULL;
1058 
1059 	message = ConvertToMessage(buffer, msgCode);
1060 	free(buffer);
1061 
1062 	PRINT(("BLooper::ReadMessageFromPort() done: %p\n", message));
1063 	return message;
1064 }
1065 
1066 
1067 BMessage*
1068 BLooper::ConvertToMessage(void* buffer, int32 code)
1069 {
1070 	PRINT(("BLooper::ConvertToMessage()\n"));
1071 	if (!buffer)
1072 		return NULL;
1073 
1074 	BMessage* message = new BMessage();
1075 	if (message->Unflatten((const char*)buffer) != B_OK) {
1076 		PRINT(("BLooper::ConvertToMessage(): unflattening message failed\n"));
1077 		delete message;
1078 		message = NULL;
1079 	}
1080 
1081 	PRINT(("BLooper::ConvertToMessage(): %p\n", message));
1082 	return message;
1083 }
1084 
1085 
1086 void
1087 BLooper::task_looper()
1088 {
1089 	PRINT(("BLooper::task_looper()\n"));
1090 	// Check that looper is locked (should be)
1091 	AssertLocked();
1092 	// Unlock the looper
1093 	Unlock();
1094 
1095 	if (IsLocked())
1096 		debugger("looper must not be locked!");
1097 
1098 	// loop: As long as we are not terminating.
1099 	while (!fTerminating) {
1100 		PRINT(("LOOPER: outer loop\n"));
1101 		// TODO: timeout determination algo
1102 		//	Read from message port (how do we determine what the timeout is?)
1103 		PRINT(("LOOPER: MessageFromPort()...\n"));
1104 		BMessage *msg = MessageFromPort();
1105 		PRINT(("LOOPER: ...done\n"));
1106 
1107 		//	Did we get a message?
1108 		if (msg)
1109 			_AddMessagePriv(msg);
1110 
1111 		// Get message count from port
1112 		int32 msgCount = port_count(fMsgPort);
1113 		for (int32 i = 0; i < msgCount; ++i) {
1114 			// Read 'count' messages from port (so we will not block)
1115 			// We use zero as our timeout since we know there is stuff there
1116 			msg = MessageFromPort(0);
1117 			if (msg)
1118 				_AddMessagePriv(msg);
1119 		}
1120 
1121 		// loop: As long as there are messages in the queue and the port is
1122 		//		 empty... and we are not terminating, of course.
1123 		bool dispatchNextMessage = true;
1124 		while (!fTerminating && dispatchNextMessage) {
1125 			PRINT(("LOOPER: inner loop\n"));
1126 			// Get next message from queue (assign to fLastMessage)
1127 			fLastMessage = fDirectTarget->Queue()->NextMessage();
1128 
1129 			Lock();
1130 
1131 			if (!fLastMessage) {
1132 				// No more messages: Unlock the looper and terminate the
1133 				// dispatch loop.
1134 				dispatchNextMessage = false;
1135 			} else {
1136 				PRINT(("LOOPER: fLastMessage: 0x%lx: %.4s\n", fLastMessage->what,
1137 					(char*)&fLastMessage->what));
1138 				DBG(fLastMessage->PrintToStream());
1139 
1140 				// Get the target handler
1141 				BHandler *handler = NULL;
1142 				BMessage::Private messagePrivate(fLastMessage);
1143 				bool usePreferred = messagePrivate.UsePreferredTarget();
1144 
1145 				if (usePreferred) {
1146 					PRINT(("LOOPER: use preferred target\n"));
1147 					handler = fPreferred;
1148 					if (handler == NULL)
1149 						handler = this;
1150 				} else {
1151 					gDefaultTokens.GetToken(messagePrivate.GetTarget(),
1152 						B_HANDLER_TOKEN, (void **)&handler);
1153 
1154 					// if this handler doesn't belong to us, we drop the message
1155 					if (handler != NULL && handler->Looper() != this)
1156 						handler = NULL;
1157 
1158 					PRINT(("LOOPER: use %ld, handler: %p, this: %p\n",
1159 						messagePrivate.GetTarget(), handler, this));
1160 				}
1161 
1162 				// Is this a scripting message? (BMessage::HasSpecifiers())
1163 				if (handler != NULL && fLastMessage->HasSpecifiers()) {
1164 					int32 index = 0;
1165 					// Make sure the current specifier is kosher
1166 					if (fLastMessage->GetCurrentSpecifier(&index) == B_OK)
1167 						handler = resolve_specifier(handler, fLastMessage);
1168 				}
1169 
1170 				if (handler) {
1171 					// Do filtering
1172 					handler = _TopLevelFilter(fLastMessage, handler);
1173 					PRINT(("LOOPER: _TopLevelFilter(): %p\n", handler));
1174 					if (handler && handler->Looper() == this)
1175 						DispatchMessage(fLastMessage, handler);
1176 				}
1177 			}
1178 
1179 			if (fTerminating) {
1180 				// we leave the looper locked when we quit
1181 				return;
1182 			}
1183 
1184 			// Unlock the looper
1185 			Unlock();
1186 
1187 			// Delete the current message (fLastMessage)
1188 			if (fLastMessage) {
1189 				delete fLastMessage;
1190 				fLastMessage = NULL;
1191 			}
1192 
1193 			// Are any messages on the port?
1194 			if (port_count(fMsgPort) > 0) {
1195 				// Do outer loop
1196 				dispatchNextMessage = false;
1197 			}
1198 		}
1199 	}
1200 	PRINT(("BLooper::task_looper() done\n"));
1201 }
1202 
1203 
1204 void
1205 BLooper::_QuitRequested(BMessage* message)
1206 {
1207 	bool isQuitting = QuitRequested();
1208 
1209 	// We send a reply to the sender, when they're waiting for a reply or
1210 	// if the request message contains a boolean "_shutdown_" field with value
1211 	// true. In the latter case the message came from the registrar, asking
1212 	// the application to shut down.
1213 	bool shutdown;
1214 	if (message->IsSourceWaiting()
1215 		|| (message->FindBool("_shutdown_", &shutdown) == B_OK && shutdown)) {
1216 		BMessage replyMsg(B_REPLY);
1217 		replyMsg.AddBool("result", isQuitting);
1218 		replyMsg.AddInt32("thread", fThread);
1219 		message->SendReply(&replyMsg);
1220 	}
1221 
1222 	if (isQuitting)
1223 		Quit();
1224 }
1225 
1226 
1227 bool
1228 BLooper::AssertLocked() const
1229 {
1230 	if (!IsLocked()) {
1231 		debugger("looper must be locked before proceeding\n");
1232 		return false;
1233 	}
1234 
1235 	return true;
1236 }
1237 
1238 
1239 BHandler*
1240 BLooper::_TopLevelFilter(BMessage* message, BHandler* target)
1241 {
1242 	if (message == NULL)
1243 		return target;
1244 
1245 	// Apply the common filters first
1246 	target = _ApplyFilters(CommonFilterList(), message, target);
1247 	if (target) {
1248 		if (target->Looper() != this) {
1249 			debugger("Targeted handler does not belong to the looper.");
1250 			target = NULL;
1251 		} else {
1252 			// Now apply handler-specific filters
1253 			target = _HandlerFilter(message, target);
1254 		}
1255 	}
1256 
1257 	return target;
1258 }
1259 
1260 
1261 BHandler*
1262 BLooper::_HandlerFilter(BMessage* message, BHandler* target)
1263 {
1264 	// Keep running filters until our handler is NULL, or until the filtering
1265 	// handler returns itself as the designated handler
1266 	BHandler* previousTarget = NULL;
1267 	while (target != NULL && target != previousTarget) {
1268 		previousTarget = target;
1269 
1270 		target = _ApplyFilters(target->FilterList(), message, target);
1271 		if (target != NULL && target->Looper() != this) {
1272 			debugger("Targeted handler does not belong to the looper.");
1273 			target = NULL;
1274 		}
1275 	}
1276 
1277 	return target;
1278 }
1279 
1280 
1281 BHandler*
1282 BLooper::_ApplyFilters(BList* list, BMessage* message, BHandler* target)
1283 {
1284 	// This is where the action is!
1285 	// Check the parameters
1286 	if (!list || !message)
1287 		return target;
1288 
1289 	// For each filter in the provided list
1290 	BMessageFilter* filter = NULL;
1291 	for (int32 i = 0; i < list->CountItems(); ++i) {
1292 		filter = (BMessageFilter*)list->ItemAt(i);
1293 
1294 		// Check command conditions
1295 		if (filter->FiltersAnyCommand() || filter->Command() == message->what) {
1296 			// Check delivery conditions
1297 			message_delivery delivery = filter->MessageDelivery();
1298 			bool dropped = message->WasDropped();
1299 			if (delivery == B_ANY_DELIVERY
1300 				|| (delivery == B_DROPPED_DELIVERY && dropped)
1301 				|| (delivery == B_PROGRAMMED_DELIVERY && !dropped)) {
1302 				// Check source conditions
1303 				message_source source = filter->MessageSource();
1304 				bool remote = message->IsSourceRemote();
1305 				if (source == B_ANY_SOURCE
1306 					|| (source == B_REMOTE_SOURCE && remote)
1307 					|| (source == B_LOCAL_SOURCE && !remote)) {
1308 					// Are we using an "external" function?
1309 					filter_result result;
1310 					filter_hook func = filter->FilterFunction();
1311 					if (func)
1312 						result = func(message, &target, filter);
1313 					else
1314 						result = filter->Filter(message, &target);
1315 
1316 					// Is further processing allowed?
1317 					if (result == B_SKIP_MESSAGE) {
1318 						// No; time to bail out
1319 						return NULL;
1320 					}
1321 				}
1322 			}
1323 		}
1324 	}
1325 
1326 	return target;
1327 }
1328 
1329 
1330 void
1331 BLooper::check_lock()
1332 {
1333 	// This is a cheap variant of AssertLocked()
1334 	// It is used in situations where it's clear that the looper is valid,
1335 	// ie. from handlers
1336 	uint32 stack;
1337 	if (((uint32)&stack & ~(B_PAGE_SIZE - 1)) == fCachedStack
1338 		|| fOwner == find_thread(NULL))
1339 		return;
1340 
1341 	debugger("Looper must be locked.");
1342 }
1343 
1344 
1345 BHandler*
1346 BLooper::resolve_specifier(BHandler* target, BMessage* message)
1347 {
1348 	// Check params
1349 	if (!target || !message)
1350 		return NULL;
1351 
1352 	int32 index;
1353 	BMessage specifier;
1354 	int32 form;
1355 	const char* property;
1356 	status_t err = B_OK;
1357 	BHandler* newTarget = target;
1358 	// Loop to deal with nested specifiers
1359 	// (e.g., the 3rd button on the 4th view)
1360 	do {
1361 		err = message->GetCurrentSpecifier(&index, &specifier, &form, &property);
1362 		if (err) {
1363 			BMessage reply(B_REPLY);
1364 			reply.AddInt32("error", err);
1365 			message->SendReply(&reply);
1366 			return NULL;
1367 		}
1368 		// Current target gets what was the new target
1369 		target = newTarget;
1370 		newTarget = target->ResolveSpecifier(message, index, &specifier, form,
1371 			property);
1372 		// Check that new target is owned by looper; use IndexOf() to avoid
1373 		// dereferencing newTarget (possible race condition with object
1374 		// destruction by another looper)
1375 		if (!newTarget || IndexOf(newTarget) < 0)
1376 			return NULL;
1377 
1378 		// Get current specifier index (may change in ResolveSpecifier())
1379 		err = message->GetCurrentSpecifier(&index);
1380 	} while (newTarget && newTarget != target && !err && index >= 0);
1381 
1382 	return newTarget;
1383 }
1384 
1385 
1386 /*!	Releases all eventually nested locks. Must be called with the lock
1387 	actually held.
1388 */
1389 void
1390 BLooper::UnlockFully()
1391 {
1392 	AssertLocked();
1393 
1394 	// Clear the owner count
1395 	fOwnerCount = 0;
1396 	// Nobody owns the lock now
1397 	fOwner = -1;
1398 	fCachedStack = 0;
1399 #if DEBUG < 1
1400 	// There is now one less thread holding a lock on this looper
1401 	int32 atomicCount = atomic_add(&fAtomicCount, -1);
1402 	if (atomicCount > 1)
1403 #endif
1404 		release_sem(fLockSem);
1405 }
1406 
1407 
1408 //	#pragma mark -
1409 
1410 
1411 port_id
1412 _get_looper_port_(const BLooper* looper)
1413 {
1414 	return looper->fMsgPort;
1415 }
1416 
1417