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