HaikuDepot: notify work status from main window
[haiku.git] / src / kits / app / Looper.cpp
blob809acc59dd3a9abb075b825e694343fab7003bce
1 /*
2 * Copyright 2001-2015 Haiku, Inc. All rights reserved
3 * Distributed under the terms of the MIT License.
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
13 // BLooper class spawns a thread that runs a message loop.
16 #include <Looper.h>
18 #include <new>
19 #include <stdio.h>
20 #include <stdlib.h>
22 #include <Autolock.h>
23 #include <Message.h>
24 #include <MessageFilter.h>
25 #include <MessageQueue.h>
26 #include <Messenger.h>
27 #include <PropertyInfo.h>
29 #include <AppMisc.h>
30 #include <AutoLocker.h>
31 #include <DirectMessageTarget.h>
32 #include <LooperList.h>
33 #include <MessagePrivate.h>
34 #include <TokenSpace.h>
37 // debugging
38 //#define DBG(x) x
39 #define DBG(x) ;
40 #define PRINT(x) DBG({ printf("[%6ld] ", find_thread(NULL)); printf x; })
43 #include <Autolock.h>
44 #include <Locker.h>
45 static BLocker sDebugPrintLocker("BLooper debug print");
46 #define PRINT(x) DBG({ \
47 BAutolock _(sDebugPrintLocker); \
48 debug_printf("[%6ld] ", find_thread(NULL)); \
49 debug_printf x; \
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);
64 enum {
65 BLOOPER_PROCESS_INTERNALLY = 0,
66 BLOOPER_HANDLER_BY_INDEX
69 static property_info sLooperPropInfo[] = {
71 "Handler",
72 {},
73 {B_INDEX_SPECIFIER, B_REVERSE_INDEX_SPECIFIER},
74 NULL, BLOOPER_HANDLER_BY_INDEX,
75 {},
76 {},
80 "Handlers",
81 {B_GET_PROPERTY},
82 {B_DIRECT_SPECIFIER},
83 NULL, BLOOPER_PROCESS_INTERNALLY,
84 {B_MESSENGER_TYPE},
85 {},
89 "Handler",
90 {B_COUNT_PROPERTIES},
91 {B_DIRECT_SPECIFIER},
92 NULL, BLOOPER_PROCESS_INTERNALLY,
93 {B_INT32_TYPE},
94 {},
98 { 0 }
101 struct _loop_data_ {
102 BLooper* looper;
103 thread_id thread;
107 // #pragma mark -
110 BLooper::BLooper(const char* name, int32 priority, int32 portCapacity)
112 BHandler(name)
114 _InitData(name, priority, -1, portCapacity);
118 BLooper::~BLooper()
120 if (fRunCalled && !fTerminating) {
121 debugger("You can't call delete on a BLooper object "
122 "once it is running.");
125 Lock();
127 // In case the looper thread calls Quit() fLastMessage is not deleted.
128 if (fLastMessage) {
129 delete fLastMessage;
130 fLastMessage = NULL;
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
138 // us bogus info
139 fDirectTarget->Close();
141 BMessage* message;
142 while ((message = fDirectTarget->Queue()->NextMessage()) != NULL) {
143 delete message;
144 // msg will automagically post generic reply
147 if (fOwnsPort) {
148 do {
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);
161 RemoveHandler(this);
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();
172 Unlock();
173 gLooperList.RemoveLooper(this);
174 delete_sem(fLockSem);
178 BLooper::BLooper(BMessage* data)
179 : BHandler(data)
181 int32 portCapacity;
182 if (data->FindInt32("_port_cap", &portCapacity) != B_OK || portCapacity < 0)
183 portCapacity = B_LOOPER_PORT_DEFAULT_CAPACITY;
185 int32 priority;
186 if (data->FindInt32("_prio", &priority) != B_OK)
187 priority = B_NORMAL_PRIORITY;
189 _InitData(Name(), priority, -1, portCapacity);
193 BArchivable*
194 BLooper::Instantiate(BMessage* data)
196 if (validate_instantiation(data, "BLooper"))
197 return new BLooper(data);
199 return NULL;
203 status_t
204 BLooper::Archive(BMessage* data, bool deep) const
206 status_t status = BHandler::Archive(data, deep);
207 if (status < B_OK)
208 return status;
210 port_info info;
211 status = get_port_info(fMsgPort, &info);
212 if (status == B_OK)
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);
219 return status;
223 status_t
224 BLooper::PostMessage(uint32 command)
226 BMessage message(command);
227 return _PostMessage(&message, this, NULL);
231 status_t
232 BLooper::PostMessage(BMessage* message)
234 return _PostMessage(message, this, NULL);
238 status_t
239 BLooper::PostMessage(uint32 command, BHandler* handler, BHandler* replyTo)
241 BMessage message(command);
242 return _PostMessage(&message, handler, replyTo);
246 status_t
247 BLooper::PostMessage(BMessage* message, BHandler* handler, BHandler* replyTo)
249 return _PostMessage(message, handler, replyTo);
253 void
254 BLooper::DispatchMessage(BMessage* message, BHandler* handler)
256 PRINT(("BLooper::DispatchMessage(%.4s)\n", (char*)&message->what));
258 switch (message->what) {
259 case _QUIT_:
260 // Can't call Quit() to do this, because of the slight chance
261 // another thread with have us locked between now and then.
262 fTerminating = true;
264 // After returning from DispatchMessage(), the looper will be
265 // deleted in _task0_()
266 break;
268 case B_QUIT_REQUESTED:
269 if (handler == this) {
270 _QuitRequested(message);
271 break;
274 // fall through
276 default:
277 handler->MessageReceived(message);
278 break;
280 PRINT(("BLooper::DispatchMessage() done\n"));
284 void
285 BLooper::MessageReceived(BMessage* message)
287 // TODO: implement scripting support
288 BHandler::MessageReceived(message);
292 BMessage*
293 BLooper::CurrentMessage() const
295 return fLastMessage;
299 BMessage*
300 BLooper::DetachCurrentMessage()
302 BMessage* message = fLastMessage;
303 fLastMessage = NULL;
304 return message;
308 void
309 BLooper::DispatchExternalMessage(BMessage* message, BHandler* handler,
310 bool& _detached)
312 AssertLocked();
314 BMessage* previousMessage = fLastMessage;
315 fLastMessage = message;
317 DispatchMessage(message, handler);
319 _detached = fLastMessage == NULL;
320 fLastMessage = previousMessage;
324 BMessageQueue*
325 BLooper::MessageQueue() const
327 return fDirectTarget->Queue();
331 bool
332 BLooper::IsMessageWaiting() const
334 AssertLocked();
336 if (!fDirectTarget->Queue()->IsEmpty())
337 return true;
339 int32 count;
340 do {
341 count = port_buffer_size_etc(fMsgPort, B_RELATIVE_TIMEOUT, 0);
342 } while (count == B_INTERRUPTED);
344 return count > 0;
348 void
349 BLooper::AddHandler(BHandler* handler)
351 if (handler == NULL)
352 return;
354 AssertLocked();
356 if (handler->Looper() == NULL) {
357 fHandlers.AddItem(handler);
358 handler->SetLooper(this);
359 if (handler != this) // avoid a cycle
360 handler->SetNextHandler(this);
365 bool
366 BLooper::RemoveHandler(BHandler* handler)
368 if (handler == NULL)
369 return false;
371 AssertLocked();
373 if (handler->Looper() == this && fHandlers.RemoveItem(handler)) {
374 if (handler == fPreferred)
375 fPreferred = NULL;
377 handler->SetNextHandler(NULL);
378 handler->SetLooper(NULL);
379 return true;
382 return false;
386 int32
387 BLooper::CountHandlers() const
389 AssertLocked();
391 return fHandlers.CountItems();
395 BHandler*
396 BLooper::HandlerAt(int32 index) const
398 AssertLocked();
400 return (BHandler*)fHandlers.ItemAt(index);
404 int32
405 BLooper::IndexOf(BHandler* handler) const
407 AssertLocked();
409 return fHandlers.IndexOf(handler);
413 BHandler*
414 BLooper::PreferredHandler() const
416 return fPreferred;
420 void
421 BLooper::SetPreferredHandler(BHandler* handler)
423 if (handler && handler->Looper() == this && IndexOf(handler) >= 0) {
424 fPreferred = handler;
425 } else {
426 fPreferred = NULL;
431 thread_id
432 BLooper::Run()
434 AssertLocked();
436 if (fRunCalled) {
437 // Not allowed to call Run() more than once
438 debugger("can't call BLooper::Run twice!");
439 return fThread;
442 fThread = spawn_thread(_task0_, Name(), fInitPriority, this);
443 if (fThread < B_OK)
444 return fThread;
446 if (fMsgPort < B_OK)
447 return fMsgPort;
449 fRunCalled = true;
450 Unlock();
452 status_t err = resume_thread(fThread);
453 if (err < B_OK)
454 return err;
456 return fThread;
460 void
461 BLooper::Quit()
463 PRINT(("BLooper::Quit()\n"));
465 if (!IsLocked()) {
466 printf("ERROR - you must Lock a looper before calling Quit(), "
467 "team=%" B_PRId32 ", looper=%s\n", Team(),
468 Name() ? Name() : "unnamed");
471 // Try to lock
472 if (!Lock()) {
473 // We're toast already
474 return;
477 PRINT((" is locked\n"));
479 if (!fRunCalled) {
480 PRINT((" Run() has not been called yet\n"));
481 fTerminating = true;
482 delete this;
483 } else if (find_thread(NULL) == fThread) {
484 PRINT((" We are the looper thread\n"));
485 fTerminating = true;
486 delete this;
487 exit_thread(0);
488 } else {
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.
497 UnlockFully();
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.
504 PostMessage(_QUIT_);
506 // We have to wait until the looper is done processing any remaining
507 // messages.
508 status_t status;
509 while (wait_for_thread(thread, &status) == B_INTERRUPTED)
513 PRINT(("BLooper::Quit() done\n"));
517 bool
518 BLooper::QuitRequested()
520 return true;
524 bool
525 BLooper::Lock()
527 // Defer to global _Lock(); see notes there
528 return _Lock(this, -1, B_INFINITE_TIMEOUT) == B_OK;
532 void
533 BLooper::Unlock()
535 PRINT(("BLooper::Unlock()\n"));
536 // Make sure we're locked to begin with
537 AssertLocked();
539 // Decrement fOwnerCount
540 --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)
545 fOwner = -1;
546 fCachedStack = 0;
548 #if DEBUG < 1
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
555 if (atomicCount > 1)
556 #endif
557 release_sem(fLockSem);
559 PRINT(("BLooper::Unlock() done\n"));
563 bool
564 BLooper::IsLocked() const
566 if (!gLooperList.IsLooperValid(this)) {
567 // The looper is gone, so of course it's not locked
568 return false;
571 uint32 stack;
572 return ((addr_t)&stack & ~(B_PAGE_SIZE - 1)) == fCachedStack
573 || find_thread(NULL) == fOwner;
577 status_t
578 BLooper::LockWithTimeout(bigtime_t timeout)
580 return _Lock(this, -1, timeout);
584 thread_id
585 BLooper::Thread() const
587 return fThread;
591 team_id
592 BLooper::Team() const
594 return BPrivate::current_team();
598 BLooper*
599 BLooper::LooperForThread(thread_id thread)
601 return gLooperList.LooperForThread(thread);
605 thread_id
606 BLooper::LockingThread() const
608 return fOwner;
612 int32
613 BLooper::CountLocks() const
615 return fOwnerCount;
619 int32
620 BLooper::CountLockRequests() const
622 return fAtomicCount;
626 sem_id
627 BLooper::Sem() const
629 return fLockSem;
633 BHandler*
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);
650 uint32 data;
651 status_t err = B_OK;
652 const char* errMsg = "";
653 if (propertyInfo.FindMatch(message, index, specifier, what, property, &data)
654 >= 0) {
655 switch (data) {
656 case BLOOPER_PROCESS_INTERNALLY:
657 return this;
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);
666 if (target) {
667 // Specifier has been fully handled
668 message->PopSpecifier();
669 return target;
670 } else {
671 err = B_BAD_INDEX;
672 errMsg = "handler index out of range";
674 break;
677 default:
678 err = B_BAD_SCRIPT_SYNTAX;
679 errMsg = "Didn't understand the specifier(s)";
681 } else {
682 return BHandler::ResolveSpecifier(message, index, specifier, what,
683 property);
686 BMessage reply(B_MESSAGE_NOT_UNDERSTOOD);
687 reply.AddInt32("error", err);
688 reply.AddString("message", errMsg);
689 message->SendReply(&reply);
691 return NULL;
695 status_t
696 BLooper::GetSupportedSuites(BMessage* data)
698 if (data == NULL)
699 return B_BAD_VALUE;
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);
705 if (status == B_OK)
706 status = BHandler::GetSupportedSuites(data);
709 return status;
713 void
714 BLooper::AddCommonFilter(BMessageFilter* filter)
716 if (filter == NULL)
717 return;
719 AssertLocked();
721 if (filter->Looper()) {
722 debugger("A MessageFilter can only be used once.");
723 return;
726 if (fCommonFilters == NULL)
727 fCommonFilters = new BList(FILTER_LIST_BLOCK_SIZE);
729 filter->SetLooper(this);
730 fCommonFilters->AddItem(filter);
734 bool
735 BLooper::RemoveCommonFilter(BMessageFilter* filter)
737 AssertLocked();
739 if (fCommonFilters == NULL)
740 return false;
742 bool result = fCommonFilters->RemoveItem(filter);
743 if (result)
744 filter->SetLooper(NULL);
746 return result;
750 void
751 BLooper::SetCommonFilterList(BList* filters)
753 AssertLocked();
755 BMessageFilter* filter;
756 if (filters) {
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.");
762 return;
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);
787 BList*
788 BLooper::CommonFilterList() const
790 return fCommonFilters;
794 status_t
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);
802 BMessage*
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
823 BLooper&
824 BLooper::operator=(const BLooper& other)
826 // Looper copying not allowed
827 return *this;
831 BLooper::BLooper(int32 priority, port_id port, const char* name)
833 _InitData(name, priority, port, B_LOOPER_PORT_DEFAULT_CAPACITY);
837 status_t
838 BLooper::_PostMessage(BMessage* msg, BHandler* handler, BHandler* replyTo)
840 status_t status;
841 BMessenger messenger(handler, this, &status);
842 if (status == B_OK)
843 return messenger.SendMessage(msg, replyTo, 0);
845 return status;
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
856 status_t
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"));
864 return B_BAD_VALUE;
867 thread_id currentThread = find_thread(NULL);
868 int32 oldCount;
869 sem_id sem;
872 AutoLocker<BLooperList> ListLock(gLooperList);
873 if (!ListLock.IsLocked())
874 return B_BAD_VALUE;
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"));
881 return B_BAD_VALUE;
883 } else if (!gLooperList.IsLooperValid(looper)) {
884 // Check looper validity
885 PRINT(("BLooper::_Lock() done 4\n"));
886 return B_BAD_VALUE;
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));
893 return B_OK;
896 // Cache the semaphore, so that we can safely access it after having
897 // unlocked the looper list
898 sem = looper->fLockSem;
899 if (sem < 0) {
900 PRINT(("BLooper::_Lock() done 6\n"));
901 return B_BAD_VALUE;
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);
912 status_t
913 BLooper::_LockComplete(BLooper* looper, int32 oldCount, thread_id thread,
914 sem_id sem, bigtime_t timeout)
916 status_t err = B_OK;
918 #if DEBUG < 1
919 if (oldCount > 0) {
920 #endif
921 do {
922 err = acquire_sem_etc(sem, 1, B_RELATIVE_TIMEOUT, timeout);
923 } while (err == B_INTERRUPTED);
924 #if DEBUG < 1
926 #endif
927 if (err == B_OK) {
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));
934 return err;
938 void
939 BLooper::_InitData(const char* name, int32 priority, port_id port,
940 int32 portCapacity)
942 fOwner = B_ERROR;
943 fCachedStack = 0;
944 fRunCalled = false;
945 fDirectTarget = new (std::nothrow) BPrivate::BDirectMessageTarget();
946 fCommonFilters = NULL;
947 fLastMessage = NULL;
948 fPreferred = NULL;
949 fThread = B_ERROR;
950 fTerminating = false;
951 fOwnsPort = true;
952 fMsgPort = -1;
953 fAtomicCount = 0;
955 if (name == NULL)
956 name = "anonymous looper";
958 #if DEBUG
959 fLockSem = create_sem(1, name);
960 #else
961 fLockSem = create_sem(0, name);
962 #endif
964 if (portCapacity <= 0)
965 portCapacity = B_LOOPER_PORT_DEFAULT_CAPACITY;
967 if (port >= 0)
968 fMsgPort = port;
969 else
970 fMsgPort = create_port(portCapacity, name);
972 fInitPriority = priority;
974 gLooperList.AddLooper(this);
975 // this will also lock this looper
977 AddHandler(this);
981 void
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
991 // looper
992 write_port_etc(fMsgPort, 0, NULL, 0, B_RELATIVE_TIMEOUT, 0);
997 void
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);
1008 status_t
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();
1019 delete looper;
1022 PRINT(("LOOPER: _task0_() done: thread %ld\n", find_thread(NULL)));
1023 return B_OK;
1027 void*
1028 BLooper::ReadRawFromPort(int32* msgCode, bigtime_t timeout)
1030 PRINT(("BLooper::ReadRawFromPort()\n"));
1031 uint8* buffer = NULL;
1032 ssize_t bufferSize;
1034 do {
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));
1040 return NULL;
1043 if (bufferSize > 0)
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) {
1054 free(buffer);
1055 return NULL;
1058 PRINT(("BLooper::ReadRawFromPort() read: %.4s, %p (%d bytes)\n",
1059 (char*)msgCode, buffer, bufferSize));
1061 return buffer;
1065 BMessage*
1066 BLooper::ReadMessageFromPort(bigtime_t timeout)
1068 PRINT(("BLooper::ReadMessageFromPort()\n"));
1069 int32 msgCode;
1070 BMessage* message = NULL;
1072 void* buffer = ReadRawFromPort(&msgCode, timeout);
1073 if (buffer == NULL)
1074 return NULL;
1076 message = ConvertToMessage(buffer, msgCode);
1077 free(buffer);
1079 PRINT(("BLooper::ReadMessageFromPort() done: %p\n", message));
1080 return message;
1084 BMessage*
1085 BLooper::ConvertToMessage(void* buffer, int32 code)
1087 PRINT(("BLooper::ConvertToMessage()\n"));
1088 if (buffer == NULL)
1089 return NULL;
1091 BMessage* message = new BMessage();
1092 if (message->Unflatten((const char*)buffer) != B_OK) {
1093 PRINT(("BLooper::ConvertToMessage(): unflattening message failed\n"));
1094 delete message;
1095 message = NULL;
1098 PRINT(("BLooper::ConvertToMessage(): %p\n", message));
1099 return message;
1103 void
1104 BLooper::task_looper()
1106 PRINT(("BLooper::task_looper()\n"));
1107 // Check that looper is locked (should be)
1108 AssertLocked();
1109 // Unlock the looper
1110 Unlock();
1112 if (IsLocked())
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?
1125 if (msg)
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);
1134 if (msg)
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
1144 // locking)
1145 BMessage* message = fDirectTarget->Queue()->NextMessage();
1147 Lock();
1149 fLastMessage = message;
1151 if (fLastMessage == NULL) {
1152 // No more messages: Unlock the looper and terminate the
1153 // dispatch loop.
1154 dispatchNextMessage = false;
1155 } else {
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();
1165 if (usePreferred) {
1166 PRINT(("LOOPER: use preferred target\n"));
1167 handler = fPreferred;
1168 if (handler == NULL)
1169 handler = this;
1170 } else {
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)
1176 handler = NULL;
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()) {
1184 int32 index = 0;
1185 // Make sure the current specifier is kosher
1186 if (fLastMessage->GetCurrentSpecifier(&index) == B_OK)
1187 handler = resolve_specifier(handler, fLastMessage);
1190 if (handler) {
1191 // Do filtering
1192 handler = _TopLevelFilter(fLastMessage, handler);
1193 PRINT(("LOOPER: _TopLevelFilter(): %p\n", handler));
1194 if (handler && handler->Looper() == this)
1195 DispatchMessage(fLastMessage, handler);
1199 if (fTerminating) {
1200 // we leave the looper locked when we quit
1201 return;
1204 message = fLastMessage;
1205 fLastMessage = NULL;
1207 // Unlock the looper
1208 Unlock();
1210 // Delete the current message (fLastMessage)
1211 if (message != NULL)
1212 delete message;
1214 // Are any messages on the port?
1215 if (port_count(fMsgPort) > 0) {
1216 // Do outer loop
1217 dispatchNextMessage = false;
1221 PRINT(("BLooper::task_looper() done\n"));
1225 void
1226 BLooper::_QuitRequested(BMessage* message)
1228 bool isQuitting = QuitRequested();
1229 int32 thread = fThread;
1231 if (isQuitting)
1232 Quit();
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.
1238 bool shutdown;
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);
1249 bool
1250 BLooper::AssertLocked() const
1252 if (!IsLocked()) {
1253 debugger("looper must be locked before proceeding\n");
1254 return false;
1257 return true;
1261 BHandler*
1262 BLooper::_TopLevelFilter(BMessage* message, BHandler* target)
1264 if (message == NULL)
1265 return target;
1267 // Apply the common filters first
1268 target = _ApplyFilters(CommonFilterList(), message, target);
1269 if (target) {
1270 if (target->Looper() != this) {
1271 debugger("Targeted handler does not belong to the looper.");
1272 target = NULL;
1273 } else {
1274 // Now apply handler-specific filters
1275 target = _HandlerFilter(message, target);
1279 return target;
1283 BHandler*
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.");
1295 target = NULL;
1299 return target;
1303 BHandler*
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)
1310 return target;
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);
1336 else
1337 result = filter->Filter(message, &target);
1339 // Is further processing allowed?
1340 if (result == B_SKIP_MESSAGE) {
1341 // no, time to bail out
1342 return NULL;
1349 return target;
1353 void
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
1359 uint32 stack;
1360 if (((addr_t)&stack & ~(B_PAGE_SIZE - 1)) == fCachedStack
1361 || fOwner == find_thread(NULL)) {
1362 return;
1365 debugger("Looper must be locked.");
1369 BHandler*
1370 BLooper::resolve_specifier(BHandler* target, BMessage* message)
1372 // check params
1373 if (!target || !message)
1374 return NULL;
1376 int32 index;
1377 BMessage specifier;
1378 int32 form;
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)
1384 do {
1385 err = message->GetCurrentSpecifier(&index, &specifier, &form,
1386 &property);
1387 if (err != B_OK) {
1388 BMessage reply(B_REPLY);
1389 reply.AddInt32("error", err);
1390 message->SendReply(&reply);
1391 return NULL;
1393 // current target gets what was the new target
1394 target = newTarget;
1395 newTarget = target->ResolveSpecifier(message, index, &specifier, form,
1396 property);
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)
1401 return NULL;
1403 // get current specifier index (may change in ResolveSpecifier())
1404 err = message->GetCurrentSpecifier(&index);
1405 } while (newTarget && newTarget != target && err == B_OK && index >= 0);
1407 return newTarget;
1411 /*! Releases all eventually nested locks. Must be called with the lock
1412 actually held.
1414 void
1415 BLooper::UnlockFully()
1417 AssertLocked();
1419 // Clear the owner count
1420 fOwnerCount = 0;
1421 // Nobody owns the lock now
1422 fOwner = -1;
1423 fCachedStack = 0;
1424 #if DEBUG < 1
1425 // There is now one less thread holding a lock on this looper
1426 int32 atomicCount = atomic_add(&fAtomicCount, -1);
1427 if (atomicCount > 1)
1428 #endif
1429 release_sem(fLockSem);
1433 // #pragma mark -
1436 port_id
1437 _get_looper_port_(const BLooper* looper)
1439 return looper->fMsgPort;