xref: /haiku/src/kits/app/Looper.cpp (revision a30a4a41f948ebb03b95dab065a27a584ac0c97a)
1 /*
2  * Copyright 2001-2013 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 <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 	:
110 	BHandler(name)
111 {
112 	_InitData(name, priority, portCapacity);
113 }
114 
115 
116 BLooper::~BLooper()
117 {
118 	if (fRunCalled && !fTerminating) {
119 		debugger("You can't call delete on a BLooper object "
120 			"once it is running.");
121 	}
122 
123 	Lock();
124 
125 	// In case the looper thread calls Quit() fLastMessage is not deleted.
126 	if (fLastMessage) {
127 		delete fLastMessage;
128 		fLastMessage = NULL;
129 	}
130 
131 	// Close the message port and read and reply to the remaining messages.
132 	if (fMsgPort >= 0)
133 		close_port(fMsgPort);
134 
135 	// Clear the queue so our call to IsMessageWaiting() below doesn't give
136 	// us bogus info
137 	fDirectTarget->Close();
138 
139 	BMessage* message;
140 	while ((message = fDirectTarget->Queue()->NextMessage()) != NULL) {
141 		delete message;
142 			// msg will automagically post generic reply
143 	}
144 
145 	do {
146 		delete ReadMessageFromPort(0);
147 			// msg will automagically post generic reply
148 	} while (IsMessageWaiting());
149 
150 	fDirectTarget->Release();
151 	delete_port(fMsgPort);
152 
153 	// Clean up our filters
154 	SetCommonFilterList(NULL);
155 
156 	AutoLocker<BLooperList> ListLock(gLooperList);
157 	RemoveHandler(this);
158 
159 	// Remove all the "child" handlers
160 	int32 count = fHandlers.CountItems();
161 	for (int32 i = 0; i < count; i++) {
162 		BHandler* handler = (BHandler*)fHandlers.ItemAtFast(i);
163 		handler->SetNextHandler(NULL);
164 		handler->SetLooper(NULL);
165 	}
166 	fHandlers.MakeEmpty();
167 
168 	Unlock();
169 	gLooperList.RemoveLooper(this);
170 	delete_sem(fLockSem);
171 }
172 
173 
174 BLooper::BLooper(BMessage* data)
175 	: BHandler(data)
176 {
177 	int32 portCapacity;
178 	if (data->FindInt32("_port_cap", &portCapacity) != B_OK || portCapacity < 0)
179 		portCapacity = B_LOOPER_PORT_DEFAULT_CAPACITY;
180 
181 	int32 priority;
182 	if (data->FindInt32("_prio", &priority) != B_OK)
183 		priority = B_NORMAL_PRIORITY;
184 
185 	_InitData(Name(), priority, portCapacity);
186 }
187 
188 
189 BArchivable*
190 BLooper::Instantiate(BMessage* data)
191 {
192 	if (validate_instantiation(data, "BLooper"))
193 		return new BLooper(data);
194 
195 	return NULL;
196 }
197 
198 
199 status_t
200 BLooper::Archive(BMessage* data, bool deep) const
201 {
202 	status_t status = BHandler::Archive(data, deep);
203 	if (status < B_OK)
204 		return status;
205 
206 	port_info info;
207 	status = get_port_info(fMsgPort, &info);
208 	if (status == B_OK)
209 		status = data->AddInt32("_port_cap", info.capacity);
210 
211 	thread_info threadInfo;
212 	if (get_thread_info(Thread(), &threadInfo) == B_OK)
213 		status = data->AddInt32("_prio", threadInfo.priority);
214 
215 	return status;
216 }
217 
218 
219 status_t
220 BLooper::PostMessage(uint32 command)
221 {
222 	BMessage message(command);
223 	return _PostMessage(&message, this, NULL);
224 }
225 
226 
227 status_t
228 BLooper::PostMessage(BMessage* message)
229 {
230 	return _PostMessage(message, this, NULL);
231 }
232 
233 
234 status_t
235 BLooper::PostMessage(uint32 command, BHandler* handler, BHandler* replyTo)
236 {
237 	BMessage message(command);
238 	return _PostMessage(&message, handler, replyTo);
239 }
240 
241 
242 status_t
243 BLooper::PostMessage(BMessage* message, BHandler* handler, BHandler* replyTo)
244 {
245 	return _PostMessage(message, handler, replyTo);
246 }
247 
248 
249 void
250 BLooper::DispatchMessage(BMessage* message, BHandler* handler)
251 {
252 	PRINT(("BLooper::DispatchMessage(%.4s)\n", (char*)&message->what));
253 
254 	switch (message->what) {
255 		case _QUIT_:
256 			// Can't call Quit() to do this, because of the slight chance
257 			// another thread with have us locked between now and then.
258 			fTerminating = true;
259 
260 			// After returning from DispatchMessage(), the looper will be
261 			// deleted in _task0_()
262 			break;
263 
264 		case B_QUIT_REQUESTED:
265 			if (handler == this) {
266 				_QuitRequested(message);
267 				break;
268 			}
269 
270 			// fall through
271 
272 		default:
273 			handler->MessageReceived(message);
274 			break;
275 	}
276 	PRINT(("BLooper::DispatchMessage() done\n"));
277 }
278 
279 
280 void
281 BLooper::MessageReceived(BMessage* message)
282 {
283 	// TODO: implement scripting support
284 	BHandler::MessageReceived(message);
285 }
286 
287 
288 BMessage*
289 BLooper::CurrentMessage() const
290 {
291 	return fLastMessage;
292 }
293 
294 
295 BMessage*
296 BLooper::DetachCurrentMessage()
297 {
298 	BMessage* message = fLastMessage;
299 	fLastMessage = NULL;
300 	return message;
301 }
302 
303 
304 void
305 BLooper::DispatchExternalMessage(BMessage* message, BHandler* handler,
306 	bool& _detached)
307 {
308 	AssertLocked();
309 
310 	BMessage* previousMessage = fLastMessage;
311 	fLastMessage = message;
312 
313 	DispatchMessage(message, handler);
314 
315 	_detached = fLastMessage == NULL;
316 	fLastMessage = previousMessage;
317 }
318 
319 
320 BMessageQueue*
321 BLooper::MessageQueue() const
322 {
323 	return fDirectTarget->Queue();
324 }
325 
326 
327 bool
328 BLooper::IsMessageWaiting() const
329 {
330 	AssertLocked();
331 
332 	if (!fDirectTarget->Queue()->IsEmpty())
333 		return true;
334 
335 	int32 count;
336 	do {
337 		count = port_buffer_size_etc(fMsgPort, B_RELATIVE_TIMEOUT, 0);
338 	} while (count == B_INTERRUPTED);
339 
340 	return count > 0;
341 }
342 
343 
344 void
345 BLooper::AddHandler(BHandler* handler)
346 {
347 	if (handler == NULL)
348 		return;
349 
350 	AssertLocked();
351 
352 	if (handler->Looper() == NULL) {
353 		fHandlers.AddItem(handler);
354 		handler->SetLooper(this);
355 		if (handler != this)	// avoid a cycle
356 			handler->SetNextHandler(this);
357 	}
358 }
359 
360 
361 bool
362 BLooper::RemoveHandler(BHandler* handler)
363 {
364 	if (handler == NULL)
365 		return false;
366 
367 	AssertLocked();
368 
369 	if (handler->Looper() == this && fHandlers.RemoveItem(handler)) {
370 		if (handler == fPreferred)
371 			fPreferred = NULL;
372 
373 		handler->SetNextHandler(NULL);
374 		handler->SetLooper(NULL);
375 		return true;
376 	}
377 
378 	return false;
379 }
380 
381 
382 int32
383 BLooper::CountHandlers() const
384 {
385 	AssertLocked();
386 
387 	return fHandlers.CountItems();
388 }
389 
390 
391 BHandler*
392 BLooper::HandlerAt(int32 index) const
393 {
394 	AssertLocked();
395 
396 	return (BHandler*)fHandlers.ItemAt(index);
397 }
398 
399 
400 int32
401 BLooper::IndexOf(BHandler* handler) const
402 {
403 	AssertLocked();
404 
405 	return fHandlers.IndexOf(handler);
406 }
407 
408 
409 BHandler*
410 BLooper::PreferredHandler() const
411 {
412 	return fPreferred;
413 }
414 
415 
416 void
417 BLooper::SetPreferredHandler(BHandler* handler)
418 {
419 	if (handler && handler->Looper() == this && IndexOf(handler) >= 0) {
420 		fPreferred = handler;
421 	} else {
422 		fPreferred = NULL;
423 	}
424 }
425 
426 
427 thread_id
428 BLooper::Run()
429 {
430 	AssertLocked();
431 
432 	if (fRunCalled) {
433 		// Not allowed to call Run() more than once
434 		debugger("can't call BLooper::Run twice!");
435 		return fThread;
436 	}
437 
438 	fThread = spawn_thread(_task0_, Name(), fInitPriority, this);
439 	if (fThread < B_OK)
440 		return fThread;
441 
442 	if (fMsgPort < B_OK)
443 		return fMsgPort;
444 
445 	fRunCalled = true;
446 	Unlock();
447 
448 	status_t err = resume_thread(fThread);
449 	if (err < B_OK)
450 		return err;
451 
452 	return fThread;
453 }
454 
455 
456 void
457 BLooper::Quit()
458 {
459 	PRINT(("BLooper::Quit()\n"));
460 
461 	if (!IsLocked()) {
462 		printf("ERROR - you must Lock a looper before calling Quit(), "
463 			"team=%" B_PRId32 ", looper=%s\n", Team(),
464 			Name() ? Name() : "unnamed");
465 	}
466 
467 	// Try to lock
468 	if (!Lock()) {
469 		// We're toast already
470 		return;
471 	}
472 
473 	PRINT(("  is locked\n"));
474 
475 	if (!fRunCalled) {
476 		PRINT(("  Run() has not been called yet\n"));
477 		fTerminating = true;
478 		delete this;
479 	} else if (find_thread(NULL) == fThread) {
480 		PRINT(("  We are the looper thread\n"));
481 		fTerminating = true;
482 		delete this;
483 		exit_thread(0);
484 	} else {
485 		PRINT(("  Run() has already been called and we are not the looper thread\n"));
486 
487 		// As with sem in _Lock(), we need to cache this here in case the looper
488 		// disappears before we get to the wait_for_thread() below
489 		thread_id thread = Thread();
490 
491 		// We need to unlock here. Otherwise the looper thread can't
492 		// dispatch the _QUIT_ message we're going to post.
493 		UnlockFully();
494 
495 		// As per the BeBook, if we've been called by a thread other than
496 		// our own, the rest of the message queue has to get processed.  So
497 		// we put this in the queue, and when it shows up, we'll call Quit()
498 		// from our own thread.
499 		// QuitRequested() will not be called in this case.
500 		PostMessage(_QUIT_);
501 
502 		// We have to wait until the looper is done processing any remaining
503 		// messages.
504 		status_t status;
505 		while (wait_for_thread(thread, &status) == B_INTERRUPTED)
506 			;
507 	}
508 
509 	PRINT(("BLooper::Quit() done\n"));
510 }
511 
512 
513 bool
514 BLooper::QuitRequested()
515 {
516 	return true;
517 }
518 
519 
520 bool
521 BLooper::Lock()
522 {
523 	// Defer to global _Lock(); see notes there
524 	return _Lock(this, -1, B_INFINITE_TIMEOUT) == B_OK;
525 }
526 
527 
528 void
529 BLooper::Unlock()
530 {
531 PRINT(("BLooper::Unlock()\n"));
532 	//	Make sure we're locked to begin with
533 	AssertLocked();
534 
535 	//	Decrement fOwnerCount
536 	--fOwnerCount;
537 PRINT(("  fOwnerCount now: %ld\n", fOwnerCount));
538 	//	Check to see if the owner still wants a lock
539 	if (fOwnerCount == 0) {
540 		//	Set fOwner to invalid thread_id (< 0)
541 		fOwner = -1;
542 		fCachedStack = 0;
543 
544 #if DEBUG < 1
545 		//	Decrement requested lock count (using fAtomicCount for this)
546 		int32 atomicCount = atomic_add(&fAtomicCount, -1);
547 PRINT(("  fAtomicCount now: %ld\n", fAtomicCount));
548 
549 		// Check if anyone is waiting for a lock
550 		// and release if it's the case
551 		if (atomicCount > 1)
552 #endif
553 			release_sem(fLockSem);
554 	}
555 PRINT(("BLooper::Unlock() done\n"));
556 }
557 
558 
559 bool
560 BLooper::IsLocked() const
561 {
562 	if (!gLooperList.IsLooperValid(this)) {
563 		// The looper is gone, so of course it's not locked
564 		return false;
565 	}
566 
567 	uint32 stack;
568 	return ((addr_t)&stack & ~(B_PAGE_SIZE - 1)) == fCachedStack
569 		|| find_thread(NULL) == fOwner;
570 }
571 
572 
573 status_t
574 BLooper::LockWithTimeout(bigtime_t timeout)
575 {
576 	return _Lock(this, -1, timeout);
577 }
578 
579 
580 thread_id
581 BLooper::Thread() const
582 {
583 	return fThread;
584 }
585 
586 
587 team_id
588 BLooper::Team() const
589 {
590 	return BPrivate::current_team();
591 }
592 
593 
594 BLooper*
595 BLooper::LooperForThread(thread_id thread)
596 {
597 	return gLooperList.LooperForThread(thread);
598 }
599 
600 
601 thread_id
602 BLooper::LockingThread() const
603 {
604 	return fOwner;
605 }
606 
607 
608 int32
609 BLooper::CountLocks() const
610 {
611 	return fOwnerCount;
612 }
613 
614 
615 int32
616 BLooper::CountLockRequests() const
617 {
618 	return fAtomicCount;
619 }
620 
621 
622 sem_id
623 BLooper::Sem() const
624 {
625 	return fLockSem;
626 }
627 
628 
629 BHandler*
630 BLooper::ResolveSpecifier(BMessage* message, int32 index, BMessage* specifier,
631 	int32 what, const char* property)
632 {
633 /**
634 	@note	When I was first dumping the results of GetSupportedSuites() from
635 			various classes, the use of the extra_data field was quite
636 			mysterious to me.  Then I dumped BApplication and compared the
637 			result against the BeBook's docs for scripting BApplication.  A
638 			bunch of it isn't documented, but what is tipped me to the idea
639 			that the extra_data is being used as a quick and dirty way to tell
640 			what scripting "command" has been sent, e.g., for easy use in a
641 			switch statement.  Would certainly be a lot faster than a bunch of
642 			string comparisons -- which wouldn't tell the whole story anyway,
643 			because of the same name being used for multiple properties.
644  */
645  	BPropertyInfo propertyInfo(sLooperPropInfo);
646 	uint32 data;
647 	status_t err = B_OK;
648 	const char* errMsg = "";
649 	if (propertyInfo.FindMatch(message, index, specifier, what, property, &data)
650 			>= 0) {
651 		switch (data) {
652 			case BLOOPER_PROCESS_INTERNALLY:
653 				return this;
654 
655 			case BLOOPER_HANDLER_BY_INDEX:
656 			{
657 				int32 index = specifier->FindInt32("index");
658 				if (what == B_REVERSE_INDEX_SPECIFIER) {
659 					index = CountHandlers() - index;
660 				}
661 				BHandler* target = HandlerAt(index);
662 				if (target) {
663 					// Specifier has been fully handled
664 					message->PopSpecifier();
665 					return target;
666 				} else {
667 					err = B_BAD_INDEX;
668 					errMsg = "handler index out of range";
669 				}
670 				break;
671 			}
672 
673 			default:
674 				err = B_BAD_SCRIPT_SYNTAX;
675 				errMsg = "Didn't understand the specifier(s)";
676 		}
677 	} else {
678 		return BHandler::ResolveSpecifier(message, index, specifier, what,
679 			property);
680 	}
681 
682 	BMessage reply(B_MESSAGE_NOT_UNDERSTOOD);
683 	reply.AddInt32("error", err);
684 	reply.AddString("message", errMsg);
685 	message->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 == NULL)
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 == NULL)
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 == NULL)
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",
1052 		(char*)msgCode, buffer, bufferSize));
1053 
1054 	return buffer;
1055 }
1056 
1057 
1058 BMessage*
1059 BLooper::ReadMessageFromPort(bigtime_t timeout)
1060 {
1061 	PRINT(("BLooper::ReadMessageFromPort()\n"));
1062 	int32 msgCode;
1063 	BMessage* message = NULL;
1064 
1065 	void* buffer = ReadRawFromPort(&msgCode, timeout);
1066 	if (buffer == NULL)
1067 		return NULL;
1068 
1069 	message = ConvertToMessage(buffer, msgCode);
1070 	free(buffer);
1071 
1072 	PRINT(("BLooper::ReadMessageFromPort() done: %p\n", message));
1073 	return message;
1074 }
1075 
1076 
1077 BMessage*
1078 BLooper::ConvertToMessage(void* buffer, int32 code)
1079 {
1080 	PRINT(("BLooper::ConvertToMessage()\n"));
1081 	if (buffer == NULL)
1082 		return NULL;
1083 
1084 	BMessage* message = new BMessage();
1085 	if (message->Unflatten((const char*)buffer) != B_OK) {
1086 		PRINT(("BLooper::ConvertToMessage(): unflattening message failed\n"));
1087 		delete message;
1088 		message = NULL;
1089 	}
1090 
1091 	PRINT(("BLooper::ConvertToMessage(): %p\n", message));
1092 	return message;
1093 }
1094 
1095 
1096 void
1097 BLooper::task_looper()
1098 {
1099 	PRINT(("BLooper::task_looper()\n"));
1100 	// Check that looper is locked (should be)
1101 	AssertLocked();
1102 	// Unlock the looper
1103 	Unlock();
1104 
1105 	if (IsLocked())
1106 		debugger("looper must not be locked!");
1107 
1108 	// loop: As long as we are not terminating.
1109 	while (!fTerminating) {
1110 		PRINT(("LOOPER: outer loop\n"));
1111 		// TODO: timeout determination algo
1112 		//	Read from message port (how do we determine what the timeout is?)
1113 		PRINT(("LOOPER: MessageFromPort()...\n"));
1114 		BMessage* msg = MessageFromPort();
1115 		PRINT(("LOOPER: ...done\n"));
1116 
1117 		//	Did we get a message?
1118 		if (msg)
1119 			_AddMessagePriv(msg);
1120 
1121 		// Get message count from port
1122 		int32 msgCount = port_count(fMsgPort);
1123 		for (int32 i = 0; i < msgCount; ++i) {
1124 			// Read 'count' messages from port (so we will not block)
1125 			// We use zero as our timeout since we know there is stuff there
1126 			msg = MessageFromPort(0);
1127 			if (msg)
1128 				_AddMessagePriv(msg);
1129 		}
1130 
1131 		// loop: As long as there are messages in the queue and the port is
1132 		//		 empty... and we are not terminating, of course.
1133 		bool dispatchNextMessage = true;
1134 		while (!fTerminating && dispatchNextMessage) {
1135 			PRINT(("LOOPER: inner loop\n"));
1136 			// Get next message from queue (assign to fLastMessage after
1137 			// locking)
1138 			BMessage* message = fDirectTarget->Queue()->NextMessage();
1139 
1140 			Lock();
1141 
1142 			fLastMessage = message;
1143 
1144 			if (fLastMessage == NULL) {
1145 				// No more messages: Unlock the looper and terminate the
1146 				// dispatch loop.
1147 				dispatchNextMessage = false;
1148 			} else {
1149 				PRINT(("LOOPER: fLastMessage: 0x%lx: %.4s\n", fLastMessage->what,
1150 					(char*)&fLastMessage->what));
1151 				DBG(fLastMessage->PrintToStream());
1152 
1153 				// Get the target handler
1154 				BHandler* handler = NULL;
1155 				BMessage::Private messagePrivate(fLastMessage);
1156 				bool usePreferred = messagePrivate.UsePreferredTarget();
1157 
1158 				if (usePreferred) {
1159 					PRINT(("LOOPER: use preferred target\n"));
1160 					handler = fPreferred;
1161 					if (handler == NULL)
1162 						handler = this;
1163 				} else {
1164 					gDefaultTokens.GetToken(messagePrivate.GetTarget(),
1165 						B_HANDLER_TOKEN, (void**)&handler);
1166 
1167 					// if this handler doesn't belong to us, we drop the message
1168 					if (handler != NULL && handler->Looper() != this)
1169 						handler = NULL;
1170 
1171 					PRINT(("LOOPER: use %ld, handler: %p, this: %p\n",
1172 						messagePrivate.GetTarget(), handler, this));
1173 				}
1174 
1175 				// Is this a scripting message? (BMessage::HasSpecifiers())
1176 				if (handler != NULL && fLastMessage->HasSpecifiers()) {
1177 					int32 index = 0;
1178 					// Make sure the current specifier is kosher
1179 					if (fLastMessage->GetCurrentSpecifier(&index) == B_OK)
1180 						handler = resolve_specifier(handler, fLastMessage);
1181 				}
1182 
1183 				if (handler) {
1184 					// Do filtering
1185 					handler = _TopLevelFilter(fLastMessage, handler);
1186 					PRINT(("LOOPER: _TopLevelFilter(): %p\n", handler));
1187 					if (handler && handler->Looper() == this)
1188 						DispatchMessage(fLastMessage, handler);
1189 				}
1190 			}
1191 
1192 			if (fTerminating) {
1193 				// we leave the looper locked when we quit
1194 				return;
1195 			}
1196 
1197 			message = fLastMessage;
1198 			fLastMessage = NULL;
1199 
1200 			// Unlock the looper
1201 			Unlock();
1202 
1203 			// Delete the current message (fLastMessage)
1204 			if (message != NULL)
1205 				delete message;
1206 
1207 			// Are any messages on the port?
1208 			if (port_count(fMsgPort) > 0) {
1209 				// Do outer loop
1210 				dispatchNextMessage = false;
1211 			}
1212 		}
1213 	}
1214 	PRINT(("BLooper::task_looper() done\n"));
1215 }
1216 
1217 
1218 void
1219 BLooper::_QuitRequested(BMessage* message)
1220 {
1221 	bool isQuitting = QuitRequested();
1222 	int32 thread = fThread;
1223 
1224 	if (isQuitting)
1225 		Quit();
1226 
1227 	// We send a reply to the sender, when they're waiting for a reply or
1228 	// if the request message contains a boolean "_shutdown_" field with value
1229 	// true. In the latter case the message came from the registrar, asking
1230 	// the application to shut down.
1231 	bool shutdown;
1232 	if (message->IsSourceWaiting()
1233 		|| (message->FindBool("_shutdown_", &shutdown) == B_OK && shutdown)) {
1234 		BMessage replyMsg(B_REPLY);
1235 		replyMsg.AddBool("result", isQuitting);
1236 		replyMsg.AddInt32("thread", thread);
1237 		message->SendReply(&replyMsg);
1238 	}
1239 }
1240 
1241 
1242 bool
1243 BLooper::AssertLocked() const
1244 {
1245 	if (!IsLocked()) {
1246 		debugger("looper must be locked before proceeding\n");
1247 		return false;
1248 	}
1249 
1250 	return true;
1251 }
1252 
1253 
1254 BHandler*
1255 BLooper::_TopLevelFilter(BMessage* message, BHandler* target)
1256 {
1257 	if (message == NULL)
1258 		return target;
1259 
1260 	// Apply the common filters first
1261 	target = _ApplyFilters(CommonFilterList(), message, target);
1262 	if (target) {
1263 		if (target->Looper() != this) {
1264 			debugger("Targeted handler does not belong to the looper.");
1265 			target = NULL;
1266 		} else {
1267 			// Now apply handler-specific filters
1268 			target = _HandlerFilter(message, target);
1269 		}
1270 	}
1271 
1272 	return target;
1273 }
1274 
1275 
1276 BHandler*
1277 BLooper::_HandlerFilter(BMessage* message, BHandler* target)
1278 {
1279 	// Keep running filters until our handler is NULL, or until the filtering
1280 	// handler returns itself as the designated handler
1281 	BHandler* previousTarget = NULL;
1282 	while (target != NULL && target != previousTarget) {
1283 		previousTarget = target;
1284 
1285 		target = _ApplyFilters(target->FilterList(), message, target);
1286 		if (target != NULL && target->Looper() != this) {
1287 			debugger("Targeted handler does not belong to the looper.");
1288 			target = NULL;
1289 		}
1290 	}
1291 
1292 	return target;
1293 }
1294 
1295 
1296 BHandler*
1297 BLooper::_ApplyFilters(BList* list, BMessage* message, BHandler* target)
1298 {
1299 	// This is where the action is!
1300 
1301 	// check the parameters
1302 	if (list == NULL || message == NULL)
1303 		return target;
1304 
1305 	// for each filter in the provided list
1306 	BMessageFilter* filter = NULL;
1307 	for (int32 i = 0; i < list->CountItems(); ++i) {
1308 		filter = (BMessageFilter*)list->ItemAt(i);
1309 
1310 		// check command conditions
1311 		if (filter->FiltersAnyCommand() || filter->Command() == message->what) {
1312 			// check delivery conditions
1313 			message_delivery delivery = filter->MessageDelivery();
1314 			bool dropped = message->WasDropped();
1315 			if (delivery == B_ANY_DELIVERY
1316 				|| (delivery == B_DROPPED_DELIVERY && dropped)
1317 				|| (delivery == B_PROGRAMMED_DELIVERY && !dropped)) {
1318 				// check source conditions
1319 				message_source source = filter->MessageSource();
1320 				bool remote = message->IsSourceRemote();
1321 				if (source == B_ANY_SOURCE
1322 					|| (source == B_REMOTE_SOURCE && remote)
1323 					|| (source == B_LOCAL_SOURCE && !remote)) {
1324 					// Are we using an "external" function?
1325 					filter_result result;
1326 					filter_hook filterFunction = filter->FilterFunction();
1327 					if (filterFunction != NULL)
1328 						result = filterFunction(message, &target, filter);
1329 					else
1330 						result = filter->Filter(message, &target);
1331 
1332 					// Is further processing allowed?
1333 					if (result == B_SKIP_MESSAGE) {
1334 						// no, time to bail out
1335 						return NULL;
1336 					}
1337 				}
1338 			}
1339 		}
1340 	}
1341 
1342 	return target;
1343 }
1344 
1345 
1346 void
1347 BLooper::check_lock()
1348 {
1349 	// this is a cheap variant of AssertLocked()
1350 	// it is used in situations where it's clear that the looper is valid,
1351 	// i.e. from handlers
1352 	uint32 stack;
1353 	if (((addr_t)&stack & ~(B_PAGE_SIZE - 1)) == fCachedStack
1354 		|| fOwner == find_thread(NULL)) {
1355 		return;
1356 	}
1357 
1358 	debugger("Looper must be locked.");
1359 }
1360 
1361 
1362 BHandler*
1363 BLooper::resolve_specifier(BHandler* target, BMessage* message)
1364 {
1365 	// check params
1366 	if (!target || !message)
1367 		return NULL;
1368 
1369 	int32 index;
1370 	BMessage specifier;
1371 	int32 form;
1372 	const char* property;
1373 	status_t err = B_OK;
1374 	BHandler* newTarget = target;
1375 	// loop to deal with nested specifiers
1376 	// (e.g., the 3rd button on the 4th view)
1377 	do {
1378 		err = message->GetCurrentSpecifier(&index, &specifier, &form,
1379 			&property);
1380 		if (err != B_OK) {
1381 			BMessage reply(B_REPLY);
1382 			reply.AddInt32("error", err);
1383 			message->SendReply(&reply);
1384 			return NULL;
1385 		}
1386 		// current target gets what was the new target
1387 		target = newTarget;
1388 		newTarget = target->ResolveSpecifier(message, index, &specifier, form,
1389 			property);
1390 		// check that new target is owned by looper; use IndexOf() to avoid
1391 		// dereferencing newTarget (possible race condition with object
1392 		// destruction by another looper)
1393 		if (newTarget == NULL || IndexOf(newTarget) < 0)
1394 			return NULL;
1395 
1396 		// get current specifier index (may change in ResolveSpecifier())
1397 		err = message->GetCurrentSpecifier(&index);
1398 	} while (newTarget && newTarget != target && err == B_OK && index >= 0);
1399 
1400 	return newTarget;
1401 }
1402 
1403 
1404 /*!	Releases all eventually nested locks. Must be called with the lock
1405 	actually held.
1406 */
1407 void
1408 BLooper::UnlockFully()
1409 {
1410 	AssertLocked();
1411 
1412 	// Clear the owner count
1413 	fOwnerCount = 0;
1414 	// Nobody owns the lock now
1415 	fOwner = -1;
1416 	fCachedStack = 0;
1417 #if DEBUG < 1
1418 	// There is now one less thread holding a lock on this looper
1419 	int32 atomicCount = atomic_add(&fAtomicCount, -1);
1420 	if (atomicCount > 1)
1421 #endif
1422 		release_sem(fLockSem);
1423 }
1424 
1425 
1426 //	#pragma mark -
1427 
1428 
1429 port_id
1430 _get_looper_port_(const BLooper* looper)
1431 {
1432 	return looper->fMsgPort;
1433 }
1434