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