xref: /haiku/src/kits/app/Looper.cpp (revision a4f6a81235ca2522c01f532de13cad9b729d4029)
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 		//	Decrement requested lock count (using fAtomicCount for this)
555 /*		int32 atomicCount =*/ atomic_add(&fAtomicCount, -1);
556 PRINT(("  fAtomicCount now: %ld\n", fAtomicCount));
557 
558 		//	Check if anyone is waiting for a lock
559 // bonefish: Currently _Lock() always acquires the semaphore.
560 //		if (atomicCount > 0)
561 		{
562 			//	release the lock
563 			release_sem(fLockSem);
564 		}
565 	}
566 PRINT(("BLooper::Unlock() done\n"));
567 }
568 
569 
570 bool
571 BLooper::IsLocked() const
572 {
573 	// We have to lock the list for the call to IsLooperValid().  Has the side
574 	// effect of not letting the looper get deleted while we're here.
575 	BObjectLocker<BLooperList> ListLock(gLooperList);
576 
577 	if (!ListLock.IsLocked()) {
578 		// If we can't lock the list, our semaphore is probably toast
579 		return false;
580 	}
581 
582 	if (!IsLooperValid(this)) {
583 		// The looper is gone, so of course it's not locked
584 		return false;
585 	}
586 
587 	// Got this from Jeremy's BLocker implementation
588 	return find_thread(NULL) == fOwner;
589 }
590 
591 
592 status_t
593 BLooper::LockWithTimeout(bigtime_t timeout)
594 {
595 	return _Lock(this, -1, timeout);
596 }
597 
598 
599 thread_id
600 BLooper::Thread() const
601 {
602 	return fTaskID;
603 }
604 
605 
606 team_id
607 BLooper::Team() const
608 {
609 	return sTeamID;
610 }
611 
612 
613 BLooper*
614 BLooper::LooperForThread(thread_id tid)
615 {
616 	BObjectLocker<BLooperList> ListLock(gLooperList);
617 	if (ListLock.IsLocked())
618 		return gLooperList.LooperForThread(tid);
619 
620 	return NULL;
621 }
622 
623 
624 thread_id
625 BLooper::LockingThread() const
626 {
627 	return fOwner;
628 }
629 
630 
631 int32
632 BLooper::CountLocks() const
633 {
634 	return fOwnerCount;
635 }
636 
637 
638 int32
639 BLooper::CountLockRequests() const
640 {
641 	return fAtomicCount;
642 }
643 
644 
645 sem_id
646 BLooper::Sem() const
647 {
648 	return fLockSem;
649 }
650 
651 
652 BHandler*
653 BLooper::ResolveSpecifier(BMessage* msg, int32 index,
654 	BMessage* specifier, int32 form, const char* property)
655 {
656 /**
657 	@note	When I was first dumping the results of GetSupportedSuites() from
658 			various classes, the use of the extra_data field was quite
659 			mysterious to me.  Then I dumped BApplication and compared the
660 			result against the BeBook's docs for scripting BApplication.  A
661 			bunch of it isn't documented, but what is tipped me to the idea
662 			that the extra_data is being used as a quick and dirty way to tell
663 			what scripting "command" has been sent, e.g., for easy use in a
664 			switch statement.  Would certainly be a lot faster than a bunch of
665 			string comparisons -- which wouldn't tell the whole story anyway,
666 			because of the same name being used for multiple properties.
667  */
668 	BPropertyInfo PropertyInfo(gLooperPropInfo);
669 	uint32 data;
670 	status_t err = B_OK;
671 	const char* errMsg = "";
672 	if (PropertyInfo.FindMatch(msg, index, specifier, form, property, &data) >= 0) {
673 		switch (data) {
674 			case BLOOPER_PROCESS_INTERNALLY:
675 				return this;
676 
677 			case BLOOPER_HANDLER_BY_INDEX:
678 			{
679 				int32 index = specifier->FindInt32("index");
680 				if (form == B_REVERSE_INDEX_SPECIFIER) {
681 					index = CountHandlers() - index;
682 				}
683 				BHandler* target = HandlerAt(index);
684 				if (target) {
685 					// Specifier has been fully handled
686 					msg->PopSpecifier();
687 					return target;
688 				} else {
689 					err = B_BAD_INDEX;
690 					errMsg = "handler index out of range";
691 				}
692 				break;
693 			}
694 
695 			default:
696 				err = B_BAD_SCRIPT_SYNTAX;
697 				errMsg = "Didn't understand the specifier(s)";
698 				break;
699 		}
700 	} else {
701 		return BHandler::ResolveSpecifier(msg, index, specifier, form,
702 			property);
703 	}
704 
705 	BMessage Reply(B_MESSAGE_NOT_UNDERSTOOD);
706 	Reply.AddInt32("error", err);
707 	Reply.AddString("message", errMsg);
708 	msg->SendReply(&Reply);
709 
710 	return NULL;
711 }
712 
713 
714 status_t
715 BLooper::GetSupportedSuites(BMessage* data)
716 {
717 	if (data == NULL)
718 		return B_BAD_VALUE;
719 
720 	status_t status = data->AddString("Suites", "suite/vnd.Be-handler");
721 	if (status == B_OK) {
722 		BPropertyInfo PropertyInfo(gLooperPropInfo);
723 		status = data->AddFlat("message", &PropertyInfo);
724 		if (status == B_OK)
725 			status = BHandler::GetSupportedSuites(data);
726 	}
727 
728 	return status;
729 }
730 
731 
732 void
733 BLooper::AddCommonFilter(BMessageFilter* filter)
734 {
735 	if (!filter)
736 		return;
737 
738 	AssertLocked();
739 
740 	if (filter->Looper()) {
741 		debugger("A MessageFilter can only be used once.");
742 		return;
743 	}
744 
745 	if (!fCommonFilters)
746 		fCommonFilters = new BList(FILTER_LIST_BLOCK_SIZE);
747 
748 	filter->SetLooper(this);
749 	fCommonFilters->AddItem(filter);
750 }
751 
752 
753 bool
754 BLooper::RemoveCommonFilter(BMessageFilter* filter)
755 {
756 	AssertLocked();
757 
758 	if (!fCommonFilters)
759 		return false;
760 
761 	bool result = fCommonFilters->RemoveItem(filter);
762 	if (result)
763 		filter->SetLooper(NULL);
764 
765 	return result;
766 }
767 
768 
769 void
770 BLooper::SetCommonFilterList(BList* filters)
771 {
772 	// We have a somewhat serious problem here.  It is entirely possible in R5
773 	// to assign a given list of filters to *two* BLoopers simultaneously.  This
774 	// becomes problematic when the loopers are destroyed: the last looper
775 	// destroyed will have a problem when it tries to delete a filter list that
776 	// has already been deleted.  In R5, this results in a general protection
777 	// fault.  We fix this by checking the filter list for ownership issues.
778 
779 	AssertLocked();
780 
781 	BMessageFilter* filter;
782 	if (filters) {
783 		// Check for ownership issues
784 		for (int32 i = 0; i < filters->CountItems(); ++i) {
785 			filter = (BMessageFilter*)filters->ItemAt(i);
786 			if (filter->Looper()) {
787 				debugger("A MessageFilter can only be used once.");
788 				return;
789 			}
790 		}
791 	}
792 
793 	if (fCommonFilters) {
794 		for (int32 i = 0; i < fCommonFilters->CountItems(); ++i) {
795 			delete (BMessageFilter*)fCommonFilters->ItemAt(i);
796 		}
797 
798 		delete fCommonFilters;
799 		fCommonFilters = NULL;
800 	}
801 
802 	// Per the BeBook, we take ownership of the list
803 	fCommonFilters = filters;
804 	if (fCommonFilters) {
805 		for (int32 i = 0; i < fCommonFilters->CountItems(); ++i) {
806 			filter = (BMessageFilter*)fCommonFilters->ItemAt(i);
807 			filter->SetLooper(this);
808 		}
809 	}
810 }
811 
812 
813 BList*
814 BLooper::CommonFilterList() const
815 {
816 	return fCommonFilters;
817 }
818 
819 
820 status_t
821 BLooper::Perform(perform_code d, void* arg)
822 {
823 	// This is sort of what we're doing for this function everywhere
824 	return BHandler::Perform(d, arg);
825 }
826 
827 
828 BMessage*
829 BLooper::MessageFromPort(bigtime_t timeout)
830 {
831 	return ReadMessageFromPort(timeout);
832 }
833 
834 
835 void BLooper::_ReservedLooper1() {}
836 void BLooper::_ReservedLooper2() {}
837 void BLooper::_ReservedLooper3() {}
838 void BLooper::_ReservedLooper4() {}
839 void BLooper::_ReservedLooper5() {}
840 void BLooper::_ReservedLooper6() {}
841 
842 
843 BLooper::BLooper(const BLooper&)
844 {
845 	// Copy construction not allowed
846 }
847 
848 
849 BLooper& BLooper::operator=(const BLooper& )
850 {
851 	// Looper copying not allowed
852 	return *this;
853 }
854 
855 
856 BLooper::BLooper(int32 priority, port_id port, const char* name)
857 {
858 	// This must be a legacy constructor
859 	fMsgPort = port;
860 	InitData(name, priority, B_LOOPER_PORT_DEFAULT_CAPACITY);
861 }
862 
863 
864 status_t
865 BLooper::_PostMessage(BMessage *msg, BHandler *handler,
866 	BHandler *replyTo)
867 {
868 	BObjectLocker<BLooperList> listLocker(gLooperList);
869 	if (!listLocker.IsLocked())
870 		return B_ERROR;
871 
872 	if (!IsLooperValid(this))
873 		return B_BAD_VALUE;
874 
875 	// Does handler belong to this looper?
876 	if (handler && handler->Looper() != this)
877 		return B_MISMATCHED_VALUES;
878 
879 	status_t status;
880 	BMessenger messenger(handler, this, &status);
881 	if (status == B_OK)
882 		status = messenger.SendMessage(msg, replyTo, 0);
883 
884 	return status;
885 }
886 
887 
888 status_t
889 BLooper::_Lock(BLooper* loop, port_id port, bigtime_t timeout)
890 {
891 PRINT(("BLooper::_Lock(%p, %lx)\n", loop, port));
892 /**
893 	@note	The assumption I'm under here is that since we can get the port of
894 			the BLooper directly from the BLooper itself, the port parameter is
895 			for identifying BLoopers by port_id when a pointer to the BLooper in
896 			question is not available.  So this function has two modes:
897 				o When loop != NULL, use it directly
898 				o When loop == NULL and port is valid, use the port_id to get
899 				  the looper
900 			I scoured the docs to find out what constitutes a valid port_id to
901 			no avail.  Since create_port uses the standard error values in its
902 			returned port_id, I'll assume that anything less than zero is a safe
903 			bet as an *invalid* port_id.  I'm guessing that, like thread and
904 			semaphore ids, anything >= zero is valid.  So, the short version of
905 			this reads: if you don't want to find by port_id, make port = -1.
906 
907 			Another assumption I'm making is that Lock() and LockWithTimeout()
908 			are covers for this function.  If it turns out that we don't really
909 			need this function, I may refactor this code into LockWithTimeout()
910 			and have Lock() call it instead.  This function could then be
911 			removed.
912  */
913 
914 	//	Check params (loop, port)
915 	if (!loop && port < 0)
916 	{
917 PRINT(("BLooper::_Lock() done 1\n"));
918 		return B_BAD_VALUE;
919 	}
920 
921 	// forward declared so I can use BAutolock on sLooperListLock
922 	thread_id curThread;
923 	sem_id sem;
924 
925 /**
926 	@note	We lock the looper list at the start of the lock operation to
927 			prevent the looper getting removed from the list while we're
928 			doing list operations.  Also ensures that the looper doesn't
929 			get deleted here (since ~BLooper() has to lock the list as
930 			well to remove itself).
931  */
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 		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
1000 	status_t err;
1001 	do
1002 	{
1003 		err = acquire_sem_etc(sem, 1, B_RELATIVE_TIMEOUT, timeout);
1004 	} while (err == B_INTERRUPTED);
1005 
1006 	if (!err)
1007 	{
1008 		//		Assign current thread to fOwner
1009 		loop->fOwner = curThread;
1010 		//		Reset fOwnerCount to 1
1011 		loop->fOwnerCount = 1;
1012 	}
1013 
1014 	PRINT(("BLooper::_Lock() done: %lx\n", err));
1015 	return err;
1016 }
1017 
1018 
1019 status_t
1020 BLooper::_LockComplete(BLooper *looper, int32 old, thread_id this_tid,
1021 	sem_id sem, bigtime_t timeout)
1022 {
1023 	// What is this for?  Hope I'm not missing something conceptually here ...
1024 	return B_ERROR;
1025 }
1026 
1027 
1028 void
1029 BLooper::InitData()
1030 {
1031 	fOwner = B_ERROR;
1032 	fRunCalled = false;
1033 	fQueue = new BMessageQueue();
1034 	fCommonFilters = NULL;
1035 	fLastMessage = NULL;
1036 	fPreferred = NULL;
1037 	fTaskID = B_ERROR;
1038 	fTerminating = false;
1039 	fMsgPort = -1;
1040 
1041 	if (sTeamID == -1) {
1042 		thread_info info;
1043 		get_thread_info(find_thread(NULL), &info);
1044 		sTeamID = info.team;
1045 	}
1046 }
1047 
1048 
1049 void
1050 BLooper::InitData(const char *name, int32 priority, int32 portCapacity)
1051 {
1052 	InitData();
1053 
1054 	if (name == NULL)
1055 		name = "anonymous looper";
1056 
1057 	fLockSem = create_sem(1, name);
1058 
1059 	if (portCapacity <= 0)
1060 		portCapacity = B_LOOPER_PORT_DEFAULT_CAPACITY;
1061 
1062 	fMsgPort = create_port(portCapacity, name);
1063 
1064 	fInitPriority = priority;
1065 
1066 	BObjectLocker<BLooperList> ListLock(gLooperList);
1067 	AddLooper(this);
1068 	AddHandler(this);
1069 }
1070 
1071 
1072 void
1073 BLooper::AddMessage(BMessage* msg)
1074 {
1075 	_AddMessagePriv(msg);
1076 
1077 	// ToDo: if called from a different thread, we need to wake up the looper
1078 }
1079 
1080 
1081 void
1082 BLooper::_AddMessagePriv(BMessage* msg)
1083 {
1084 	// ToDo: if no target token is specified, set to preferred handler
1085 	// Others may want to peek into our message queue, so the preferred
1086 	// handler must be set correctly already if no token was given
1087 
1088 	fQueue->AddMessage(msg);
1089 }
1090 
1091 
1092 status_t
1093 BLooper::_task0_(void *arg)
1094 {
1095 	BLooper *looper = (BLooper *)arg;
1096 
1097 	PRINT(("LOOPER: _task0_()\n"));
1098 
1099 	if (looper->Lock()) {
1100 		PRINT(("LOOPER: looper locked\n"));
1101 		looper->task_looper();
1102 
1103 		delete looper;
1104 	}
1105 
1106 	PRINT(("LOOPER: _task0_() done: thread %ld\n", find_thread(NULL)));
1107 	return B_OK;
1108 }
1109 
1110 
1111 void *
1112 BLooper::ReadRawFromPort(int32 *msgCode, bigtime_t timeout)
1113 {
1114 	PRINT(("BLooper::ReadRawFromPort()\n"));
1115 	int8 *msgBuffer = NULL;
1116 	ssize_t bufferSize;
1117 
1118 	do {
1119 		bufferSize = port_buffer_size_etc(fMsgPort, B_RELATIVE_TIMEOUT, timeout);
1120 	} while (bufferSize == B_INTERRUPTED);
1121 
1122 	if (bufferSize < B_OK) {
1123 		PRINT(("BLooper::ReadRawFromPort(): failed: %ld\n", bufferSize));
1124 		return NULL;
1125 	}
1126 
1127 	if (bufferSize > 0)
1128 		msgBuffer = new int8[bufferSize];
1129 
1130 	// we don't want to wait again here, since that can only mean
1131 	// that someone else has read our message and our bufferSize
1132 	// is now probably wrong
1133 	PRINT(("read_port()...\n"));
1134 	bufferSize = read_port_etc(fMsgPort, msgCode, msgBuffer, bufferSize,
1135 					  B_RELATIVE_TIMEOUT, 0);
1136 	if (bufferSize < B_OK) {
1137 		delete[] msgBuffer;
1138 		return NULL;
1139 	}
1140 
1141 	PRINT(("BLooper::ReadRawFromPort() read: %.4s, %p\n", (char *)msgCode, msgBuffer));
1142 	return msgBuffer;
1143 }
1144 
1145 
1146 BMessage *
1147 BLooper::ReadMessageFromPort(bigtime_t tout)
1148 {
1149 	PRINT(("BLooper::ReadMessageFromPort()\n"));
1150 	int32 msgcode;
1151 	BMessage* bmsg;
1152 
1153 	void* msgbuffer = ReadRawFromPort(&msgcode, tout);
1154 	if (!msgbuffer)
1155 		return NULL;
1156 
1157 	bmsg = ConvertToMessage(msgbuffer, msgcode);
1158 
1159 	delete[] (int8*)msgbuffer;
1160 
1161 	PRINT(("BLooper::ReadMessageFromPort() done: %p\n", bmsg));
1162 	return bmsg;
1163 }
1164 
1165 
1166 BMessage*
1167 BLooper::ConvertToMessage(void* raw, int32 code)
1168 {
1169 	PRINT(("BLooper::ConvertToMessage()\n"));
1170 	BMessage* bmsg = new BMessage(code);
1171 
1172 	if (raw != NULL) {
1173 		if (bmsg->Unflatten((const char*)raw) != B_OK) {
1174 			PRINT(("BLooper::ConvertToMessage(): unflattening message failed\n"));
1175 			delete bmsg;
1176 			bmsg = NULL;
1177 		}
1178 	}
1179 
1180 	PRINT(("BLooper::ConvertToMessage(): %p\n", bmsg));
1181 	return bmsg;
1182 }
1183 
1184 
1185 void
1186 BLooper::task_looper()
1187 {
1188 	PRINT(("BLooper::task_looper()\n"));
1189 	// Check that looper is locked (should be)
1190 	AssertLocked();
1191 	// Unlock the looper
1192 	Unlock();
1193 
1194 	if (IsLocked())
1195 		debugger("looper must not be locked!");
1196 
1197 	// loop: As long as we are not terminating.
1198 	while (!fTerminating) {
1199 		PRINT(("LOOPER: outer loop\n"));
1200 		// TODO: timeout determination algo
1201 		//	Read from message port (how do we determine what the timeout is?)
1202 		PRINT(("LOOPER: MessageFromPort()...\n"));
1203 		BMessage *msg = MessageFromPort();
1204 		PRINT(("LOOPER: ...done\n"));
1205 
1206 		//	Did we get a message?
1207 		if (msg)
1208 			_AddMessagePriv(msg);
1209 
1210 		// Get message count from port
1211 		int32 msgCount = port_count(fMsgPort);
1212 		for (int32 i = 0; i < msgCount; ++i) {
1213 			// Read 'count' messages from port (so we will not block)
1214 			// We use zero as our timeout since we know there is stuff there
1215 			msg = MessageFromPort(0);
1216 			if (msg)
1217 				_AddMessagePriv(msg);
1218 		}
1219 
1220 		// loop: As long as there are messages in the queue and the port is
1221 		//		 empty... and we are not terminating, of course.
1222 		bool dispatchNextMessage = true;
1223 		while (!fTerminating && dispatchNextMessage) {
1224 			PRINT(("LOOPER: inner loop\n"));
1225 			// Get next message from queue (assign to fLastMessage)
1226 			fLastMessage = fQueue->NextMessage();
1227 
1228 			Lock();
1229 
1230 			if (!fLastMessage) {
1231 				// No more messages: Unlock the looper and terminate the
1232 				// dispatch loop.
1233 				dispatchNextMessage = false;
1234 			} else {
1235 				PRINT(("LOOPER: fLastMessage: 0x%lx: %.4s\n", fLastMessage->what,
1236 					(char*)&fLastMessage->what));
1237 				DBG(fLastMessage->PrintToStream());
1238 
1239 				// Get the target handler
1240 				BHandler *handler = NULL;
1241 				BMessage::Private messagePrivate(fLastMessage);
1242 				bool usePreferred = messagePrivate.UsePreferredTarget();
1243 
1244 				if (usePreferred) {
1245 					PRINT(("LOOPER: use preferred target\n"));
1246 					handler = fPreferred;
1247 					if (handler == NULL)
1248 						handler = this;
1249 				} else {
1250 					gDefaultTokens.GetToken(messagePrivate.GetTarget(),
1251 						B_HANDLER_TOKEN, (void **)&handler);
1252 
1253 					// if this handler doesn't belong to us, we drop the message
1254 					if (handler != NULL && handler->Looper() != this)
1255 						handler = NULL;
1256 
1257 					PRINT(("LOOPER: use %ld, handler: %p, this: %p\n",
1258 						messagePrivate.GetTarget(), handler, this));
1259 				}
1260 
1261 				// Is this a scripting message? (BMessage::HasSpecifiers())
1262 				if (handler != NULL && fLastMessage->HasSpecifiers()) {
1263 					int32 index = 0;
1264 					// Make sure the current specifier is kosher
1265 					if (fLastMessage->GetCurrentSpecifier(&index) == B_OK)
1266 						handler = resolve_specifier(handler, fLastMessage);
1267 				}
1268 
1269 				if (handler) {
1270 					// Do filtering
1271 					handler = _TopLevelFilter(fLastMessage, handler);
1272 					PRINT(("LOOPER: _TopLevelFilter(): %p\n", handler));
1273 					if (handler && handler->Looper() == this)
1274 						DispatchMessage(fLastMessage, handler);
1275 				}
1276 			}
1277 
1278 			if (fTerminating) {
1279 				// we leave the looper locked when we quit
1280 				return;
1281 			}
1282 
1283 			// Unlock the looper
1284 			Unlock();
1285 
1286 			// Delete the current message (fLastMessage)
1287 			if (fLastMessage) {
1288 				delete fLastMessage;
1289 				fLastMessage = NULL;
1290 			}
1291 
1292 			// Are any messages on the port?
1293 			if (port_count(fMsgPort) > 0) {
1294 				// Do outer loop
1295 				dispatchNextMessage = false;
1296 			}
1297 		}
1298 	}
1299 	PRINT(("BLooper::task_looper() done\n"));
1300 }
1301 
1302 
1303 void
1304 BLooper::_QuitRequested(BMessage *msg)
1305 {
1306 	bool isQuitting = QuitRequested();
1307 
1308 	// We send a reply to the sender, when they're waiting for a reply or
1309 	// if the request message contains a boolean "_shutdown_" field with value
1310 	// true. In the latter case the message came from the registrar, asking
1311 	// the application to shut down.
1312 	bool shutdown;
1313 	if (msg->IsSourceWaiting()
1314 		|| (msg->FindBool("_shutdown_", &shutdown) == B_OK && shutdown)) {
1315 		BMessage ReplyMsg(B_REPLY);
1316 		ReplyMsg.AddBool("result", isQuitting);
1317 		ReplyMsg.AddInt32("thread", fTaskID);
1318 		msg->SendReply(&ReplyMsg);
1319 	}
1320 
1321 	if (isQuitting)
1322 		Quit();
1323 }
1324 
1325 
1326 bool
1327 BLooper::AssertLocked() const
1328 {
1329 	if (!IsLocked()) {
1330 		debugger("looper must be locked before proceeding\n");
1331 		return false;
1332 	}
1333 
1334 	return true;
1335 }
1336 
1337 
1338 BHandler *
1339 BLooper::_TopLevelFilter(BMessage* msg, BHandler* target)
1340 {
1341 	if (msg) {
1342 		// Apply the common filters first
1343 		target = _ApplyFilters(CommonFilterList(), msg, target);
1344 		if (target) {
1345 			if (target->Looper() != this) {
1346 				debugger("Targeted handler does not belong to the looper.");
1347 				target = NULL;
1348 			} else {
1349 				// Now apply handler-specific filters
1350 				target = _HandlerFilter(msg, target);
1351 			}
1352 		}
1353 	}
1354 
1355 	return target;
1356 }
1357 
1358 
1359 BHandler *
1360 BLooper::_HandlerFilter(BMessage* msg, BHandler* target)
1361 {
1362 	// Keep running filters until our handler is NULL, or until the filtering
1363 	// handler returns itself as the designated handler
1364 	BHandler* previousTarget = NULL;
1365 	while (target != NULL && target != previousTarget) {
1366 		previousTarget = target;
1367 
1368 		target = _ApplyFilters(target->FilterList(), msg, target);
1369 		if (target != NULL && target->Looper() != this) {
1370 			debugger("Targeted handler does not belong to the looper.");
1371 			target = NULL;
1372 		}
1373 	}
1374 
1375 	return target;
1376 }
1377 
1378 
1379 BHandler *
1380 BLooper::_ApplyFilters(BList* list, BMessage* msg, BHandler* target)
1381 {
1382 	// This is where the action is!
1383 	// Check the parameters
1384 	if (!list || !msg)
1385 		return target;
1386 
1387 	// For each filter in the provided list
1388 	BMessageFilter* filter = NULL;
1389 	for (int32 i = 0; i < list->CountItems(); ++i) {
1390 		filter = (BMessageFilter*)list->ItemAt(i);
1391 
1392 		// Check command conditions
1393 		if (filter->FiltersAnyCommand() || (filter->Command() == msg->what)) {
1394 			// Check delivery conditions
1395 			message_delivery delivery = filter->MessageDelivery();
1396 			bool dropped = msg->WasDropped();
1397 			if (delivery == B_ANY_DELIVERY
1398 				|| (delivery == B_DROPPED_DELIVERY && dropped)
1399 				|| (delivery == B_PROGRAMMED_DELIVERY && !dropped)) {
1400 				// Check source conditions
1401 				message_source source = filter->MessageSource();
1402 				bool remote = msg->IsSourceRemote();
1403 				if (source == B_ANY_SOURCE
1404 					|| (source == B_REMOTE_SOURCE && remote)
1405 					|| (source == B_LOCAL_SOURCE && !remote)) {
1406 					// Are we using an "external" function?
1407 					filter_result result;
1408 					filter_hook func = filter->FilterFunction();
1409 					if (func)
1410 						result = func(msg, &target, filter);
1411 					else
1412 						result = filter->Filter(msg, &target);
1413 
1414 					// Is further processing allowed?
1415 					if (result == B_SKIP_MESSAGE) {
1416 						// No; time to bail out
1417 						return NULL;
1418 					}
1419 				}
1420 			}
1421 		}
1422 	}
1423 
1424 	return target;
1425 }
1426 
1427 
1428 void
1429 BLooper::check_lock()
1430 {
1431 	// This is a cheap variant of AssertLocked()
1432 	// It is used in situations where it's clear that the looper is valid,
1433 	// ie. from handlers
1434 	if (fOwner == -1 || fOwner != find_thread(NULL))
1435 		debugger("Looper must be locked.");
1436 }
1437 
1438 
1439 BHandler *
1440 BLooper::resolve_specifier(BHandler* target, BMessage* msg)
1441 {
1442 	// Check params
1443 	if (!target || !msg)
1444 		return NULL;
1445 
1446 	int32 index;
1447 	BMessage specifier;
1448 	int32 form;
1449 	const char* property;
1450 	status_t err = B_OK;
1451 	BHandler* newTarget = target;
1452 	//	Loop to deal with nested specifiers
1453 	//	(e.g., the 3rd button on the 4th view)
1454 	do {
1455 		err = msg->GetCurrentSpecifier(&index, &specifier, &form, &property);
1456 		if (err) {
1457 			BMessage reply(B_REPLY);
1458 			reply.AddInt32("error", err);
1459 			msg->SendReply(&reply);
1460 			return NULL;
1461 		}
1462 		//	Current target gets what was the new target
1463 		target = newTarget;
1464 		newTarget = target->ResolveSpecifier(msg, index, &specifier, form,
1465 			property);
1466 		//	Check that new target is owned by looper;
1467 		//	use IndexOf() to avoid dereferencing newTarget
1468 		//	(possible race condition with object destruction
1469 		//	by another looper)
1470 		if (!newTarget || IndexOf(newTarget) < 0)
1471 			return NULL;
1472 
1473 		//	Get current specifier index (may change in ResolveSpecifier())
1474 		msg->GetCurrentSpecifier(&index);
1475 	} while (newTarget && newTarget != target && index >= 0);
1476 
1477 	return newTarget;
1478 }
1479 
1480 
1481 void
1482 BLooper::UnlockFully()
1483 {
1484 	AssertLocked();
1485 
1486 /**
1487 	@note	What we're doing here is completely undoing the current owner's lock
1488 			on the looper.  This is actually pretty easy, since the owner only
1489 			has a single aquisition on the semaphore; every subsequent "lock"
1490 			is just an increment to the owner count.  The whole thing is quite
1491 			similar to Unlock(), except that we clear the ownership variables,
1492 			rather than merely decrementing them.
1493  */
1494 	// Clear the owner count
1495 	fOwnerCount = 0;
1496 	// Nobody owns the lock now
1497 	fOwner = -1;
1498 	// There is now one less thread holding a lock on this looper
1499 // bonefish: Currently _Lock() always acquires the semaphore.
1500 /*	long atomicCount = */atomic_add(&fAtomicCount, -1);
1501 //	if (atomicCount > 0)
1502 	{
1503 		release_sem(fLockSem);
1504 	}
1505 }
1506 
1507 
1508 void
1509 BLooper::AddLooper(BLooper *looper)
1510 {
1511 	if (gLooperList.IsLocked())
1512 		gLooperList.AddLooper(looper);
1513 }
1514 
1515 
1516 bool
1517 BLooper::IsLooperValid(const BLooper *looper)
1518 {
1519 	if (gLooperList.IsLocked())
1520 		return gLooperList.IsLooperValid(looper);
1521 
1522 	return false;
1523 }
1524 
1525 
1526 void
1527 BLooper::RemoveLooper(BLooper *looper)
1528 {
1529 	if (gLooperList.IsLocked())
1530 		gLooperList.RemoveLooper(looper);
1531 }
1532 
1533 
1534 void
1535 BLooper::GetLooperList(BList* list)
1536 {
1537 	BObjectLocker<BLooperList> ListLock(gLooperList);
1538 	if (ListLock.IsLocked())
1539 		gLooperList.GetLooperList(list);
1540 }
1541 
1542 
1543 BLooper *
1544 BLooper::LooperForName(const char* name)
1545 {
1546 	if (gLooperList.IsLocked())
1547 		return gLooperList.LooperForName(name);
1548 
1549 	return NULL;
1550 }
1551 
1552 
1553 BLooper *
1554 BLooper::LooperForPort(port_id port)
1555 {
1556 	if (gLooperList.IsLocked())
1557 		return gLooperList.LooperForPort(port);
1558 
1559 	return NULL;
1560 }
1561 
1562 
1563 //	#pragma mark -
1564 
1565 
1566 port_id
1567 _get_looper_port_(const BLooper *looper)
1568 {
1569 	return looper->fMsgPort;
1570 }
1571 
1572