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