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