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