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