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