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
,
110 BLooper::BLooper(const char* name
, int32 priority
, int32 portCapacity
)
114 _InitData(name
, priority
, -1, portCapacity
);
120 if (fRunCalled
&& !fTerminating
) {
121 debugger("You can't call delete on a BLooper object "
122 "once it is running.");
127 // In case the looper thread calls Quit() fLastMessage is not deleted.
133 // Close the message port and read and reply to the remaining messages.
134 if (fMsgPort
>= 0 && fOwnsPort
)
135 close_port(fMsgPort
);
137 // Clear the queue so our call to IsMessageWaiting() below doesn't give
139 fDirectTarget
->Close();
142 while ((message
= fDirectTarget
->Queue()->NextMessage()) != NULL
) {
144 // msg will automagically post generic reply
149 delete ReadMessageFromPort(0);
150 // msg will automagically post generic reply
151 } while (IsMessageWaiting());
153 delete_port(fMsgPort
);
155 fDirectTarget
->Release();
157 // Clean up our filters
158 SetCommonFilterList(NULL
);
160 AutoLocker
<BLooperList
> ListLock(gLooperList
);
163 // Remove all the "child" handlers
164 int32 count
= fHandlers
.CountItems();
165 for (int32 i
= 0; i
< count
; i
++) {
166 BHandler
* handler
= (BHandler
*)fHandlers
.ItemAtFast(i
);
167 handler
->SetNextHandler(NULL
);
168 handler
->SetLooper(NULL
);
170 fHandlers
.MakeEmpty();
173 gLooperList
.RemoveLooper(this);
174 delete_sem(fLockSem
);
178 BLooper::BLooper(BMessage
* data
)
182 if (data
->FindInt32("_port_cap", &portCapacity
) != B_OK
|| portCapacity
< 0)
183 portCapacity
= B_LOOPER_PORT_DEFAULT_CAPACITY
;
186 if (data
->FindInt32("_prio", &priority
) != B_OK
)
187 priority
= B_NORMAL_PRIORITY
;
189 _InitData(Name(), priority
, -1, portCapacity
);
194 BLooper::Instantiate(BMessage
* data
)
196 if (validate_instantiation(data
, "BLooper"))
197 return new BLooper(data
);
204 BLooper::Archive(BMessage
* data
, bool deep
) const
206 status_t status
= BHandler::Archive(data
, deep
);
211 status
= get_port_info(fMsgPort
, &info
);
213 status
= data
->AddInt32("_port_cap", info
.capacity
);
215 thread_info threadInfo
;
216 if (get_thread_info(Thread(), &threadInfo
) == B_OK
)
217 status
= data
->AddInt32("_prio", threadInfo
.priority
);
224 BLooper::PostMessage(uint32 command
)
226 BMessage
message(command
);
227 return _PostMessage(&message
, this, NULL
);
232 BLooper::PostMessage(BMessage
* message
)
234 return _PostMessage(message
, this, NULL
);
239 BLooper::PostMessage(uint32 command
, BHandler
* handler
, BHandler
* replyTo
)
241 BMessage
message(command
);
242 return _PostMessage(&message
, handler
, replyTo
);
247 BLooper::PostMessage(BMessage
* message
, BHandler
* handler
, BHandler
* replyTo
)
249 return _PostMessage(message
, handler
, replyTo
);
254 BLooper::DispatchMessage(BMessage
* message
, BHandler
* handler
)
256 PRINT(("BLooper::DispatchMessage(%.4s)\n", (char*)&message
->what
));
258 switch (message
->what
) {
260 // Can't call Quit() to do this, because of the slight chance
261 // another thread with have us locked between now and then.
264 // After returning from DispatchMessage(), the looper will be
265 // deleted in _task0_()
268 case B_QUIT_REQUESTED
:
269 if (handler
== this) {
270 _QuitRequested(message
);
277 handler
->MessageReceived(message
);
280 PRINT(("BLooper::DispatchMessage() done\n"));
285 BLooper::MessageReceived(BMessage
* message
)
287 // TODO: implement scripting support
288 BHandler::MessageReceived(message
);
293 BLooper::CurrentMessage() const
300 BLooper::DetachCurrentMessage()
302 BMessage
* message
= fLastMessage
;
309 BLooper::DispatchExternalMessage(BMessage
* message
, BHandler
* handler
,
314 BMessage
* previousMessage
= fLastMessage
;
315 fLastMessage
= message
;
317 DispatchMessage(message
, handler
);
319 _detached
= fLastMessage
== NULL
;
320 fLastMessage
= previousMessage
;
325 BLooper::MessageQueue() const
327 return fDirectTarget
->Queue();
332 BLooper::IsMessageWaiting() const
336 if (!fDirectTarget
->Queue()->IsEmpty())
341 count
= port_buffer_size_etc(fMsgPort
, B_RELATIVE_TIMEOUT
, 0);
342 } while (count
== B_INTERRUPTED
);
349 BLooper::AddHandler(BHandler
* handler
)
356 if (handler
->Looper() == NULL
) {
357 fHandlers
.AddItem(handler
);
358 handler
->SetLooper(this);
359 if (handler
!= this) // avoid a cycle
360 handler
->SetNextHandler(this);
366 BLooper::RemoveHandler(BHandler
* handler
)
373 if (handler
->Looper() == this && fHandlers
.RemoveItem(handler
)) {
374 if (handler
== fPreferred
)
377 handler
->SetNextHandler(NULL
);
378 handler
->SetLooper(NULL
);
387 BLooper::CountHandlers() const
391 return fHandlers
.CountItems();
396 BLooper::HandlerAt(int32 index
) const
400 return (BHandler
*)fHandlers
.ItemAt(index
);
405 BLooper::IndexOf(BHandler
* handler
) const
409 return fHandlers
.IndexOf(handler
);
414 BLooper::PreferredHandler() const
421 BLooper::SetPreferredHandler(BHandler
* handler
)
423 if (handler
&& handler
->Looper() == this && IndexOf(handler
) >= 0) {
424 fPreferred
= handler
;
437 // Not allowed to call Run() more than once
438 debugger("can't call BLooper::Run twice!");
442 fThread
= spawn_thread(_task0_
, Name(), fInitPriority
, this);
452 status_t err
= resume_thread(fThread
);
463 PRINT(("BLooper::Quit()\n"));
466 printf("ERROR - you must Lock a looper before calling Quit(), "
467 "team=%" B_PRId32
", looper=%s\n", Team(),
468 Name() ? Name() : "unnamed");
473 // We're toast already
477 PRINT((" is locked\n"));
480 PRINT((" Run() has not been called yet\n"));
483 } else if (find_thread(NULL
) == fThread
) {
484 PRINT((" We are the looper thread\n"));
489 PRINT((" Run() has already been called and we are not the looper thread\n"));
491 // As with sem in _Lock(), we need to cache this here in case the looper
492 // disappears before we get to the wait_for_thread() below
493 thread_id thread
= Thread();
495 // We need to unlock here. Otherwise the looper thread can't
496 // dispatch the _QUIT_ message we're going to post.
499 // As per the BeBook, if we've been called by a thread other than
500 // our own, the rest of the message queue has to get processed. So
501 // we put this in the queue, and when it shows up, we'll call Quit()
502 // from our own thread.
503 // QuitRequested() will not be called in this case.
506 // We have to wait until the looper is done processing any remaining
509 while (wait_for_thread(thread
, &status
) == B_INTERRUPTED
)
513 PRINT(("BLooper::Quit() done\n"));
518 BLooper::QuitRequested()
527 // Defer to global _Lock(); see notes there
528 return _Lock(this, -1, B_INFINITE_TIMEOUT
) == B_OK
;
535 PRINT(("BLooper::Unlock()\n"));
536 // Make sure we're locked to begin with
539 // Decrement fOwnerCount
541 PRINT((" fOwnerCount now: %ld\n", fOwnerCount
));
542 // Check to see if the owner still wants a lock
543 if (fOwnerCount
== 0) {
544 // Set fOwner to invalid thread_id (< 0)
549 // Decrement requested lock count (using fAtomicCount for this)
550 int32 atomicCount
= atomic_add(&fAtomicCount
, -1);
551 PRINT((" fAtomicCount now: %ld\n", fAtomicCount
));
553 // Check if anyone is waiting for a lock
554 // and release if it's the case
557 release_sem(fLockSem
);
559 PRINT(("BLooper::Unlock() done\n"));
564 BLooper::IsLocked() const
566 if (!gLooperList
.IsLooperValid(this)) {
567 // The looper is gone, so of course it's not locked
572 return ((addr_t
)&stack
& ~(B_PAGE_SIZE
- 1)) == fCachedStack
573 || find_thread(NULL
) == fOwner
;
578 BLooper::LockWithTimeout(bigtime_t timeout
)
580 return _Lock(this, -1, timeout
);
585 BLooper::Thread() const
592 BLooper::Team() const
594 return BPrivate::current_team();
599 BLooper::LooperForThread(thread_id thread
)
601 return gLooperList
.LooperForThread(thread
);
606 BLooper::LockingThread() const
613 BLooper::CountLocks() const
620 BLooper::CountLockRequests() const
634 BLooper::ResolveSpecifier(BMessage
* message
, int32 index
, BMessage
* specifier
,
635 int32 what
, const char* property
)
638 @note When I was first dumping the results of GetSupportedSuites() from
639 various classes, the use of the extra_data field was quite
640 mysterious to me. Then I dumped BApplication and compared the
641 result against the BeBook's docs for scripting BApplication. A
642 bunch of it isn't documented, but what is tipped me to the idea
643 that the extra_data is being used as a quick and dirty way to tell
644 what scripting "command" has been sent, e.g., for easy use in a
645 switch statement. Would certainly be a lot faster than a bunch of
646 string comparisons -- which wouldn't tell the whole story anyway,
647 because of the same name being used for multiple properties.
649 BPropertyInfo
propertyInfo(sLooperPropInfo
);
652 const char* errMsg
= "";
653 if (propertyInfo
.FindMatch(message
, index
, specifier
, what
, property
, &data
)
656 case BLOOPER_PROCESS_INTERNALLY
:
659 case BLOOPER_HANDLER_BY_INDEX
:
661 int32 index
= specifier
->FindInt32("index");
662 if (what
== B_REVERSE_INDEX_SPECIFIER
) {
663 index
= CountHandlers() - index
;
665 BHandler
* target
= HandlerAt(index
);
667 // Specifier has been fully handled
668 message
->PopSpecifier();
672 errMsg
= "handler index out of range";
678 err
= B_BAD_SCRIPT_SYNTAX
;
679 errMsg
= "Didn't understand the specifier(s)";
682 return BHandler::ResolveSpecifier(message
, index
, specifier
, what
,
686 BMessage
reply(B_MESSAGE_NOT_UNDERSTOOD
);
687 reply
.AddInt32("error", err
);
688 reply
.AddString("message", errMsg
);
689 message
->SendReply(&reply
);
696 BLooper::GetSupportedSuites(BMessage
* data
)
701 status_t status
= data
->AddString("suites", "suite/vnd.Be-looper");
702 if (status
== B_OK
) {
703 BPropertyInfo
PropertyInfo(sLooperPropInfo
);
704 status
= data
->AddFlat("messages", &PropertyInfo
);
706 status
= BHandler::GetSupportedSuites(data
);
714 BLooper::AddCommonFilter(BMessageFilter
* filter
)
721 if (filter
->Looper()) {
722 debugger("A MessageFilter can only be used once.");
726 if (fCommonFilters
== NULL
)
727 fCommonFilters
= new BList(FILTER_LIST_BLOCK_SIZE
);
729 filter
->SetLooper(this);
730 fCommonFilters
->AddItem(filter
);
735 BLooper::RemoveCommonFilter(BMessageFilter
* filter
)
739 if (fCommonFilters
== NULL
)
742 bool result
= fCommonFilters
->RemoveItem(filter
);
744 filter
->SetLooper(NULL
);
751 BLooper::SetCommonFilterList(BList
* filters
)
755 BMessageFilter
* filter
;
757 // Check for ownership issues - a filter can only have one owner
758 for (int32 i
= 0; i
< filters
->CountItems(); ++i
) {
759 filter
= (BMessageFilter
*)filters
->ItemAt(i
);
760 if (filter
->Looper()) {
761 debugger("A MessageFilter can only be used once.");
767 if (fCommonFilters
) {
768 for (int32 i
= 0; i
< fCommonFilters
->CountItems(); ++i
) {
769 delete (BMessageFilter
*)fCommonFilters
->ItemAt(i
);
772 delete fCommonFilters
;
773 fCommonFilters
= NULL
;
776 // Per the BeBook, we take ownership of the list
777 fCommonFilters
= filters
;
778 if (fCommonFilters
) {
779 for (int32 i
= 0; i
< fCommonFilters
->CountItems(); ++i
) {
780 filter
= (BMessageFilter
*)fCommonFilters
->ItemAt(i
);
781 filter
->SetLooper(this);
788 BLooper::CommonFilterList() const
790 return fCommonFilters
;
795 BLooper::Perform(perform_code d
, void* arg
)
797 // This is sort of what we're doing for this function everywhere
798 return BHandler::Perform(d
, arg
);
803 BLooper::MessageFromPort(bigtime_t timeout
)
805 return ReadMessageFromPort(timeout
);
809 void BLooper::_ReservedLooper1() {}
810 void BLooper::_ReservedLooper2() {}
811 void BLooper::_ReservedLooper3() {}
812 void BLooper::_ReservedLooper4() {}
813 void BLooper::_ReservedLooper5() {}
814 void BLooper::_ReservedLooper6() {}
817 BLooper::BLooper(const BLooper
& other
)
819 // Copy construction not allowed
824 BLooper::operator=(const BLooper
& other
)
826 // Looper copying not allowed
831 BLooper::BLooper(int32 priority
, port_id port
, const char* name
)
833 _InitData(name
, priority
, port
, B_LOOPER_PORT_DEFAULT_CAPACITY
);
838 BLooper::_PostMessage(BMessage
* msg
, BHandler
* handler
, BHandler
* replyTo
)
841 BMessenger
messenger(handler
, this, &status
);
843 return messenger
.SendMessage(msg
, replyTo
, 0);
850 Locks a looper either by port or using a direct pointer to the looper.
852 \param looper looper to lock, if not NULL
853 \param port port to identify the looper in case \a looper is NULL
854 \param timeout timeout for acquiring the lock
857 BLooper::_Lock(BLooper
* looper
, port_id port
, bigtime_t timeout
)
859 PRINT(("BLooper::_Lock(%p, %lx)\n", looper
, port
));
861 // Check params (loop, port)
862 if (looper
== NULL
&& port
< 0) {
863 PRINT(("BLooper::_Lock() done 1\n"));
867 thread_id currentThread
= find_thread(NULL
);
872 AutoLocker
<BLooperList
> ListLock(gLooperList
);
873 if (!ListLock
.IsLocked())
876 // Look up looper by port_id, if necessary
877 if (looper
== NULL
) {
878 looper
= gLooperList
.LooperForPort(port
);
879 if (looper
== NULL
) {
880 PRINT(("BLooper::_Lock() done 3\n"));
883 } else if (!gLooperList
.IsLooperValid(looper
)) {
884 // Check looper validity
885 PRINT(("BLooper::_Lock() done 4\n"));
889 // Check for nested lock attempt
890 if (currentThread
== looper
->fOwner
) {
891 ++looper
->fOwnerCount
;
892 PRINT(("BLooper::_Lock() done 5: fOwnerCount: %ld\n", loop
->fOwnerCount
));
896 // Cache the semaphore, so that we can safely access it after having
897 // unlocked the looper list
898 sem
= looper
->fLockSem
;
900 PRINT(("BLooper::_Lock() done 6\n"));
904 // Bump the requested lock count (using fAtomicCount for this)
905 oldCount
= atomic_add(&looper
->fAtomicCount
, 1);
908 return _LockComplete(looper
, oldCount
, currentThread
, sem
, timeout
);
913 BLooper::_LockComplete(BLooper
* looper
, int32 oldCount
, thread_id thread
,
914 sem_id sem
, bigtime_t timeout
)
922 err
= acquire_sem_etc(sem
, 1, B_RELATIVE_TIMEOUT
, timeout
);
923 } while (err
== B_INTERRUPTED
);
928 looper
->fOwner
= thread
;
929 looper
->fCachedStack
= (addr_t
)&err
& ~(B_PAGE_SIZE
- 1);
930 looper
->fOwnerCount
= 1;
933 PRINT(("BLooper::_LockComplete() done: %lx\n", err
));
939 BLooper::_InitData(const char* name
, int32 priority
, port_id port
,
945 fDirectTarget
= new (std::nothrow
) BPrivate::BDirectMessageTarget();
946 fCommonFilters
= NULL
;
950 fTerminating
= false;
956 name
= "anonymous looper";
959 fLockSem
= create_sem(1, name
);
961 fLockSem
= create_sem(0, name
);
964 if (portCapacity
<= 0)
965 portCapacity
= B_LOOPER_PORT_DEFAULT_CAPACITY
;
970 fMsgPort
= create_port(portCapacity
, name
);
972 fInitPriority
= priority
;
974 gLooperList
.AddLooper(this);
975 // this will also lock this looper
982 BLooper::AddMessage(BMessage
* message
)
984 _AddMessagePriv(message
);
986 // wakeup looper when being called from other threads if necessary
987 if (find_thread(NULL
) != Thread()
988 && fDirectTarget
->Queue()->IsNextMessage(message
)
989 && port_count(fMsgPort
) <= 0) {
990 // there is currently no message waiting, and we need to wakeup the
992 write_port_etc(fMsgPort
, 0, NULL
, 0, B_RELATIVE_TIMEOUT
, 0);
998 BLooper::_AddMessagePriv(BMessage
* message
)
1000 // ToDo: if no target token is specified, set to preferred handler
1001 // Others may want to peek into our message queue, so the preferred
1002 // handler must be set correctly already if no token was given
1004 fDirectTarget
->Queue()->AddMessage(message
);
1009 BLooper::_task0_(void* arg
)
1011 BLooper
* looper
= (BLooper
*)arg
;
1013 PRINT(("LOOPER: _task0_()\n"));
1015 if (looper
->Lock()) {
1016 PRINT(("LOOPER: looper locked\n"));
1017 looper
->task_looper();
1022 PRINT(("LOOPER: _task0_() done: thread %ld\n", find_thread(NULL
)));
1028 BLooper::ReadRawFromPort(int32
* msgCode
, bigtime_t timeout
)
1030 PRINT(("BLooper::ReadRawFromPort()\n"));
1031 uint8
* buffer
= NULL
;
1035 bufferSize
= port_buffer_size_etc(fMsgPort
, B_RELATIVE_TIMEOUT
, timeout
);
1036 } while (bufferSize
== B_INTERRUPTED
);
1038 if (bufferSize
< B_OK
) {
1039 PRINT(("BLooper::ReadRawFromPort(): failed: %ld\n", bufferSize
));
1044 buffer
= (uint8
*)malloc(bufferSize
);
1046 // we don't want to wait again here, since that can only mean
1047 // that someone else has read our message and our bufferSize
1048 // is now probably wrong
1049 PRINT(("read_port()...\n"));
1050 bufferSize
= read_port_etc(fMsgPort
, msgCode
, buffer
, bufferSize
,
1051 B_RELATIVE_TIMEOUT
, 0);
1053 if (bufferSize
< B_OK
) {
1058 PRINT(("BLooper::ReadRawFromPort() read: %.4s, %p (%d bytes)\n",
1059 (char*)msgCode
, buffer
, bufferSize
));
1066 BLooper::ReadMessageFromPort(bigtime_t timeout
)
1068 PRINT(("BLooper::ReadMessageFromPort()\n"));
1070 BMessage
* message
= NULL
;
1072 void* buffer
= ReadRawFromPort(&msgCode
, timeout
);
1076 message
= ConvertToMessage(buffer
, msgCode
);
1079 PRINT(("BLooper::ReadMessageFromPort() done: %p\n", message
));
1085 BLooper::ConvertToMessage(void* buffer
, int32 code
)
1087 PRINT(("BLooper::ConvertToMessage()\n"));
1091 BMessage
* message
= new BMessage();
1092 if (message
->Unflatten((const char*)buffer
) != B_OK
) {
1093 PRINT(("BLooper::ConvertToMessage(): unflattening message failed\n"));
1098 PRINT(("BLooper::ConvertToMessage(): %p\n", message
));
1104 BLooper::task_looper()
1106 PRINT(("BLooper::task_looper()\n"));
1107 // Check that looper is locked (should be)
1109 // Unlock the looper
1113 debugger("looper must not be locked!");
1115 // loop: As long as we are not terminating.
1116 while (!fTerminating
) {
1117 PRINT(("LOOPER: outer loop\n"));
1118 // TODO: timeout determination algo
1119 // Read from message port (how do we determine what the timeout is?)
1120 PRINT(("LOOPER: MessageFromPort()...\n"));
1121 BMessage
* msg
= MessageFromPort();
1122 PRINT(("LOOPER: ...done\n"));
1124 // Did we get a message?
1126 _AddMessagePriv(msg
);
1128 // Get message count from port
1129 int32 msgCount
= port_count(fMsgPort
);
1130 for (int32 i
= 0; i
< msgCount
; ++i
) {
1131 // Read 'count' messages from port (so we will not block)
1132 // We use zero as our timeout since we know there is stuff there
1133 msg
= MessageFromPort(0);
1135 _AddMessagePriv(msg
);
1138 // loop: As long as there are messages in the queue and the port is
1139 // empty... and we are not terminating, of course.
1140 bool dispatchNextMessage
= true;
1141 while (!fTerminating
&& dispatchNextMessage
) {
1142 PRINT(("LOOPER: inner loop\n"));
1143 // Get next message from queue (assign to fLastMessage after
1145 BMessage
* message
= fDirectTarget
->Queue()->NextMessage();
1149 fLastMessage
= message
;
1151 if (fLastMessage
== NULL
) {
1152 // No more messages: Unlock the looper and terminate the
1154 dispatchNextMessage
= false;
1156 PRINT(("LOOPER: fLastMessage: 0x%lx: %.4s\n", fLastMessage
->what
,
1157 (char*)&fLastMessage
->what
));
1158 DBG(fLastMessage
->PrintToStream());
1160 // Get the target handler
1161 BHandler
* handler
= NULL
;
1162 BMessage::Private
messagePrivate(fLastMessage
);
1163 bool usePreferred
= messagePrivate
.UsePreferredTarget();
1166 PRINT(("LOOPER: use preferred target\n"));
1167 handler
= fPreferred
;
1168 if (handler
== NULL
)
1171 gDefaultTokens
.GetToken(messagePrivate
.GetTarget(),
1172 B_HANDLER_TOKEN
, (void**)&handler
);
1174 // if this handler doesn't belong to us, we drop the message
1175 if (handler
!= NULL
&& handler
->Looper() != this)
1178 PRINT(("LOOPER: use %ld, handler: %p, this: %p\n",
1179 messagePrivate
.GetTarget(), handler
, this));
1182 // Is this a scripting message? (BMessage::HasSpecifiers())
1183 if (handler
!= NULL
&& fLastMessage
->HasSpecifiers()) {
1185 // Make sure the current specifier is kosher
1186 if (fLastMessage
->GetCurrentSpecifier(&index
) == B_OK
)
1187 handler
= resolve_specifier(handler
, fLastMessage
);
1192 handler
= _TopLevelFilter(fLastMessage
, handler
);
1193 PRINT(("LOOPER: _TopLevelFilter(): %p\n", handler
));
1194 if (handler
&& handler
->Looper() == this)
1195 DispatchMessage(fLastMessage
, handler
);
1200 // we leave the looper locked when we quit
1204 message
= fLastMessage
;
1205 fLastMessage
= NULL
;
1207 // Unlock the looper
1210 // Delete the current message (fLastMessage)
1211 if (message
!= NULL
)
1214 // Are any messages on the port?
1215 if (port_count(fMsgPort
) > 0) {
1217 dispatchNextMessage
= false;
1221 PRINT(("BLooper::task_looper() done\n"));
1226 BLooper::_QuitRequested(BMessage
* message
)
1228 bool isQuitting
= QuitRequested();
1229 int32 thread
= fThread
;
1234 // We send a reply to the sender, when they're waiting for a reply or
1235 // if the request message contains a boolean "_shutdown_" field with value
1236 // true. In the latter case the message came from the registrar, asking
1237 // the application to shut down.
1239 if (message
->IsSourceWaiting()
1240 || (message
->FindBool("_shutdown_", &shutdown
) == B_OK
&& shutdown
)) {
1241 BMessage
replyMsg(B_REPLY
);
1242 replyMsg
.AddBool("result", isQuitting
);
1243 replyMsg
.AddInt32("thread", thread
);
1244 message
->SendReply(&replyMsg
);
1250 BLooper::AssertLocked() const
1253 debugger("looper must be locked before proceeding\n");
1262 BLooper::_TopLevelFilter(BMessage
* message
, BHandler
* target
)
1264 if (message
== NULL
)
1267 // Apply the common filters first
1268 target
= _ApplyFilters(CommonFilterList(), message
, target
);
1270 if (target
->Looper() != this) {
1271 debugger("Targeted handler does not belong to the looper.");
1274 // Now apply handler-specific filters
1275 target
= _HandlerFilter(message
, target
);
1284 BLooper::_HandlerFilter(BMessage
* message
, BHandler
* target
)
1286 // Keep running filters until our handler is NULL, or until the filtering
1287 // handler returns itself as the designated handler
1288 BHandler
* previousTarget
= NULL
;
1289 while (target
!= NULL
&& target
!= previousTarget
) {
1290 previousTarget
= target
;
1292 target
= _ApplyFilters(target
->FilterList(), message
, target
);
1293 if (target
!= NULL
&& target
->Looper() != this) {
1294 debugger("Targeted handler does not belong to the looper.");
1304 BLooper::_ApplyFilters(BList
* list
, BMessage
* message
, BHandler
* target
)
1306 // This is where the action is!
1308 // check the parameters
1309 if (list
== NULL
|| message
== NULL
)
1312 // for each filter in the provided list
1313 BMessageFilter
* filter
= NULL
;
1314 for (int32 i
= 0; i
< list
->CountItems(); ++i
) {
1315 filter
= (BMessageFilter
*)list
->ItemAt(i
);
1317 // check command conditions
1318 if (filter
->FiltersAnyCommand() || filter
->Command() == message
->what
) {
1319 // check delivery conditions
1320 message_delivery delivery
= filter
->MessageDelivery();
1321 bool dropped
= message
->WasDropped();
1322 if (delivery
== B_ANY_DELIVERY
1323 || (delivery
== B_DROPPED_DELIVERY
&& dropped
)
1324 || (delivery
== B_PROGRAMMED_DELIVERY
&& !dropped
)) {
1325 // check source conditions
1326 message_source source
= filter
->MessageSource();
1327 bool remote
= message
->IsSourceRemote();
1328 if (source
== B_ANY_SOURCE
1329 || (source
== B_REMOTE_SOURCE
&& remote
)
1330 || (source
== B_LOCAL_SOURCE
&& !remote
)) {
1331 // Are we using an "external" function?
1332 filter_result result
;
1333 filter_hook filterFunction
= filter
->FilterFunction();
1334 if (filterFunction
!= NULL
)
1335 result
= filterFunction(message
, &target
, filter
);
1337 result
= filter
->Filter(message
, &target
);
1339 // Is further processing allowed?
1340 if (result
== B_SKIP_MESSAGE
) {
1341 // no, time to bail out
1354 BLooper::check_lock()
1356 // this is a cheap variant of AssertLocked()
1357 // it is used in situations where it's clear that the looper is valid,
1358 // i.e. from handlers
1360 if (((addr_t
)&stack
& ~(B_PAGE_SIZE
- 1)) == fCachedStack
1361 || fOwner
== find_thread(NULL
)) {
1365 debugger("Looper must be locked.");
1370 BLooper::resolve_specifier(BHandler
* target
, BMessage
* message
)
1373 if (!target
|| !message
)
1379 const char* property
;
1380 status_t err
= B_OK
;
1381 BHandler
* newTarget
= target
;
1382 // loop to deal with nested specifiers
1383 // (e.g., the 3rd button on the 4th view)
1385 err
= message
->GetCurrentSpecifier(&index
, &specifier
, &form
,
1388 BMessage
reply(B_REPLY
);
1389 reply
.AddInt32("error", err
);
1390 message
->SendReply(&reply
);
1393 // current target gets what was the new target
1395 newTarget
= target
->ResolveSpecifier(message
, index
, &specifier
, form
,
1397 // check that new target is owned by looper; use IndexOf() to avoid
1398 // dereferencing newTarget (possible race condition with object
1399 // destruction by another looper)
1400 if (newTarget
== NULL
|| IndexOf(newTarget
) < 0)
1403 // get current specifier index (may change in ResolveSpecifier())
1404 err
= message
->GetCurrentSpecifier(&index
);
1405 } while (newTarget
&& newTarget
!= target
&& err
== B_OK
&& index
>= 0);
1411 /*! Releases all eventually nested locks. Must be called with the lock
1415 BLooper::UnlockFully()
1419 // Clear the owner count
1421 // Nobody owns the lock now
1425 // There is now one less thread holding a lock on this looper
1426 int32 atomicCount
= atomic_add(&fAtomicCount
, -1);
1427 if (atomicCount
> 1)
1429 release_sem(fLockSem
);
1437 _get_looper_port_(const BLooper
* looper
)
1439 return looper
->fMsgPort
;