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