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