xref: /haiku/src/kits/app/Looper.cpp (revision 687d231c5fab1e917f93e3b63b48b7b0e97882e3)
1 //------------------------------------------------------------------------------
2 //	Copyright (c) 2001-2005, Haiku
3 //
4 //	Permission is hereby granted, free of charge, to any person obtaining a
5 //	copy of this software and associated documentation files (the "Software"),
6 //	to deal in the Software without restriction, including without limitation
7 //	the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 //	and/or sell copies of the Software, and to permit persons to whom the
9 //	Software is furnished to do so, subject to the following conditions:
10 //
11 //	The above copyright notice and this permission notice shall be included in
12 //	all copies or substantial portions of the Software.
13 //
14 //	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 //	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 //	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 //	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 //	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19 //	FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
20 //	DEALINGS IN THE SOFTWARE.
21 //
22 //	File Name:		Looper.cpp
23 //	Author(s):		Erik Jaesler (erik@cgsoftware.com)
24 //					DarkWyrm (bpmagic@columbus.rr.com)
25 //	Description:	BLooper class spawns a thread that runs a message loop.
26 //------------------------------------------------------------------------------
27 
28 /**
29 	@note	Although I'm implementing "by the book" for now, I would like to
30 			refactor sLooperList and all of the functions that operate on it
31 			into their own class in the BPrivate namespace.
32 
33 			Also considering adding the thread priority when archiving.
34 
35 
36  */
37 
38 // debugging
39 //#define DBG(x) x
40 #define DBG(x)	;
41 #define PRINT(x)	DBG({ printf("[%6ld] ", find_thread(NULL)); printf x; })
42 
43 /*
44 #include <Autolock.h>
45 #include <Locker.h>
46 static BLocker sDebugPrintLocker("BLooper debug print");
47 #define PRINT(x)	DBG({						\
48 	BAutolock _(sDebugPrintLocker);				\
49 	debug_printf("[%6ld] ", find_thread(NULL));	\
50 	debug_printf x;								\
51 })
52 */
53 
54 #include <stdio.h>
55 
56 #include <Autolock.h>
57 #include <Looper.h>
58 #include <Message.h>
59 #include <MessageFilter.h>
60 #include <MessageQueue.h>
61 #include <Messenger.h>
62 #include <PropertyInfo.h>
63 
64 #include <LooperList.h>
65 #include <ObjectLocker.h>
66 #include <TokenSpace.h>
67 
68 
69 #define FILTER_LIST_BLOCK_SIZE	5
70 #define DATA_BLOCK_SIZE			5
71 
72 // Globals ---------------------------------------------------------------------
73 using BPrivate::gDefaultTokens;
74 using BPrivate::gLooperList;
75 using BPrivate::BObjectLocker;
76 using BPrivate::BLooperList;
77 
78 port_id _get_looper_port_(const BLooper* looper);
79 bool _use_preferred_target_(BMessage* msg) { return msg->fPreferred; }
80 int32 _get_message_target_(BMessage* msg) { return msg->fTarget; }
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 		BList* filters = handler->FilterList();
383 		if (filters) {
384 			for (int32 i = 0; i < filters->CountItems(); ++i) {
385 				((BMessageFilter*)filters->ItemAt(i))->SetLooper(this);
386 			}
387 		}
388 	}
389 }
390 
391 
392 bool
393 BLooper::RemoveHandler(BHandler* handler)
394 {
395 	if (handler == NULL)
396 		return false;
397 
398 	AssertLocked();
399 
400 	if (handler->Looper() == this && fHandlers.RemoveItem(handler)) {
401 		if (handler == fPreferred)
402 			fPreferred = NULL;
403 
404 		handler->SetNextHandler(NULL);
405 		handler->SetLooper(NULL);
406 		return true;
407 	}
408 
409 	return false;
410 }
411 
412 
413 int32
414 BLooper::CountHandlers() const
415 {
416 	AssertLocked();
417 
418 	return fHandlers.CountItems();
419 }
420 
421 
422 BHandler*
423 BLooper::HandlerAt(int32 index) const
424 {
425 	AssertLocked();
426 
427 	return (BHandler*)fHandlers.ItemAt(index);
428 }
429 
430 
431 int32
432 BLooper::IndexOf(BHandler* handler) const
433 {
434 	AssertLocked();
435 
436 	return fHandlers.IndexOf(handler);
437 }
438 
439 
440 BHandler*
441 BLooper::PreferredHandler() const
442 {
443 	return fPreferred;
444 }
445 
446 
447 void
448 BLooper::SetPreferredHandler(BHandler* handler)
449 {
450 	if (handler && handler->Looper() == this && IndexOf(handler) >= 0) {
451 		fPreferred = handler;
452 	} else {
453 		fPreferred = NULL;
454 	}
455 }
456 
457 
458 thread_id
459 BLooper::Run()
460 {
461 	AssertLocked();
462 
463 	if (fRunCalled) {
464 		// Not allowed to call Run() more than once
465 		debugger("can't call BLooper::Run twice!");
466 	}
467 
468 	fTaskID = spawn_thread(_task0_, Name(), fInitPriority, this);
469 	if (fTaskID < B_OK)
470 		return fTaskID;
471 
472 	if (fMsgPort < B_OK)
473 		return fMsgPort;
474 
475 	fRunCalled = true;
476 	Unlock();
477 
478 	status_t err = resume_thread(fTaskID);
479 	if (err < B_OK)
480 		return err;
481 
482 	return fTaskID;
483 }
484 
485 
486 void
487 BLooper::Quit()
488 {
489 	PRINT(("BLooper::Quit()\n"));
490 
491 	if (!IsLocked()) {
492 		printf("ERROR - you must Lock a looper before calling Quit(), "
493 			   "team=%ld, looper=%s\n", Team(), Name() ? Name() : "unnamed");
494 	}
495 
496 	// Try to lock
497 	if (!Lock()) {
498 		// We're toast already
499 		return;
500 	}
501 
502 	PRINT(("  is locked\n"));
503 
504 	if (!fRunCalled) {
505 		PRINT(("  Run() has not been called yet\n"));
506 		fTerminating = true;
507 		delete this;
508 	} else if (find_thread(NULL) == fTaskID) {
509 		PRINT(("  We are the looper thread\n"));
510 		fTerminating = true;
511 		delete this;
512 		exit_thread(0);
513 	} else {
514 		PRINT(("  Run() has already been called and we are not the looper thread\n"));
515 
516 		// As with sem in _Lock(), we need to cache this here in case the looper
517 		// disappears before we get to the wait_for_thread() below
518 		thread_id tid = Thread();
519 
520 		// We need to unlock here. Otherwise the looper thread can't
521 		// dispatch the _QUIT_ message we're going to post.
522 		UnlockFully();
523 
524 		// As per the BeBook, if we've been called by a thread other than
525 		// our own, the rest of the message queue has to get processed.  So
526 		// we put this in the queue, and when it shows up, we'll call Quit()
527 		// from our own thread.
528 		// A little testing with BMessageFilter shows _QUIT_ is being used here.
529 		// I got suspicious when my test QuitRequested() wasn't getting called
530 		// when Quit() was invoked from another thread.  Makes a nice proof that
531 		// this is how it's handled, too.
532 
533 		while (PostMessage(_QUIT_) == B_WOULD_BLOCK) {
534 			// There's a slight chance that PostMessage() will return B_WOULD_BLOCK
535 			// because the port is full, so we'll wait a bit and re-post until
536 			// we won't block.
537 			snooze(25000);
538 		}
539 
540 		// We have to wait until the looper is done processing any remaining messages.
541 		int32 temp;
542 		while (wait_for_thread(tid, &temp) == B_INTERRUPTED)
543 			;
544 	}
545 
546 	PRINT(("BLooper::Quit() done\n"));
547 }
548 
549 
550 bool
551 BLooper::QuitRequested()
552 {
553 	return true;
554 }
555 
556 
557 bool
558 BLooper::Lock()
559 {
560 	// Defer to global _Lock(); see notes there
561 	return _Lock(this, -1, B_INFINITE_TIMEOUT) == B_OK;
562 }
563 
564 
565 void
566 BLooper::Unlock()
567 {
568 PRINT(("BLooper::Unlock()\n"));
569 	//	Make sure we're locked to begin with
570 	AssertLocked();
571 
572 	//	Decrement fOwnerCount
573 	--fOwnerCount;
574 PRINT(("  fOwnerCount now: %ld\n", fOwnerCount));
575 	//	Check to see if the owner still wants a lock
576 	if (fOwnerCount == 0) {
577 		//	Set fOwner to invalid thread_id (< 0)
578 		fOwner = -1;
579 
580 		//	Decrement requested lock count (using fAtomicCount for this)
581 /*		int32 atomicCount =*/ atomic_add(&fAtomicCount, -1);
582 PRINT(("  fAtomicCount now: %ld\n", fAtomicCount));
583 
584 		//	Check if anyone is waiting for a lock
585 // bonefish: Currently _Lock() always acquires the semaphore.
586 //		if (atomicCount > 0)
587 		{
588 			//	release the lock
589 			release_sem(fLockSem);
590 		}
591 	}
592 PRINT(("BLooper::Unlock() done\n"));
593 }
594 
595 
596 bool
597 BLooper::IsLocked() const
598 {
599 	// We have to lock the list for the call to IsLooperValid().  Has the side
600 	// effect of not letting the looper get deleted while we're here.
601 	BObjectLocker<BLooperList> ListLock(gLooperList);
602 
603 	if (!ListLock.IsLocked()) {
604 		// If we can't lock the list, our semaphore is probably toast
605 		return false;
606 	}
607 
608 	if (!IsLooperValid(this)) {
609 		// The looper is gone, so of course it's not locked
610 		return false;
611 	}
612 
613 	// Got this from Jeremy's BLocker implementation
614 	return find_thread(NULL) == fOwner;
615 }
616 
617 
618 status_t
619 BLooper::LockWithTimeout(bigtime_t timeout)
620 {
621 	return _Lock(this, -1, timeout);
622 }
623 
624 
625 thread_id
626 BLooper::Thread() const
627 {
628 	return fTaskID;
629 }
630 
631 
632 team_id
633 BLooper::Team() const
634 {
635 	return sTeamID;
636 }
637 
638 
639 BLooper*
640 BLooper::LooperForThread(thread_id tid)
641 {
642 	BObjectLocker<BLooperList> ListLock(gLooperList);
643 	if (ListLock.IsLocked())
644 		return gLooperList.LooperForThread(tid);
645 
646 	return NULL;
647 }
648 
649 
650 thread_id
651 BLooper::LockingThread() const
652 {
653 	return fOwner;
654 }
655 
656 
657 int32
658 BLooper::CountLocks() const
659 {
660 	return fOwnerCount;
661 }
662 
663 
664 int32
665 BLooper::CountLockRequests() const
666 {
667 	return fAtomicCount;
668 }
669 
670 
671 sem_id
672 BLooper::Sem() const
673 {
674 	return fLockSem;
675 }
676 
677 
678 BHandler*
679 BLooper::ResolveSpecifier(BMessage* msg, int32 index,
680 	BMessage* specifier, int32 form, const char* property)
681 {
682 /**
683 	@note	When I was first dumping the results of GetSupportedSuites() from
684 			various classes, the use of the extra_data field was quite
685 			mysterious to me.  Then I dumped BApplication and compared the
686 			result against the BeBook's docs for scripting BApplication.  A
687 			bunch of it isn't documented, but what is tipped me to the idea
688 			that the extra_data is being used as a quick and dirty way to tell
689 			what scripting "command" has been sent, e.g., for easy use in a
690 			switch statement.  Would certainly be a lot faster than a bunch of
691 			string comparisons -- which wouldn't tell the whole story anyway,
692 			because of the same name being used for multiple properties.
693  */
694 	BPropertyInfo PropertyInfo(gLooperPropInfo);
695 	uint32 data;
696 	status_t err = B_OK;
697 	const char* errMsg = "";
698 	if (PropertyInfo.FindMatch(msg, index, specifier, form, property, &data) >= 0) {
699 		switch (data) {
700 			case BLOOPER_PROCESS_INTERNALLY:
701 				return this;
702 
703 			case BLOOPER_HANDLER_BY_INDEX:
704 			{
705 				int32 index = specifier->FindInt32("index");
706 				if (form == B_REVERSE_INDEX_SPECIFIER) {
707 					index = CountHandlers() - index;
708 				}
709 				BHandler* target = HandlerAt(index);
710 				if (target) {
711 					// Specifier has been fully handled
712 					msg->PopSpecifier();
713 					return target;
714 				} else {
715 					err = B_BAD_INDEX;
716 					errMsg = "handler index out of range";
717 				}
718 				break;
719 			}
720 
721 			default:
722 				err = B_BAD_SCRIPT_SYNTAX;
723 				errMsg = "Didn't understand the specifier(s)";
724 				break;
725 		}
726 	} else {
727 		return BHandler::ResolveSpecifier(msg, index, specifier, form,
728 			property);
729 	}
730 
731 	BMessage Reply(B_MESSAGE_NOT_UNDERSTOOD);
732 	Reply.AddInt32("error", err);
733 	Reply.AddString("message", errMsg);
734 	msg->SendReply(&Reply);
735 
736 	return NULL;
737 }
738 
739 
740 status_t
741 BLooper::GetSupportedSuites(BMessage* data)
742 {
743 	if (data == NULL)
744 		return B_BAD_VALUE;
745 
746 	status_t status = data->AddString("Suites", "suite/vnd.Be-handler");
747 	if (status == B_OK) {
748 		BPropertyInfo PropertyInfo(gLooperPropInfo);
749 		status = data->AddFlat("message", &PropertyInfo);
750 		if (status == B_OK)
751 			status = BHandler::GetSupportedSuites(data);
752 	}
753 
754 	return status;
755 }
756 
757 
758 void
759 BLooper::AddCommonFilter(BMessageFilter* filter)
760 {
761 	if (!filter)
762 		return;
763 
764 	AssertLocked();
765 
766 	if (filter->Looper()) {
767 		debugger("A MessageFilter can only be used once.");
768 		return;
769 	}
770 
771 	if (!fCommonFilters)
772 		fCommonFilters = new BList(FILTER_LIST_BLOCK_SIZE);
773 
774 	filter->SetLooper(this);
775 	fCommonFilters->AddItem(filter);
776 }
777 
778 
779 bool
780 BLooper::RemoveCommonFilter(BMessageFilter* filter)
781 {
782 	AssertLocked();
783 
784 	if (!fCommonFilters)
785 		return false;
786 
787 	bool result = fCommonFilters->RemoveItem(filter);
788 	if (result)
789 		filter->SetLooper(NULL);
790 
791 	return result;
792 }
793 
794 
795 void
796 BLooper::SetCommonFilterList(BList* filters)
797 {
798 	// We have a somewhat serious problem here.  It is entirely possible in R5
799 	// to assign a given list of filters to *two* BLoopers simultaneously.  This
800 	// becomes problematic when the loopers are destroyed: the last looper
801 	// destroyed will have a problem when it tries to delete a filter list that
802 	// has already been deleted.  In R5, this results in a general protection
803 	// fault.  We fix this by checking the filter list for ownership issues.
804 
805 	AssertLocked();
806 
807 	BMessageFilter* filter;
808 	if (filters) {
809 		// Check for ownership issues
810 		for (int32 i = 0; i < filters->CountItems(); ++i) {
811 			filter = (BMessageFilter*)filters->ItemAt(i);
812 			if (filter->Looper()) {
813 				debugger("A MessageFilter can only be used once.");
814 				return;
815 			}
816 		}
817 	}
818 
819 	if (fCommonFilters) {
820 		for (int32 i = 0; i < fCommonFilters->CountItems(); ++i) {
821 			delete (BMessageFilter*)fCommonFilters->ItemAt(i);
822 		}
823 
824 		delete fCommonFilters;
825 		fCommonFilters = NULL;
826 	}
827 
828 	// Per the BeBook, we take ownership of the list
829 	fCommonFilters = filters;
830 	if (fCommonFilters) {
831 		for (int32 i = 0; i < fCommonFilters->CountItems(); ++i) {
832 			filter = (BMessageFilter*)fCommonFilters->ItemAt(i);
833 			filter->SetLooper(this);
834 		}
835 	}
836 }
837 
838 
839 BList*
840 BLooper::CommonFilterList() const
841 {
842 	return fCommonFilters;
843 }
844 
845 
846 status_t
847 BLooper::Perform(perform_code d, void* arg)
848 {
849 	// This is sort of what we're doing for this function everywhere
850 	return BHandler::Perform(d, arg);
851 }
852 
853 
854 BMessage*
855 BLooper::MessageFromPort(bigtime_t timeout)
856 {
857 	return ReadMessageFromPort(timeout);
858 }
859 
860 
861 void BLooper::_ReservedLooper1() {}
862 void BLooper::_ReservedLooper2() {}
863 void BLooper::_ReservedLooper3() {}
864 void BLooper::_ReservedLooper4() {}
865 void BLooper::_ReservedLooper5() {}
866 void BLooper::_ReservedLooper6() {}
867 
868 
869 BLooper::BLooper(const BLooper&)
870 {
871 	// Copy construction not allowed
872 }
873 
874 
875 BLooper& BLooper::operator=(const BLooper& )
876 {
877 	// Looper copying not allowed
878 	return *this;
879 }
880 
881 
882 BLooper::BLooper(int32 priority, port_id port, const char* name)
883 {
884 	// This must be a legacy constructor
885 	fMsgPort = port;
886 	InitData(name, priority, B_LOOPER_PORT_DEFAULT_CAPACITY);
887 }
888 
889 
890 status_t
891 BLooper::_PostMessage(BMessage *msg, BHandler *handler,
892 	BHandler *replyTo)
893 {
894 	BObjectLocker<BLooperList> listLocker(gLooperList);
895 	if (!listLocker.IsLocked())
896 		return B_ERROR;
897 
898 	if (!IsLooperValid(this))
899 		return B_BAD_VALUE;
900 
901 	// Does handler belong to this looper?
902 	if (handler && handler->Looper() != this)
903 		return B_MISMATCHED_VALUES;
904 
905 	status_t status;
906 	BMessenger messenger(handler, this, &status);
907 	if (status == B_OK)
908 		status = messenger.SendMessage(msg, replyTo, 0);
909 
910 	return status;
911 }
912 
913 
914 status_t
915 BLooper::_Lock(BLooper* loop, port_id port, bigtime_t timeout)
916 {
917 PRINT(("BLooper::_Lock(%p, %lx)\n", loop, port));
918 /**
919 	@note	The assumption I'm under here is that since we can get the port of
920 			the BLooper directly from the BLooper itself, the port parameter is
921 			for identifying BLoopers by port_id when a pointer to the BLooper in
922 			question is not available.  So this function has two modes:
923 				o When loop != NULL, use it directly
924 				o When loop == NULL and port is valid, use the port_id to get
925 				  the looper
926 			I scoured the docs to find out what constitutes a valid port_id to
927 			no avail.  Since create_port uses the standard error values in its
928 			returned port_id, I'll assume that anything less than zero is a safe
929 			bet as an *invalid* port_id.  I'm guessing that, like thread and
930 			semaphore ids, anything >= zero is valid.  So, the short version of
931 			this reads: if you don't want to find by port_id, make port = -1.
932 
933 			Another assumption I'm making is that Lock() and LockWithTimeout()
934 			are covers for this function.  If it turns out that we don't really
935 			need this function, I may refactor this code into LockWithTimeout()
936 			and have Lock() call it instead.  This function could then be
937 			removed.
938  */
939 
940 	//	Check params (loop, port)
941 	if (!loop && port < 0)
942 	{
943 PRINT(("BLooper::_Lock() done 1\n"));
944 		return B_BAD_VALUE;
945 	}
946 
947 	// forward declared so I can use BAutolock on sLooperListLock
948 	thread_id curThread;
949 	sem_id sem;
950 
951 /**
952 	@note	We lock the looper list at the start of the lock operation to
953 			prevent the looper getting removed from the list while we're
954 			doing list operations.  Also ensures that the looper doesn't
955 			get deleted here (since ~BLooper() has to lock the list as
956 			well to remove itself).
957  */
958 	{
959 		BObjectLocker<BLooperList> ListLock(gLooperList);
960 		if (!ListLock.IsLocked())
961 		{
962 			// If we can't lock, the semaphore is probably
963 			// gone, which leaves us in no-man's land
964 PRINT(("BLooper::_Lock() done 2\n"));
965 			return B_BAD_VALUE;
966 		}
967 
968 		//	Look up looper by port_id, if necessary
969 		if (!loop)
970 		{
971 			loop = LooperForPort(port);
972 			if (!loop)
973 			{
974 PRINT(("BLooper::_Lock() done 3\n"));
975 				return B_BAD_VALUE;
976 			}
977 		}
978 		else
979 		{
980 			//	Check looper validity
981 			if (!IsLooperValid(loop))
982 			{
983 PRINT(("BLooper::_Lock() done 4\n"));
984 				return B_BAD_VALUE;
985 			}
986 		}
987 
988 		//	Is the looper trying to lock itself?
989 		//	Check for nested lock attempt
990 		curThread = find_thread(NULL);
991 		if (curThread == loop->fOwner)
992 		{
993 			//	Bump fOwnerCount
994 			++loop->fOwnerCount;
995 PRINT(("BLooper::_Lock() done 5: fOwnerCount: %ld\n", loop->fOwnerCount));
996 			return B_OK;
997 		}
998 
999 		//	Something external to the looper is attempting to lock
1000 		//	Cache the semaphore
1001 		sem = loop->fLockSem;
1002 
1003 		//	Validate the semaphore
1004 		if (sem < 0)
1005 		{
1006 PRINT(("BLooper::_Lock() done 6\n"));
1007 			return B_BAD_VALUE;
1008 		}
1009 
1010 		//	Bump the requested lock count (using fAtomicCount for this)
1011 		atomic_add(&loop->fAtomicCount, 1);
1012 
1013 		// sLooperListLock automatically released here
1014 	}
1015 
1016 /**
1017 	@note	We have to operate with the looper list unlocked during semaphore
1018 			acquisition so that the rest of the application doesn't have to
1019 			wait for this lock to happen.  This is why we cached fLockSem
1020 			earlier -- with the list unlocked, the looper might get deleted
1021 			right out from under us.  This is also why we use a raw semaphore
1022 			instead of the easier-to-deal-with BLocker; you can't cache a
1023 			BLocker.
1024  */
1025 	//	acquire the lock
1026 	status_t err;
1027 	do
1028 	{
1029 		err = acquire_sem_etc(sem, 1, B_RELATIVE_TIMEOUT, timeout);
1030 	} while (err == B_INTERRUPTED);
1031 
1032 	if (!err)
1033 	{
1034 		//		Assign current thread to fOwner
1035 		loop->fOwner = curThread;
1036 		//		Reset fOwnerCount to 1
1037 		loop->fOwnerCount = 1;
1038 	}
1039 
1040 	PRINT(("BLooper::_Lock() done: %lx\n", err));
1041 	return err;
1042 }
1043 
1044 
1045 status_t
1046 BLooper::_LockComplete(BLooper *looper, int32 old, thread_id this_tid,
1047 	sem_id sem, bigtime_t timeout)
1048 {
1049 	// What is this for?  Hope I'm not missing something conceptually here ...
1050 	return B_ERROR;
1051 }
1052 
1053 
1054 void
1055 BLooper::InitData()
1056 {
1057 	fOwner = B_ERROR;
1058 	fRunCalled = false;
1059 	fQueue = new BMessageQueue();
1060 	fCommonFilters = NULL;
1061 	fLastMessage = NULL;
1062 	fPreferred = NULL;
1063 	fTaskID = B_ERROR;
1064 	fTerminating = false;
1065 	fMsgPort = -1;
1066 
1067 	if (sTeamID == -1) {
1068 		thread_info info;
1069 		get_thread_info(find_thread(NULL), &info);
1070 		sTeamID = info.team;
1071 	}
1072 }
1073 
1074 
1075 void
1076 BLooper::InitData(const char *name, int32 priority, int32 portCapacity)
1077 {
1078 	InitData();
1079 
1080 	if (name == NULL)
1081 		name = "anonymous looper";
1082 
1083 	fLockSem = create_sem(1, name);
1084 
1085 	if (portCapacity <= 0)
1086 		portCapacity = B_LOOPER_PORT_DEFAULT_CAPACITY;
1087 
1088 	fMsgPort = create_port(portCapacity, name);
1089 
1090 	fInitPriority = priority;
1091 
1092 	BObjectLocker<BLooperList> ListLock(gLooperList);
1093 	AddLooper(this);
1094 	AddHandler(this);
1095 }
1096 
1097 
1098 void
1099 BLooper::AddMessage(BMessage* msg)
1100 {
1101 	_AddMessagePriv(msg);
1102 
1103 	// ToDo: if called from a different thread, we need to wake up the looper
1104 }
1105 
1106 
1107 void
1108 BLooper::_AddMessagePriv(BMessage* msg)
1109 {
1110 	// ToDo: if no target token is specified, set to preferred handler
1111 	// Others may want to peek into our message queue, so the preferred
1112 	// handler must be set correctly already if no token was given
1113 
1114 	fQueue->AddMessage(msg);
1115 }
1116 
1117 
1118 status_t
1119 BLooper::_task0_(void *arg)
1120 {
1121 	BLooper *looper = (BLooper *)arg;
1122 
1123 	PRINT(("LOOPER: _task0_()\n"));
1124 
1125 	if (looper->Lock()) {
1126 		PRINT(("LOOPER: looper locked\n"));
1127 		looper->task_looper();
1128 
1129 		delete looper;
1130 	}
1131 
1132 	PRINT(("LOOPER: _task0_() done: thread %ld\n", find_thread(NULL)));
1133 	return B_OK;
1134 }
1135 
1136 
1137 void *
1138 BLooper::ReadRawFromPort(int32 *msgCode, bigtime_t timeout)
1139 {
1140 	PRINT(("BLooper::ReadRawFromPort()\n"));
1141 	int8 *msgBuffer = NULL;
1142 	ssize_t bufferSize;
1143 
1144 	do {
1145 		bufferSize = port_buffer_size_etc(fMsgPort, B_RELATIVE_TIMEOUT, timeout);
1146 	} while (bufferSize == B_INTERRUPTED);
1147 
1148 	if (bufferSize < B_OK) {
1149 		PRINT(("BLooper::ReadRawFromPort(): failed: %ld\n", bufferSize));
1150 		return NULL;
1151 	}
1152 
1153 	if (bufferSize > 0)
1154 		msgBuffer = new int8[bufferSize];
1155 
1156 	// we don't want to wait again here, since that can only mean
1157 	// that someone else has read our message and our bufferSize
1158 	// is now probably wrong
1159 	PRINT(("read_port()...\n"));
1160 	bufferSize = read_port_etc(fMsgPort, msgCode, msgBuffer, bufferSize,
1161 					  B_RELATIVE_TIMEOUT, 0);
1162 	if (bufferSize < B_OK) {
1163 		delete[] msgBuffer;
1164 		return NULL;
1165 	}
1166 
1167 	PRINT(("BLooper::ReadRawFromPort() read: %.4s, %p\n", (char *)msgCode, msgBuffer));
1168 	return msgBuffer;
1169 }
1170 
1171 
1172 BMessage *
1173 BLooper::ReadMessageFromPort(bigtime_t tout)
1174 {
1175 	PRINT(("BLooper::ReadMessageFromPort()\n"));
1176 	int32 msgcode;
1177 	BMessage* bmsg;
1178 
1179 	void* msgbuffer = ReadRawFromPort(&msgcode, tout);
1180 	if (!msgbuffer)
1181 		return NULL;
1182 
1183 	bmsg = ConvertToMessage(msgbuffer, msgcode);
1184 
1185 	delete[] (int8*)msgbuffer;
1186 
1187 PRINT(("BLooper::ReadMessageFromPort() done: %p\n", bmsg));
1188 	return bmsg;
1189 }
1190 //------------------------------------------------------------------------------
1191 BMessage* BLooper::ConvertToMessage(void* raw, int32 code)
1192 {
1193 PRINT(("BLooper::ConvertToMessage()\n"));
1194 	BMessage* bmsg = new BMessage(code);
1195 
1196 	if (raw != NULL)
1197 	{
1198 		if (bmsg->Unflatten((const char*)raw) != B_OK)
1199 		{
1200 PRINT(("BLooper::ConvertToMessage(): unflattening message failed\n"));
1201 			delete bmsg;
1202 			bmsg = NULL;
1203 		}
1204 	}
1205 
1206 PRINT(("BLooper::ConvertToMessage(): %p\n", bmsg));
1207 	return bmsg;
1208 }
1209 
1210 
1211 void
1212 BLooper::task_looper()
1213 {
1214 	PRINT(("BLooper::task_looper()\n"));
1215 	//	Check that looper is locked (should be)
1216 	AssertLocked();
1217 	//	Unlock the looper
1218 	Unlock();
1219 
1220 	//	loop: As long as we are not terminating.
1221 	while (!fTerminating)
1222 	{
1223 		PRINT(("LOOPER: outer loop\n"));
1224 		// TODO: timeout determination algo
1225 		//	Read from message port (how do we determine what the timeout is?)
1226 		PRINT(("LOOPER: MessageFromPort()...\n"));
1227 		BMessage *msg = MessageFromPort();
1228 		PRINT(("LOOPER: ...done\n"));
1229 
1230 		//	Did we get a message?
1231 		if (msg)
1232 			_AddMessagePriv(msg);
1233 
1234 		//	Get message count from port
1235 		int32 msgCount = port_count(fMsgPort);
1236 		for (int32 i = 0; i < msgCount; ++i) {
1237 			//	Read 'count' messages from port (so we will not block)
1238 			//	We use zero as our timeout since we know there is stuff there
1239 			msg = MessageFromPort(0);
1240 			//	Add messages to queue
1241 			if (msg)
1242 				_AddMessagePriv(msg);
1243 		}
1244 
1245 		//	loop: As long as there are messages in the queue and the port is
1246 		//		  empty... and we are not terminating, of course.
1247 		bool dispatchNextMessage = true;
1248 		while (!fTerminating && dispatchNextMessage) {
1249 			PRINT(("LOOPER: inner loop\n"));
1250 			//	Get next message from queue (assign to fLastMessage)
1251 			fLastMessage = fQueue->NextMessage();
1252 
1253 			//	Lock the looper
1254 			Lock();
1255 			if (!fLastMessage) {
1256 				// No more messages: Unlock the looper and terminate the
1257 				// dispatch loop.
1258 				dispatchNextMessage = false;
1259 			} else {
1260 				PRINT(("LOOPER: fLastMessage: 0x%lx: %.4s\n", fLastMessage->what,
1261 					(char*)&fLastMessage->what));
1262 				DBG(fLastMessage->PrintToStream());
1263 
1264 				//	Get the target handler
1265 				//	Use BMessage friend functions to determine if we are using the
1266 				//	preferred handler, or if a target has been specified
1267 				BHandler* handler;
1268 				if (_use_preferred_target_(fLastMessage)) {
1269 					PRINT(("LOOPER: use preferred target\n"));
1270 					handler = fPreferred;
1271 				} else {
1272 					PRINT(("LOOPER: don't use preferred target\n"));
1273 					/**
1274 						@note	Here is where all the token stuff starts to
1275 								make sense.  How, exactly, do we determine
1276 								what the target BHandler is?  If we look at
1277 								BMessage, we see an int32 field, fTarget.
1278 								Amazingly, we happen to have a global mapping
1279 								of BHandler pointers to int32s!
1280 					 */
1281 					PRINT(("LOOPER: use: %ld\n", _get_message_target_(fLastMessage)));
1282 					gDefaultTokens.GetToken(_get_message_target_(fLastMessage),
1283 						B_HANDLER_TOKEN, (void **)&handler);
1284 					PRINT(("LOOPER: handler: %p, this: %p\n", handler, this));
1285 				}
1286 
1287 				if (!handler) {
1288 					PRINT(("LOOPER: no target handler, use this\n"));
1289 					handler = this;
1290 				}
1291 
1292 				//	Is this a scripting message? (BMessage::HasSpecifiers())
1293 				if (fLastMessage->HasSpecifiers()) {
1294 					int32 index = 0;
1295 					// Make sure the current specifier is kosher
1296 					if (fLastMessage->GetCurrentSpecifier(&index) == B_OK)
1297 						handler = resolve_specifier(handler, fLastMessage);
1298 				} else {
1299 					PRINT(("LOOPER: no scripting message\n"));
1300 				}
1301 
1302 				if (handler) {
1303 					//	Do filtering
1304 					handler = top_level_filter(fLastMessage, handler);
1305 					PRINT(("LOOPER: top_level_filter(): %p\n", handler));
1306 					if (handler && handler->Looper() == this)
1307 						DispatchMessage(fLastMessage, handler);
1308 				}
1309 			}
1310 
1311 			//	Unlock the looper
1312 			Unlock();
1313 
1314 			//	Delete the current message (fLastMessage)
1315 			if (fLastMessage) {
1316 				delete fLastMessage;
1317 				fLastMessage = NULL;
1318 			}
1319 
1320 			//	Are any messages on the port?
1321 			if (port_count(fMsgPort) > 0) {
1322 				//	Do outer loop
1323 				dispatchNextMessage = false;
1324 			}
1325 		}
1326 	}
1327 	PRINT(("BLooper::task_looper() done\n"));
1328 }
1329 
1330 
1331 void
1332 BLooper::do_quit_requested(BMessage *msg)
1333 {
1334 	bool isQuitting = QuitRequested();
1335 
1336 	// We send a reply to the sender, when they're waiting for a reply or
1337 	// if the request message contains a boolean "_shutdown_" field with value
1338 	// true. In the latter case the message came from the registrar, asking
1339 	// the application to shut down.
1340 	bool shutdown;
1341 	if (msg->IsSourceWaiting()
1342 		|| (msg->FindBool("_shutdown_", &shutdown) == B_OK && shutdown)) {
1343 		BMessage ReplyMsg(B_REPLY);
1344 		ReplyMsg.AddBool("result", isQuitting);
1345 		ReplyMsg.AddInt32("thread", fTaskID);
1346 		msg->SendReply(&ReplyMsg);
1347 	}
1348 
1349 	if (isQuitting)
1350 		Quit();
1351 }
1352 
1353 
1354 bool
1355 BLooper::AssertLocked() const
1356 {
1357 	if (!IsLocked()) {
1358 		debugger("looper must be locked before proceeding\n");
1359 		return false;
1360 	}
1361 
1362 	return true;
1363 }
1364 
1365 
1366 BHandler *
1367 BLooper::top_level_filter(BMessage* msg, BHandler* target)
1368 {
1369 	if (msg) {
1370 		// Apply the common filters first
1371 		target = apply_filters(CommonFilterList(), msg, target);
1372 		if (target) {
1373 			if (target->Looper() != this) {
1374 				debugger("Targeted handler does not belong to the looper.");
1375 				target = NULL;
1376 			} else {
1377 				// Now apply handler-specific filters
1378 				target = handler_only_filter(msg, target);
1379 			}
1380 		}
1381 	}
1382 
1383 	return target;
1384 }
1385 
1386 
1387 BHandler *
1388 BLooper::handler_only_filter(BMessage* msg, BHandler* target)
1389 {
1390 	// Keep running filters until our handler is NULL, or until the filtering
1391 	// handler returns itself as the designated handler
1392 	BHandler* oldTarget = NULL;
1393 	while (target != NULL && target != oldTarget) {
1394 		oldTarget = target;
1395 		target = apply_filters(oldTarget->FilterList(), msg, oldTarget);
1396 		if (target && (target->Looper() != this)) {
1397 			debugger("Targeted handler does not belong to the looper.");
1398 			target = NULL;
1399 		}
1400 	}
1401 
1402 	return target;
1403 }
1404 
1405 
1406 BHandler *
1407 BLooper::apply_filters(BList* list, BMessage* msg, BHandler* target)
1408 {
1409 	// This is where the action is!
1410 	// Check the parameters
1411 	if (!list || !msg)
1412 		return target;
1413 
1414 	// For each filter in the provided list
1415 	BMessageFilter* filter = NULL;
1416 	for (int32 i = 0; i < list->CountItems(); ++i) {
1417 		filter = (BMessageFilter*)list->ItemAt(i);
1418 
1419 		// Check command conditions
1420 		if (filter->FiltersAnyCommand() || (filter->Command() == msg->what)) {
1421 			// Check delivery conditions
1422 			message_delivery delivery = filter->MessageDelivery();
1423 			bool dropped = msg->WasDropped();
1424 			if (delivery == B_ANY_DELIVERY ||
1425 				((delivery == B_DROPPED_DELIVERY) && dropped) ||
1426 				((delivery == B_PROGRAMMED_DELIVERY) && !dropped)) {
1427 				// Check source conditions
1428 				message_source source = filter->MessageSource();
1429 				bool remote = msg->IsSourceRemote();
1430 				if (source == B_ANY_SOURCE
1431 					|| (source == B_REMOTE_SOURCE && remote)
1432 					|| (source == B_LOCAL_SOURCE && !remote)) {
1433 					// Are we using an "external" function?
1434 					filter_result result;
1435 					filter_hook func = filter->FilterFunction();
1436 					if (func)
1437 						result = func(msg, &target, filter);
1438 					else
1439 						result = filter->Filter(msg, &target);
1440 
1441 					// Is further processing allowed?
1442 					if (result == B_SKIP_MESSAGE) {
1443 						// No; time to bail out
1444 						return NULL;
1445 					}
1446 				}
1447 			}
1448 		}
1449 	}
1450 
1451 	return target;
1452 }
1453 
1454 
1455 void
1456 BLooper::check_lock()
1457 {
1458 	// This is a cheap variant of AssertLocked()
1459 	// It is used in situations where it's clear that the looper is valid,
1460 	// ie. from handlers
1461 	if (fOwner == -1 || fOwner != find_thread(NULL))
1462 		debugger("Looper must be locked.");
1463 }
1464 
1465 
1466 BHandler *
1467 BLooper::resolve_specifier(BHandler* target, BMessage* msg)
1468 {
1469 	// Check params
1470 	if (!target || !msg)
1471 		return NULL;
1472 
1473 	int32 index;
1474 	BMessage specifier;
1475 	int32 form;
1476 	const char* property;
1477 	status_t err = B_OK;
1478 	BHandler* newTarget = target;
1479 	//	Loop to deal with nested specifiers
1480 	//	(e.g., the 3rd button on the 4th view)
1481 	do {
1482 		err = msg->GetCurrentSpecifier(&index, &specifier, &form, &property);
1483 		if (err) {
1484 			BMessage reply(B_REPLY);
1485 			reply.AddInt32("error", err);
1486 			msg->SendReply(&reply);
1487 			return NULL;
1488 		}
1489 		//	Current target gets what was the new target
1490 		target = newTarget;
1491 		newTarget = target->ResolveSpecifier(msg, index, &specifier, form,
1492 			property);
1493 		//	Check that new target is owned by looper;
1494 		//	use IndexOf() to avoid dereferencing newTarget
1495 		//	(possible race condition with object destruction
1496 		//	by another looper)
1497 		if (!newTarget || IndexOf(newTarget) < 0)
1498 			return NULL;
1499 
1500 		//	Get current specifier index (may change in ResolveSpecifier())
1501 		msg->GetCurrentSpecifier(&index);
1502 	} while (newTarget && newTarget != target && index >= 0);
1503 
1504 	return newTarget;
1505 }
1506 
1507 
1508 void
1509 BLooper::UnlockFully()
1510 {
1511 	AssertLocked();
1512 
1513 /**
1514 	@note	What we're doing here is completely undoing the current owner's lock
1515 			on the looper.  This is actually pretty easy, since the owner only
1516 			has a single aquisition on the semaphore; every subsequent "lock"
1517 			is just an increment to the owner count.  The whole thing is quite
1518 			similar to Unlock(), except that we clear the ownership variables,
1519 			rather than merely decrementing them.
1520  */
1521 	// Clear the owner count
1522 	fOwnerCount = 0;
1523 	// Nobody owns the lock now
1524 	fOwner = -1;
1525 	// There is now one less thread holding a lock on this looper
1526 // bonefish: Currently _Lock() always acquires the semaphore.
1527 /*	long atomicCount = */atomic_add(&fAtomicCount, -1);
1528 //	if (atomicCount > 0)
1529 	{
1530 		release_sem(fLockSem);
1531 	}
1532 }
1533 
1534 
1535 void
1536 BLooper::AddLooper(BLooper *looper)
1537 {
1538 	if (gLooperList.IsLocked())
1539 		gLooperList.AddLooper(looper);
1540 }
1541 
1542 
1543 bool
1544 BLooper::IsLooperValid(const BLooper *looper)
1545 {
1546 	if (gLooperList.IsLocked())
1547 		return gLooperList.IsLooperValid(looper);
1548 
1549 	return false;
1550 }
1551 
1552 
1553 void
1554 BLooper::RemoveLooper(BLooper *looper)
1555 {
1556 	if (gLooperList.IsLocked())
1557 		gLooperList.RemoveLooper(looper);
1558 }
1559 
1560 
1561 void
1562 BLooper::GetLooperList(BList* list)
1563 {
1564 	BObjectLocker<BLooperList> ListLock(gLooperList);
1565 	if (ListLock.IsLocked())
1566 		gLooperList.GetLooperList(list);
1567 }
1568 
1569 
1570 BLooper *
1571 BLooper::LooperForName(const char* name)
1572 {
1573 	if (gLooperList.IsLocked())
1574 		return gLooperList.LooperForName(name);
1575 
1576 	return NULL;
1577 }
1578 
1579 
1580 BLooper *
1581 BLooper::LooperForPort(port_id port)
1582 {
1583 	if (gLooperList.IsLocked())
1584 		return gLooperList.LooperForPort(port);
1585 
1586 	return NULL;
1587 }
1588 
1589 
1590 //	#pragma mark -
1591 
1592 
1593 port_id
1594 _get_looper_port_(const BLooper *looper)
1595 {
1596 	return looper->fMsgPort;
1597 }
1598 
1599