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