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