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