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