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