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