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