2 * Copyright 2001-2015 Haiku, Inc. All rights reserved
3 * Distributed under the terms of the MIT License.
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
13 // BLooper class spawns a thread that runs a message loop.
24 #include <MessageFilter.h>
25 #include <MessageQueue.h>
26 #include <Messenger.h>
27 #include <PropertyInfo.h>
30 #include <AutoLocker.h>
31 #include <DirectMessageTarget.h>
32 #include <LooperList.h>
33 #include <MessagePrivate.h>
34 #include <TokenSpace.h>
40 #define PRINT(x) DBG({ printf("[%6ld] ", find_thread(NULL)); printf x; })
45 static BLocker sDebugPrintLocker("BLooper debug print");
46 #define PRINT(x) DBG({ \
47 BAutolock _(sDebugPrintLocker); \
48 debug_printf("[%6ld] ", find_thread(NULL)); \
54 #define FILTER_LIST_BLOCK_SIZE 5
55 #define DATA_BLOCK_SIZE 5
58 using BPrivate::gDefaultTokens
;
59 using BPrivate::gLooperList
;
60 using BPrivate::BLooperList
;
62 port_id
_get_looper_port_(const BLooper
* looper
);
65 BLOOPER_PROCESS_INTERNALLY
= 0,
66 BLOOPER_HANDLER_BY_INDEX
69 static property_info sLooperPropInfo
[] = {
73 {B_INDEX_SPECIFIER
, B_REVERSE_INDEX_SPECIFIER
},
74 NULL
, BLOOPER_HANDLER_BY_INDEX
,
83 NULL
, BLOOPER_PROCESS_INTERNALLY
,
92 NULL
, BLOOPER_PROCESS_INTERNALLY
,
109 BLooper::BLooper(const char* name
, int32 priority
, int32 portCapacity
)
113 _InitData(name
, priority
, -1, portCapacity
);
119 if (fRunCalled
&& !fTerminating
) {
120 debugger("You can't call delete on a BLooper object "
121 "once it is running.");
126 // In case the looper thread calls Quit() fLastMessage is not deleted.
132 // Close the message port and read and reply to the remaining messages.
133 if (fMsgPort
>= 0 && fOwnsPort
)
134 close_port(fMsgPort
);
136 // Clear the queue so our call to IsMessageWaiting() below doesn't give
138 fDirectTarget
->Close();
141 while ((message
= fDirectTarget
->Queue()->NextMessage()) != NULL
) {
143 // msg will automagically post generic reply
148 delete ReadMessageFromPort(0);
149 // msg will automagically post generic reply
150 } while (IsMessageWaiting());
152 delete_port(fMsgPort
);
154 fDirectTarget
->Release();
156 // Clean up our filters
157 SetCommonFilterList(NULL
);
159 AutoLocker
<BLooperList
> ListLock(gLooperList
);
162 // Remove all the "child" handlers
163 int32 count
= fHandlers
.CountItems();
164 for (int32 i
= 0; i
< count
; i
++) {
165 BHandler
* handler
= (BHandler
*)fHandlers
.ItemAtFast(i
);
166 handler
->SetNextHandler(NULL
);
167 handler
->SetLooper(NULL
);
169 fHandlers
.MakeEmpty();
172 gLooperList
.RemoveLooper(this);
173 delete_sem(fLockSem
);
177 BLooper::BLooper(BMessage
* data
)
181 if (data
->FindInt32("_port_cap", &portCapacity
) != B_OK
|| portCapacity
< 0)
182 portCapacity
= B_LOOPER_PORT_DEFAULT_CAPACITY
;
185 if (data
->FindInt32("_prio", &priority
) != B_OK
)
186 priority
= B_NORMAL_PRIORITY
;
188 _InitData(Name(), priority
, -1, portCapacity
);
193 BLooper::Instantiate(BMessage
* data
)
195 if (validate_instantiation(data
, "BLooper"))
196 return new BLooper(data
);
203 BLooper::Archive(BMessage
* data
, bool deep
) const
205 status_t status
= BHandler::Archive(data
, deep
);
210 status
= get_port_info(fMsgPort
, &info
);
212 status
= data
->AddInt32("_port_cap", info
.capacity
);
214 thread_info threadInfo
;
215 if (get_thread_info(Thread(), &threadInfo
) == B_OK
)
216 status
= data
->AddInt32("_prio", threadInfo
.priority
);
223 BLooper::PostMessage(uint32 command
)
225 BMessage
message(command
);
226 return _PostMessage(&message
, this, NULL
);
231 BLooper::PostMessage(BMessage
* message
)
233 return _PostMessage(message
, this, NULL
);
238 BLooper::PostMessage(uint32 command
, BHandler
* handler
, BHandler
* replyTo
)
240 BMessage
message(command
);
241 return _PostMessage(&message
, handler
, replyTo
);
246 BLooper::PostMessage(BMessage
* message
, BHandler
* handler
, BHandler
* replyTo
)
248 return _PostMessage(message
, handler
, replyTo
);
253 BLooper::DispatchMessage(BMessage
* message
, BHandler
* handler
)
255 PRINT(("BLooper::DispatchMessage(%.4s)\n", (char*)&message
->what
));
257 switch (message
->what
) {
259 // Can't call Quit() to do this, because of the slight chance
260 // another thread with have us locked between now and then.
263 // After returning from DispatchMessage(), the looper will be
264 // deleted in _task0_()
267 case B_QUIT_REQUESTED
:
268 if (handler
== this) {
269 _QuitRequested(message
);
276 handler
->MessageReceived(message
);
279 PRINT(("BLooper::DispatchMessage() done\n"));
284 BLooper::MessageReceived(BMessage
* message
)
286 // TODO: implement scripting support
287 BHandler::MessageReceived(message
);
292 BLooper::CurrentMessage() const
299 BLooper::DetachCurrentMessage()
301 BMessage
* message
= fLastMessage
;
308 BLooper::DispatchExternalMessage(BMessage
* message
, BHandler
* handler
,
313 BMessage
* previousMessage
= fLastMessage
;
314 fLastMessage
= message
;
316 DispatchMessage(message
, handler
);
318 _detached
= fLastMessage
== NULL
;
319 fLastMessage
= previousMessage
;
324 BLooper::MessageQueue() const
326 return fDirectTarget
->Queue();
331 BLooper::IsMessageWaiting() const
335 if (!fDirectTarget
->Queue()->IsEmpty())
340 count
= port_buffer_size_etc(fMsgPort
, B_RELATIVE_TIMEOUT
, 0);
341 } while (count
== B_INTERRUPTED
);
348 BLooper::AddHandler(BHandler
* handler
)
355 if (handler
->Looper() == NULL
) {
356 fHandlers
.AddItem(handler
);
357 handler
->SetLooper(this);
358 if (handler
!= this) // avoid a cycle
359 handler
->SetNextHandler(this);
365 BLooper::RemoveHandler(BHandler
* handler
)
372 if (handler
->Looper() == this && fHandlers
.RemoveItem(handler
)) {
373 if (handler
== fPreferred
)
376 handler
->SetNextHandler(NULL
);
377 handler
->SetLooper(NULL
);
386 BLooper::CountHandlers() const
390 return fHandlers
.CountItems();
395 BLooper::HandlerAt(int32 index
) const
399 return (BHandler
*)fHandlers
.ItemAt(index
);
404 BLooper::IndexOf(BHandler
* handler
) const
408 return fHandlers
.IndexOf(handler
);
413 BLooper::PreferredHandler() const
420 BLooper::SetPreferredHandler(BHandler
* handler
)
422 if (handler
&& handler
->Looper() == this && IndexOf(handler
) >= 0) {
423 fPreferred
= handler
;
436 // Not allowed to call Run() more than once
437 debugger("can't call BLooper::Run twice!");
441 fThread
= spawn_thread(_task0_
, Name(), fInitPriority
, this);
451 status_t err
= resume_thread(fThread
);
462 PRINT(("BLooper::Quit()\n"));
465 printf("ERROR - you must Lock a looper before calling Quit(), "
466 "team=%" B_PRId32
", looper=%s\n", Team(),
467 Name() ? Name() : "unnamed");
472 // We're toast already
476 PRINT((" is locked\n"));
479 PRINT((" Run() has not been called yet\n"));
482 } else if (find_thread(NULL
) == fThread
) {
483 PRINT((" We are the looper thread\n"));
488 PRINT((" Run() has already been called and we are not the looper thread\n"));
490 // As with sem in _Lock(), we need to cache this here in case the looper
491 // disappears before we get to the wait_for_thread() below
492 thread_id thread
= Thread();
494 // We need to unlock here. Otherwise the looper thread can't
495 // dispatch the _QUIT_ message we're going to post.
498 // As per the BeBook, if we've been called by a thread other than
499 // our own, the rest of the message queue has to get processed. So
500 // we put this in the queue, and when it shows up, we'll call Quit()
501 // from our own thread.
502 // QuitRequested() will not be called in this case.
505 // We have to wait until the looper is done processing any remaining
508 while (wait_for_thread(thread
, &status
) == B_INTERRUPTED
)
512 PRINT(("BLooper::Quit() done\n"));
517 BLooper::QuitRequested()
526 // Defer to global _Lock(); see notes there
527 return _Lock(this, -1, B_INFINITE_TIMEOUT
) == B_OK
;
534 PRINT(("BLooper::Unlock()\n"));
535 // Make sure we're locked to begin with
538 // Decrement fOwnerCount
540 PRINT((" fOwnerCount now: %ld\n", fOwnerCount
));
541 // Check to see if the owner still wants a lock
542 if (fOwnerCount
== 0) {
543 // Set fOwner to invalid thread_id (< 0)
548 // Decrement requested lock count (using fAtomicCount for this)
549 int32 atomicCount
= atomic_add(&fAtomicCount
, -1);
550 PRINT((" fAtomicCount now: %ld\n", fAtomicCount
));
552 // Check if anyone is waiting for a lock
553 // and release if it's the case
556 release_sem(fLockSem
);
558 PRINT(("BLooper::Unlock() done\n"));
563 BLooper::IsLocked() const
565 if (!gLooperList
.IsLooperValid(this)) {
566 // The looper is gone, so of course it's not locked
571 return ((addr_t
)&stack
& ~(B_PAGE_SIZE
- 1)) == fCachedStack
572 || find_thread(NULL
) == fOwner
;
577 BLooper::LockWithTimeout(bigtime_t timeout
)
579 return _Lock(this, -1, timeout
);
584 BLooper::Thread() const
591 BLooper::Team() const
593 return BPrivate::current_team();
598 BLooper::LooperForThread(thread_id thread
)
600 return gLooperList
.LooperForThread(thread
);
605 BLooper::LockingThread() const
612 BLooper::CountLocks() const
619 BLooper::CountLockRequests() const
633 BLooper::ResolveSpecifier(BMessage
* message
, int32 index
, BMessage
* specifier
,
634 int32 what
, const char* property
)
637 @note When I was first dumping the results of GetSupportedSuites() from
638 various classes, the use of the extra_data field was quite
639 mysterious to me. Then I dumped BApplication and compared the
640 result against the BeBook's docs for scripting BApplication. A
641 bunch of it isn't documented, but what is tipped me to the idea
642 that the extra_data is being used as a quick and dirty way to tell
643 what scripting "command" has been sent, e.g., for easy use in a
644 switch statement. Would certainly be a lot faster than a bunch of
645 string comparisons -- which wouldn't tell the whole story anyway,
646 because of the same name being used for multiple properties.
648 BPropertyInfo
propertyInfo(sLooperPropInfo
);
651 const char* errMsg
= "";
652 if (propertyInfo
.FindMatch(message
, index
, specifier
, what
, property
, &data
)
655 case BLOOPER_PROCESS_INTERNALLY
:
658 case BLOOPER_HANDLER_BY_INDEX
:
660 int32 index
= specifier
->FindInt32("index");
661 if (what
== B_REVERSE_INDEX_SPECIFIER
) {
662 index
= CountHandlers() - index
;
664 BHandler
* target
= HandlerAt(index
);
666 // Specifier has been fully handled
667 message
->PopSpecifier();
671 errMsg
= "handler index out of range";
677 err
= B_BAD_SCRIPT_SYNTAX
;
678 errMsg
= "Didn't understand the specifier(s)";
681 return BHandler::ResolveSpecifier(message
, index
, specifier
, what
,
685 BMessage
reply(B_MESSAGE_NOT_UNDERSTOOD
);
686 reply
.AddInt32("error", err
);
687 reply
.AddString("message", errMsg
);
688 message
->SendReply(&reply
);
695 BLooper::GetSupportedSuites(BMessage
* data
)
700 status_t status
= data
->AddString("suites", "suite/vnd.Be-looper");
701 if (status
== B_OK
) {
702 BPropertyInfo
PropertyInfo(sLooperPropInfo
);
703 status
= data
->AddFlat("messages", &PropertyInfo
);
705 status
= BHandler::GetSupportedSuites(data
);
713 BLooper::AddCommonFilter(BMessageFilter
* filter
)
720 if (filter
->Looper()) {
721 debugger("A MessageFilter can only be used once.");
725 if (fCommonFilters
== NULL
)
726 fCommonFilters
= new BList(FILTER_LIST_BLOCK_SIZE
);
728 filter
->SetLooper(this);
729 fCommonFilters
->AddItem(filter
);
734 BLooper::RemoveCommonFilter(BMessageFilter
* filter
)
738 if (fCommonFilters
== NULL
)
741 bool result
= fCommonFilters
->RemoveItem(filter
);
743 filter
->SetLooper(NULL
);
750 BLooper::SetCommonFilterList(BList
* filters
)
754 BMessageFilter
* filter
;
756 // Check for ownership issues - a filter can only have one owner
757 for (int32 i
= 0; i
< filters
->CountItems(); ++i
) {
758 filter
= (BMessageFilter
*)filters
->ItemAt(i
);
759 if (filter
->Looper()) {
760 debugger("A MessageFilter can only be used once.");
766 if (fCommonFilters
) {
767 for (int32 i
= 0; i
< fCommonFilters
->CountItems(); ++i
) {
768 delete (BMessageFilter
*)fCommonFilters
->ItemAt(i
);
771 delete fCommonFilters
;
772 fCommonFilters
= NULL
;
775 // Per the BeBook, we take ownership of the list
776 fCommonFilters
= filters
;
777 if (fCommonFilters
) {
778 for (int32 i
= 0; i
< fCommonFilters
->CountItems(); ++i
) {
779 filter
= (BMessageFilter
*)fCommonFilters
->ItemAt(i
);
780 filter
->SetLooper(this);
787 BLooper::CommonFilterList() const
789 return fCommonFilters
;
794 BLooper::Perform(perform_code d
, void* arg
)
796 // This is sort of what we're doing for this function everywhere
797 return BHandler::Perform(d
, arg
);
802 BLooper::MessageFromPort(bigtime_t timeout
)
804 return ReadMessageFromPort(timeout
);
808 void BLooper::_ReservedLooper1() {}
809 void BLooper::_ReservedLooper2() {}
810 void BLooper::_ReservedLooper3() {}
811 void BLooper::_ReservedLooper4() {}
812 void BLooper::_ReservedLooper5() {}
813 void BLooper::_ReservedLooper6() {}
816 BLooper::BLooper(const BLooper
& other
)
818 // Copy construction not allowed
823 BLooper::operator=(const BLooper
& other
)
825 // Looper copying not allowed
830 BLooper::BLooper(int32 priority
, port_id port
, const char* name
)
832 _InitData(name
, priority
, port
, B_LOOPER_PORT_DEFAULT_CAPACITY
);
837 BLooper::_PostMessage(BMessage
* msg
, BHandler
* handler
, BHandler
* replyTo
)
840 BMessenger
messenger(handler
, this, &status
);
842 return messenger
.SendMessage(msg
, replyTo
, 0);
849 Locks a looper either by port or using a direct pointer to the looper.
851 \param looper looper to lock, if not NULL
852 \param port port to identify the looper in case \a looper is NULL
853 \param timeout timeout for acquiring the lock
856 BLooper::_Lock(BLooper
* looper
, port_id port
, bigtime_t timeout
)
858 PRINT(("BLooper::_Lock(%p, %lx)\n", looper
, port
));
860 // Check params (loop, port)
861 if (looper
== NULL
&& port
< 0) {
862 PRINT(("BLooper::_Lock() done 1\n"));
866 thread_id currentThread
= find_thread(NULL
);
871 AutoLocker
<BLooperList
> ListLock(gLooperList
);
872 if (!ListLock
.IsLocked())
875 // Look up looper by port_id, if necessary
876 if (looper
== NULL
) {
877 looper
= gLooperList
.LooperForPort(port
);
878 if (looper
== NULL
) {
879 PRINT(("BLooper::_Lock() done 3\n"));
882 } else if (!gLooperList
.IsLooperValid(looper
)) {
883 // Check looper validity
884 PRINT(("BLooper::_Lock() done 4\n"));
888 // Check for nested lock attempt
889 if (currentThread
== looper
->fOwner
) {
890 ++looper
->fOwnerCount
;
891 PRINT(("BLooper::_Lock() done 5: fOwnerCount: %ld\n", loop
->fOwnerCount
));
895 // Cache the semaphore, so that we can safely access it after having
896 // unlocked the looper list
897 sem
= looper
->fLockSem
;
899 PRINT(("BLooper::_Lock() done 6\n"));
903 // Bump the requested lock count (using fAtomicCount for this)
904 oldCount
= atomic_add(&looper
->fAtomicCount
, 1);
907 return _LockComplete(looper
, oldCount
, currentThread
, sem
, timeout
);
912 BLooper::_LockComplete(BLooper
* looper
, int32 oldCount
, thread_id thread
,
913 sem_id sem
, bigtime_t timeout
)
921 err
= acquire_sem_etc(sem
, 1, B_RELATIVE_TIMEOUT
, timeout
);
922 } while (err
== B_INTERRUPTED
);
927 looper
->fOwner
= thread
;
928 looper
->fCachedStack
= (addr_t
)&err
& ~(B_PAGE_SIZE
- 1);
929 looper
->fOwnerCount
= 1;
932 PRINT(("BLooper::_LockComplete() done: %lx\n", err
));
938 BLooper::_InitData(const char* name
, int32 priority
, port_id port
,
944 fDirectTarget
= new (std::nothrow
) BPrivate::BDirectMessageTarget();
945 fCommonFilters
= NULL
;
949 fTerminating
= false;
955 name
= "anonymous looper";
958 fLockSem
= create_sem(1, name
);
960 fLockSem
= create_sem(0, name
);
963 if (portCapacity
<= 0)
964 portCapacity
= B_LOOPER_PORT_DEFAULT_CAPACITY
;
969 fMsgPort
= create_port(portCapacity
, name
);
971 fInitPriority
= priority
;
973 gLooperList
.AddLooper(this);
974 // this will also lock this looper
981 BLooper::AddMessage(BMessage
* message
)
983 _AddMessagePriv(message
);
985 // wakeup looper when being called from other threads if necessary
986 if (find_thread(NULL
) != Thread()
987 && fDirectTarget
->Queue()->IsNextMessage(message
)
988 && port_count(fMsgPort
) <= 0) {
989 // there is currently no message waiting, and we need to wakeup the
991 write_port_etc(fMsgPort
, 0, NULL
, 0, B_RELATIVE_TIMEOUT
, 0);
997 BLooper::_AddMessagePriv(BMessage
* message
)
999 // ToDo: if no target token is specified, set to preferred handler
1000 // Others may want to peek into our message queue, so the preferred
1001 // handler must be set correctly already if no token was given
1003 fDirectTarget
->Queue()->AddMessage(message
);
1008 BLooper::_task0_(void* arg
)
1010 BLooper
* looper
= (BLooper
*)arg
;
1012 PRINT(("LOOPER: _task0_()\n"));
1014 if (looper
->Lock()) {
1015 PRINT(("LOOPER: looper locked\n"));
1016 looper
->task_looper();
1021 PRINT(("LOOPER: _task0_() done: thread %ld\n", find_thread(NULL
)));
1027 BLooper::ReadRawFromPort(int32
* msgCode
, bigtime_t timeout
)
1029 PRINT(("BLooper::ReadRawFromPort()\n"));
1030 uint8
* buffer
= NULL
;
1034 bufferSize
= port_buffer_size_etc(fMsgPort
, B_RELATIVE_TIMEOUT
, timeout
);
1035 } while (bufferSize
== B_INTERRUPTED
);
1037 if (bufferSize
< B_OK
) {
1038 PRINT(("BLooper::ReadRawFromPort(): failed: %ld\n", bufferSize
));
1043 buffer
= (uint8
*)malloc(bufferSize
);
1045 // we don't want to wait again here, since that can only mean
1046 // that someone else has read our message and our bufferSize
1047 // is now probably wrong
1048 PRINT(("read_port()...\n"));
1049 bufferSize
= read_port_etc(fMsgPort
, msgCode
, buffer
, bufferSize
,
1050 B_RELATIVE_TIMEOUT
, 0);
1052 if (bufferSize
< B_OK
) {
1057 PRINT(("BLooper::ReadRawFromPort() read: %.4s, %p (%d bytes)\n",
1058 (char*)msgCode
, buffer
, bufferSize
));
1065 BLooper::ReadMessageFromPort(bigtime_t timeout
)
1067 PRINT(("BLooper::ReadMessageFromPort()\n"));
1069 BMessage
* message
= NULL
;
1071 void* buffer
= ReadRawFromPort(&msgCode
, timeout
);
1075 message
= ConvertToMessage(buffer
, msgCode
);
1078 PRINT(("BLooper::ReadMessageFromPort() done: %p\n", message
));
1084 BLooper::ConvertToMessage(void* buffer
, int32 code
)
1086 PRINT(("BLooper::ConvertToMessage()\n"));
1090 BMessage
* message
= new BMessage();
1091 if (message
->Unflatten((const char*)buffer
) != B_OK
) {
1092 PRINT(("BLooper::ConvertToMessage(): unflattening message failed\n"));
1097 PRINT(("BLooper::ConvertToMessage(): %p\n", message
));
1103 BLooper::task_looper()
1105 PRINT(("BLooper::task_looper()\n"));
1106 // Check that looper is locked (should be)
1108 // Unlock the looper
1112 debugger("looper must not be locked!");
1114 // loop: As long as we are not terminating.
1115 while (!fTerminating
) {
1116 PRINT(("LOOPER: outer loop\n"));
1117 // TODO: timeout determination algo
1118 // Read from message port (how do we determine what the timeout is?)
1119 PRINT(("LOOPER: MessageFromPort()...\n"));
1120 BMessage
* msg
= MessageFromPort();
1121 PRINT(("LOOPER: ...done\n"));
1123 // Did we get a message?
1125 _AddMessagePriv(msg
);
1127 // Get message count from port
1128 int32 msgCount
= port_count(fMsgPort
);
1129 for (int32 i
= 0; i
< msgCount
; ++i
) {
1130 // Read 'count' messages from port (so we will not block)
1131 // We use zero as our timeout since we know there is stuff there
1132 msg
= MessageFromPort(0);
1134 _AddMessagePriv(msg
);
1137 // loop: As long as there are messages in the queue and the port is
1138 // empty... and we are not terminating, of course.
1139 bool dispatchNextMessage
= true;
1140 while (!fTerminating
&& dispatchNextMessage
) {
1141 PRINT(("LOOPER: inner loop\n"));
1142 // Get next message from queue (assign to fLastMessage after
1144 BMessage
* message
= fDirectTarget
->Queue()->NextMessage();
1148 fLastMessage
= message
;
1150 if (fLastMessage
== NULL
) {
1151 // No more messages: Unlock the looper and terminate the
1153 dispatchNextMessage
= false;
1155 PRINT(("LOOPER: fLastMessage: 0x%lx: %.4s\n", fLastMessage
->what
,
1156 (char*)&fLastMessage
->what
));
1157 DBG(fLastMessage
->PrintToStream());
1159 // Get the target handler
1160 BHandler
* handler
= NULL
;
1161 BMessage::Private
messagePrivate(fLastMessage
);
1162 bool usePreferred
= messagePrivate
.UsePreferredTarget();
1165 PRINT(("LOOPER: use preferred target\n"));
1166 handler
= fPreferred
;
1167 if (handler
== NULL
)
1170 gDefaultTokens
.GetToken(messagePrivate
.GetTarget(),
1171 B_HANDLER_TOKEN
, (void**)&handler
);
1173 // if this handler doesn't belong to us, we drop the message
1174 if (handler
!= NULL
&& handler
->Looper() != this)
1177 PRINT(("LOOPER: use %ld, handler: %p, this: %p\n",
1178 messagePrivate
.GetTarget(), handler
, this));
1181 // Is this a scripting message? (BMessage::HasSpecifiers())
1182 if (handler
!= NULL
&& fLastMessage
->HasSpecifiers()) {
1184 // Make sure the current specifier is kosher
1185 if (fLastMessage
->GetCurrentSpecifier(&index
) == B_OK
)
1186 handler
= resolve_specifier(handler
, fLastMessage
);
1191 handler
= _TopLevelFilter(fLastMessage
, handler
);
1192 PRINT(("LOOPER: _TopLevelFilter(): %p\n", handler
));
1193 if (handler
&& handler
->Looper() == this)
1194 DispatchMessage(fLastMessage
, handler
);
1199 // we leave the looper locked when we quit
1203 message
= fLastMessage
;
1204 fLastMessage
= NULL
;
1206 // Unlock the looper
1209 // Delete the current message (fLastMessage)
1210 if (message
!= NULL
)
1213 // Are any messages on the port?
1214 if (port_count(fMsgPort
) > 0) {
1216 dispatchNextMessage
= false;
1220 PRINT(("BLooper::task_looper() done\n"));
1225 BLooper::_QuitRequested(BMessage
* message
)
1227 bool isQuitting
= QuitRequested();
1228 int32 thread
= fThread
;
1233 // We send a reply to the sender, when they're waiting for a reply or
1234 // if the request message contains a boolean "_shutdown_" field with value
1235 // true. In the latter case the message came from the registrar, asking
1236 // the application to shut down.
1238 if (message
->IsSourceWaiting()
1239 || (message
->FindBool("_shutdown_", &shutdown
) == B_OK
&& shutdown
)) {
1240 BMessage
replyMsg(B_REPLY
);
1241 replyMsg
.AddBool("result", isQuitting
);
1242 replyMsg
.AddInt32("thread", thread
);
1243 message
->SendReply(&replyMsg
);
1249 BLooper::AssertLocked() const
1252 debugger("looper must be locked before proceeding\n");
1261 BLooper::_TopLevelFilter(BMessage
* message
, BHandler
* target
)
1263 if (message
== NULL
)
1266 // Apply the common filters first
1267 target
= _ApplyFilters(CommonFilterList(), message
, target
);
1269 if (target
->Looper() != this) {
1270 debugger("Targeted handler does not belong to the looper.");
1273 // Now apply handler-specific filters
1274 target
= _HandlerFilter(message
, target
);
1283 BLooper::_HandlerFilter(BMessage
* message
, BHandler
* target
)
1285 // Keep running filters until our handler is NULL, or until the filtering
1286 // handler returns itself as the designated handler
1287 BHandler
* previousTarget
= NULL
;
1288 while (target
!= NULL
&& target
!= previousTarget
) {
1289 previousTarget
= target
;
1291 target
= _ApplyFilters(target
->FilterList(), message
, target
);
1292 if (target
!= NULL
&& target
->Looper() != this) {
1293 debugger("Targeted handler does not belong to the looper.");
1303 BLooper::_ApplyFilters(BList
* list
, BMessage
* message
, BHandler
* target
)
1305 // This is where the action is!
1307 // check the parameters
1308 if (list
== NULL
|| message
== NULL
)
1311 // for each filter in the provided list
1312 BMessageFilter
* filter
= NULL
;
1313 for (int32 i
= 0; i
< list
->CountItems(); ++i
) {
1314 filter
= (BMessageFilter
*)list
->ItemAt(i
);
1316 // check command conditions
1317 if (filter
->FiltersAnyCommand() || filter
->Command() == message
->what
) {
1318 // check delivery conditions
1319 message_delivery delivery
= filter
->MessageDelivery();
1320 bool dropped
= message
->WasDropped();
1321 if (delivery
== B_ANY_DELIVERY
1322 || (delivery
== B_DROPPED_DELIVERY
&& dropped
)
1323 || (delivery
== B_PROGRAMMED_DELIVERY
&& !dropped
)) {
1324 // check source conditions
1325 message_source source
= filter
->MessageSource();
1326 bool remote
= message
->IsSourceRemote();
1327 if (source
== B_ANY_SOURCE
1328 || (source
== B_REMOTE_SOURCE
&& remote
)
1329 || (source
== B_LOCAL_SOURCE
&& !remote
)) {
1330 // Are we using an "external" function?
1331 filter_result result
;
1332 filter_hook filterFunction
= filter
->FilterFunction();
1333 if (filterFunction
!= NULL
)
1334 result
= filterFunction(message
, &target
, filter
);
1336 result
= filter
->Filter(message
, &target
);
1338 // Is further processing allowed?
1339 if (result
== B_SKIP_MESSAGE
) {
1340 // no, time to bail out
1353 BLooper::check_lock()
1355 // this is a cheap variant of AssertLocked()
1356 // it is used in situations where it's clear that the looper is valid,
1357 // i.e. from handlers
1359 if (((addr_t
)&stack
& ~(B_PAGE_SIZE
- 1)) == fCachedStack
1360 || fOwner
== find_thread(NULL
)) {
1364 debugger("Looper must be locked.");
1369 BLooper::resolve_specifier(BHandler
* target
, BMessage
* message
)
1372 if (!target
|| !message
)
1378 const char* property
;
1379 status_t err
= B_OK
;
1380 BHandler
* newTarget
= target
;
1381 // loop to deal with nested specifiers
1382 // (e.g., the 3rd button on the 4th view)
1384 err
= message
->GetCurrentSpecifier(&index
, &specifier
, &form
,
1387 BMessage
reply(B_REPLY
);
1388 reply
.AddInt32("error", err
);
1389 message
->SendReply(&reply
);
1392 // current target gets what was the new target
1394 newTarget
= target
->ResolveSpecifier(message
, index
, &specifier
, form
,
1396 // check that new target is owned by looper; use IndexOf() to avoid
1397 // dereferencing newTarget (possible race condition with object
1398 // destruction by another looper)
1399 if (newTarget
== NULL
|| IndexOf(newTarget
) < 0)
1402 // get current specifier index (may change in ResolveSpecifier())
1403 err
= message
->GetCurrentSpecifier(&index
);
1404 } while (newTarget
&& newTarget
!= target
&& err
== B_OK
&& index
>= 0);
1410 /*! Releases all eventually nested locks. Must be called with the lock
1414 BLooper::UnlockFully()
1418 // Clear the owner count
1420 // Nobody owns the lock now
1424 // There is now one less thread holding a lock on this looper
1425 int32 atomicCount
= atomic_add(&fAtomicCount
, -1);
1426 if (atomicCount
> 1)
1428 release_sem(fLockSem
);
1436 _get_looper_port_(const BLooper
* looper
)
1438 return looper
->fMsgPort
;