BPicture: Fix archive constructor.
[haiku.git] / src / kits / app / Looper.cpp
blob579d1db4f07444e0ad6f76a548c3fb4b890d40a0
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 {},
100 struct _loop_data_ {
101 BLooper* looper;
102 thread_id thread;
106 // #pragma mark -
109 BLooper::BLooper(const char* name, int32 priority, int32 portCapacity)
111 BHandler(name)
113 _InitData(name, priority, -1, portCapacity);
117 BLooper::~BLooper()
119 if (fRunCalled && !fTerminating) {
120 debugger("You can't call delete on a BLooper object "
121 "once it is running.");
124 Lock();
126 // In case the looper thread calls Quit() fLastMessage is not deleted.
127 if (fLastMessage) {
128 delete fLastMessage;
129 fLastMessage = NULL;
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
137 // us bogus info
138 fDirectTarget->Close();
140 BMessage* message;
141 while ((message = fDirectTarget->Queue()->NextMessage()) != NULL) {
142 delete message;
143 // msg will automagically post generic reply
146 if (fOwnsPort) {
147 do {
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);
160 RemoveHandler(this);
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();
171 Unlock();
172 gLooperList.RemoveLooper(this);
173 delete_sem(fLockSem);
177 BLooper::BLooper(BMessage* data)
178 : BHandler(data)
180 int32 portCapacity;
181 if (data->FindInt32("_port_cap", &portCapacity) != B_OK || portCapacity < 0)
182 portCapacity = B_LOOPER_PORT_DEFAULT_CAPACITY;
184 int32 priority;
185 if (data->FindInt32("_prio", &priority) != B_OK)
186 priority = B_NORMAL_PRIORITY;
188 _InitData(Name(), priority, -1, portCapacity);
192 BArchivable*
193 BLooper::Instantiate(BMessage* data)
195 if (validate_instantiation(data, "BLooper"))
196 return new BLooper(data);
198 return NULL;
202 status_t
203 BLooper::Archive(BMessage* data, bool deep) const
205 status_t status = BHandler::Archive(data, deep);
206 if (status < B_OK)
207 return status;
209 port_info info;
210 status = get_port_info(fMsgPort, &info);
211 if (status == B_OK)
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);
218 return status;
222 status_t
223 BLooper::PostMessage(uint32 command)
225 BMessage message(command);
226 return _PostMessage(&message, this, NULL);
230 status_t
231 BLooper::PostMessage(BMessage* message)
233 return _PostMessage(message, this, NULL);
237 status_t
238 BLooper::PostMessage(uint32 command, BHandler* handler, BHandler* replyTo)
240 BMessage message(command);
241 return _PostMessage(&message, handler, replyTo);
245 status_t
246 BLooper::PostMessage(BMessage* message, BHandler* handler, BHandler* replyTo)
248 return _PostMessage(message, handler, replyTo);
252 void
253 BLooper::DispatchMessage(BMessage* message, BHandler* handler)
255 PRINT(("BLooper::DispatchMessage(%.4s)\n", (char*)&message->what));
257 switch (message->what) {
258 case _QUIT_:
259 // Can't call Quit() to do this, because of the slight chance
260 // another thread with have us locked between now and then.
261 fTerminating = true;
263 // After returning from DispatchMessage(), the looper will be
264 // deleted in _task0_()
265 break;
267 case B_QUIT_REQUESTED:
268 if (handler == this) {
269 _QuitRequested(message);
270 break;
273 // fall through
275 default:
276 handler->MessageReceived(message);
277 break;
279 PRINT(("BLooper::DispatchMessage() done\n"));
283 void
284 BLooper::MessageReceived(BMessage* message)
286 // TODO: implement scripting support
287 BHandler::MessageReceived(message);
291 BMessage*
292 BLooper::CurrentMessage() const
294 return fLastMessage;
298 BMessage*
299 BLooper::DetachCurrentMessage()
301 BMessage* message = fLastMessage;
302 fLastMessage = NULL;
303 return message;
307 void
308 BLooper::DispatchExternalMessage(BMessage* message, BHandler* handler,
309 bool& _detached)
311 AssertLocked();
313 BMessage* previousMessage = fLastMessage;
314 fLastMessage = message;
316 DispatchMessage(message, handler);
318 _detached = fLastMessage == NULL;
319 fLastMessage = previousMessage;
323 BMessageQueue*
324 BLooper::MessageQueue() const
326 return fDirectTarget->Queue();
330 bool
331 BLooper::IsMessageWaiting() const
333 AssertLocked();
335 if (!fDirectTarget->Queue()->IsEmpty())
336 return true;
338 int32 count;
339 do {
340 count = port_buffer_size_etc(fMsgPort, B_RELATIVE_TIMEOUT, 0);
341 } while (count == B_INTERRUPTED);
343 return count > 0;
347 void
348 BLooper::AddHandler(BHandler* handler)
350 if (handler == NULL)
351 return;
353 AssertLocked();
355 if (handler->Looper() == NULL) {
356 fHandlers.AddItem(handler);
357 handler->SetLooper(this);
358 if (handler != this) // avoid a cycle
359 handler->SetNextHandler(this);
364 bool
365 BLooper::RemoveHandler(BHandler* handler)
367 if (handler == NULL)
368 return false;
370 AssertLocked();
372 if (handler->Looper() == this && fHandlers.RemoveItem(handler)) {
373 if (handler == fPreferred)
374 fPreferred = NULL;
376 handler->SetNextHandler(NULL);
377 handler->SetLooper(NULL);
378 return true;
381 return false;
385 int32
386 BLooper::CountHandlers() const
388 AssertLocked();
390 return fHandlers.CountItems();
394 BHandler*
395 BLooper::HandlerAt(int32 index) const
397 AssertLocked();
399 return (BHandler*)fHandlers.ItemAt(index);
403 int32
404 BLooper::IndexOf(BHandler* handler) const
406 AssertLocked();
408 return fHandlers.IndexOf(handler);
412 BHandler*
413 BLooper::PreferredHandler() const
415 return fPreferred;
419 void
420 BLooper::SetPreferredHandler(BHandler* handler)
422 if (handler && handler->Looper() == this && IndexOf(handler) >= 0) {
423 fPreferred = handler;
424 } else {
425 fPreferred = NULL;
430 thread_id
431 BLooper::Run()
433 AssertLocked();
435 if (fRunCalled) {
436 // Not allowed to call Run() more than once
437 debugger("can't call BLooper::Run twice!");
438 return fThread;
441 fThread = spawn_thread(_task0_, Name(), fInitPriority, this);
442 if (fThread < B_OK)
443 return fThread;
445 if (fMsgPort < B_OK)
446 return fMsgPort;
448 fRunCalled = true;
449 Unlock();
451 status_t err = resume_thread(fThread);
452 if (err < B_OK)
453 return err;
455 return fThread;
459 void
460 BLooper::Quit()
462 PRINT(("BLooper::Quit()\n"));
464 if (!IsLocked()) {
465 printf("ERROR - you must Lock a looper before calling Quit(), "
466 "team=%" B_PRId32 ", looper=%s\n", Team(),
467 Name() ? Name() : "unnamed");
470 // Try to lock
471 if (!Lock()) {
472 // We're toast already
473 return;
476 PRINT((" is locked\n"));
478 if (!fRunCalled) {
479 PRINT((" Run() has not been called yet\n"));
480 fTerminating = true;
481 delete this;
482 } else if (find_thread(NULL) == fThread) {
483 PRINT((" We are the looper thread\n"));
484 fTerminating = true;
485 delete this;
486 exit_thread(0);
487 } else {
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.
496 UnlockFully();
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.
503 PostMessage(_QUIT_);
505 // We have to wait until the looper is done processing any remaining
506 // messages.
507 status_t status;
508 while (wait_for_thread(thread, &status) == B_INTERRUPTED)
512 PRINT(("BLooper::Quit() done\n"));
516 bool
517 BLooper::QuitRequested()
519 return true;
523 bool
524 BLooper::Lock()
526 // Defer to global _Lock(); see notes there
527 return _Lock(this, -1, B_INFINITE_TIMEOUT) == B_OK;
531 void
532 BLooper::Unlock()
534 PRINT(("BLooper::Unlock()\n"));
535 // Make sure we're locked to begin with
536 AssertLocked();
538 // Decrement fOwnerCount
539 --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)
544 fOwner = -1;
545 fCachedStack = 0;
547 #if DEBUG < 1
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
554 if (atomicCount > 1)
555 #endif
556 release_sem(fLockSem);
558 PRINT(("BLooper::Unlock() done\n"));
562 bool
563 BLooper::IsLocked() const
565 if (!gLooperList.IsLooperValid(this)) {
566 // The looper is gone, so of course it's not locked
567 return false;
570 uint32 stack;
571 return ((addr_t)&stack & ~(B_PAGE_SIZE - 1)) == fCachedStack
572 || find_thread(NULL) == fOwner;
576 status_t
577 BLooper::LockWithTimeout(bigtime_t timeout)
579 return _Lock(this, -1, timeout);
583 thread_id
584 BLooper::Thread() const
586 return fThread;
590 team_id
591 BLooper::Team() const
593 return BPrivate::current_team();
597 BLooper*
598 BLooper::LooperForThread(thread_id thread)
600 return gLooperList.LooperForThread(thread);
604 thread_id
605 BLooper::LockingThread() const
607 return fOwner;
611 int32
612 BLooper::CountLocks() const
614 return fOwnerCount;
618 int32
619 BLooper::CountLockRequests() const
621 return fAtomicCount;
625 sem_id
626 BLooper::Sem() const
628 return fLockSem;
632 BHandler*
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);
649 uint32 data;
650 status_t err = B_OK;
651 const char* errMsg = "";
652 if (propertyInfo.FindMatch(message, index, specifier, what, property, &data)
653 >= 0) {
654 switch (data) {
655 case BLOOPER_PROCESS_INTERNALLY:
656 return this;
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);
665 if (target) {
666 // Specifier has been fully handled
667 message->PopSpecifier();
668 return target;
669 } else {
670 err = B_BAD_INDEX;
671 errMsg = "handler index out of range";
673 break;
676 default:
677 err = B_BAD_SCRIPT_SYNTAX;
678 errMsg = "Didn't understand the specifier(s)";
680 } else {
681 return BHandler::ResolveSpecifier(message, index, specifier, what,
682 property);
685 BMessage reply(B_MESSAGE_NOT_UNDERSTOOD);
686 reply.AddInt32("error", err);
687 reply.AddString("message", errMsg);
688 message->SendReply(&reply);
690 return NULL;
694 status_t
695 BLooper::GetSupportedSuites(BMessage* data)
697 if (data == NULL)
698 return B_BAD_VALUE;
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);
704 if (status == B_OK)
705 status = BHandler::GetSupportedSuites(data);
708 return status;
712 void
713 BLooper::AddCommonFilter(BMessageFilter* filter)
715 if (filter == NULL)
716 return;
718 AssertLocked();
720 if (filter->Looper()) {
721 debugger("A MessageFilter can only be used once.");
722 return;
725 if (fCommonFilters == NULL)
726 fCommonFilters = new BList(FILTER_LIST_BLOCK_SIZE);
728 filter->SetLooper(this);
729 fCommonFilters->AddItem(filter);
733 bool
734 BLooper::RemoveCommonFilter(BMessageFilter* filter)
736 AssertLocked();
738 if (fCommonFilters == NULL)
739 return false;
741 bool result = fCommonFilters->RemoveItem(filter);
742 if (result)
743 filter->SetLooper(NULL);
745 return result;
749 void
750 BLooper::SetCommonFilterList(BList* filters)
752 AssertLocked();
754 BMessageFilter* filter;
755 if (filters) {
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.");
761 return;
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);
786 BList*
787 BLooper::CommonFilterList() const
789 return fCommonFilters;
793 status_t
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);
801 BMessage*
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
822 BLooper&
823 BLooper::operator=(const BLooper& other)
825 // Looper copying not allowed
826 return *this;
830 BLooper::BLooper(int32 priority, port_id port, const char* name)
832 _InitData(name, priority, port, B_LOOPER_PORT_DEFAULT_CAPACITY);
836 status_t
837 BLooper::_PostMessage(BMessage* msg, BHandler* handler, BHandler* replyTo)
839 status_t status;
840 BMessenger messenger(handler, this, &status);
841 if (status == B_OK)
842 return messenger.SendMessage(msg, replyTo, 0);
844 return status;
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
855 status_t
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"));
863 return B_BAD_VALUE;
866 thread_id currentThread = find_thread(NULL);
867 int32 oldCount;
868 sem_id sem;
871 AutoLocker<BLooperList> ListLock(gLooperList);
872 if (!ListLock.IsLocked())
873 return B_BAD_VALUE;
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"));
880 return B_BAD_VALUE;
882 } else if (!gLooperList.IsLooperValid(looper)) {
883 // Check looper validity
884 PRINT(("BLooper::_Lock() done 4\n"));
885 return B_BAD_VALUE;
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));
892 return B_OK;
895 // Cache the semaphore, so that we can safely access it after having
896 // unlocked the looper list
897 sem = looper->fLockSem;
898 if (sem < 0) {
899 PRINT(("BLooper::_Lock() done 6\n"));
900 return B_BAD_VALUE;
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);
911 status_t
912 BLooper::_LockComplete(BLooper* looper, int32 oldCount, thread_id thread,
913 sem_id sem, bigtime_t timeout)
915 status_t err = B_OK;
917 #if DEBUG < 1
918 if (oldCount > 0) {
919 #endif
920 do {
921 err = acquire_sem_etc(sem, 1, B_RELATIVE_TIMEOUT, timeout);
922 } while (err == B_INTERRUPTED);
923 #if DEBUG < 1
925 #endif
926 if (err == B_OK) {
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));
933 return err;
937 void
938 BLooper::_InitData(const char* name, int32 priority, port_id port,
939 int32 portCapacity)
941 fOwner = B_ERROR;
942 fCachedStack = 0;
943 fRunCalled = false;
944 fDirectTarget = new (std::nothrow) BPrivate::BDirectMessageTarget();
945 fCommonFilters = NULL;
946 fLastMessage = NULL;
947 fPreferred = NULL;
948 fThread = B_ERROR;
949 fTerminating = false;
950 fOwnsPort = true;
951 fMsgPort = -1;
952 fAtomicCount = 0;
954 if (name == NULL)
955 name = "anonymous looper";
957 #if DEBUG
958 fLockSem = create_sem(1, name);
959 #else
960 fLockSem = create_sem(0, name);
961 #endif
963 if (portCapacity <= 0)
964 portCapacity = B_LOOPER_PORT_DEFAULT_CAPACITY;
966 if (port >= 0)
967 fMsgPort = port;
968 else
969 fMsgPort = create_port(portCapacity, name);
971 fInitPriority = priority;
973 gLooperList.AddLooper(this);
974 // this will also lock this looper
976 AddHandler(this);
980 void
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
990 // looper
991 write_port_etc(fMsgPort, 0, NULL, 0, B_RELATIVE_TIMEOUT, 0);
996 void
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);
1007 status_t
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();
1018 delete looper;
1021 PRINT(("LOOPER: _task0_() done: thread %ld\n", find_thread(NULL)));
1022 return B_OK;
1026 void*
1027 BLooper::ReadRawFromPort(int32* msgCode, bigtime_t timeout)
1029 PRINT(("BLooper::ReadRawFromPort()\n"));
1030 uint8* buffer = NULL;
1031 ssize_t bufferSize;
1033 do {
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));
1039 return NULL;
1042 if (bufferSize > 0)
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) {
1053 free(buffer);
1054 return NULL;
1057 PRINT(("BLooper::ReadRawFromPort() read: %.4s, %p (%d bytes)\n",
1058 (char*)msgCode, buffer, bufferSize));
1060 return buffer;
1064 BMessage*
1065 BLooper::ReadMessageFromPort(bigtime_t timeout)
1067 PRINT(("BLooper::ReadMessageFromPort()\n"));
1068 int32 msgCode;
1069 BMessage* message = NULL;
1071 void* buffer = ReadRawFromPort(&msgCode, timeout);
1072 if (buffer == NULL)
1073 return NULL;
1075 message = ConvertToMessage(buffer, msgCode);
1076 free(buffer);
1078 PRINT(("BLooper::ReadMessageFromPort() done: %p\n", message));
1079 return message;
1083 BMessage*
1084 BLooper::ConvertToMessage(void* buffer, int32 code)
1086 PRINT(("BLooper::ConvertToMessage()\n"));
1087 if (buffer == NULL)
1088 return NULL;
1090 BMessage* message = new BMessage();
1091 if (message->Unflatten((const char*)buffer) != B_OK) {
1092 PRINT(("BLooper::ConvertToMessage(): unflattening message failed\n"));
1093 delete message;
1094 message = NULL;
1097 PRINT(("BLooper::ConvertToMessage(): %p\n", message));
1098 return message;
1102 void
1103 BLooper::task_looper()
1105 PRINT(("BLooper::task_looper()\n"));
1106 // Check that looper is locked (should be)
1107 AssertLocked();
1108 // Unlock the looper
1109 Unlock();
1111 if (IsLocked())
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?
1124 if (msg)
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);
1133 if (msg)
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
1143 // locking)
1144 BMessage* message = fDirectTarget->Queue()->NextMessage();
1146 Lock();
1148 fLastMessage = message;
1150 if (fLastMessage == NULL) {
1151 // No more messages: Unlock the looper and terminate the
1152 // dispatch loop.
1153 dispatchNextMessage = false;
1154 } else {
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();
1164 if (usePreferred) {
1165 PRINT(("LOOPER: use preferred target\n"));
1166 handler = fPreferred;
1167 if (handler == NULL)
1168 handler = this;
1169 } else {
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)
1175 handler = NULL;
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()) {
1183 int32 index = 0;
1184 // Make sure the current specifier is kosher
1185 if (fLastMessage->GetCurrentSpecifier(&index) == B_OK)
1186 handler = resolve_specifier(handler, fLastMessage);
1189 if (handler) {
1190 // Do filtering
1191 handler = _TopLevelFilter(fLastMessage, handler);
1192 PRINT(("LOOPER: _TopLevelFilter(): %p\n", handler));
1193 if (handler && handler->Looper() == this)
1194 DispatchMessage(fLastMessage, handler);
1198 if (fTerminating) {
1199 // we leave the looper locked when we quit
1200 return;
1203 message = fLastMessage;
1204 fLastMessage = NULL;
1206 // Unlock the looper
1207 Unlock();
1209 // Delete the current message (fLastMessage)
1210 if (message != NULL)
1211 delete message;
1213 // Are any messages on the port?
1214 if (port_count(fMsgPort) > 0) {
1215 // Do outer loop
1216 dispatchNextMessage = false;
1220 PRINT(("BLooper::task_looper() done\n"));
1224 void
1225 BLooper::_QuitRequested(BMessage* message)
1227 bool isQuitting = QuitRequested();
1228 int32 thread = fThread;
1230 if (isQuitting)
1231 Quit();
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.
1237 bool shutdown;
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);
1248 bool
1249 BLooper::AssertLocked() const
1251 if (!IsLocked()) {
1252 debugger("looper must be locked before proceeding\n");
1253 return false;
1256 return true;
1260 BHandler*
1261 BLooper::_TopLevelFilter(BMessage* message, BHandler* target)
1263 if (message == NULL)
1264 return target;
1266 // Apply the common filters first
1267 target = _ApplyFilters(CommonFilterList(), message, target);
1268 if (target) {
1269 if (target->Looper() != this) {
1270 debugger("Targeted handler does not belong to the looper.");
1271 target = NULL;
1272 } else {
1273 // Now apply handler-specific filters
1274 target = _HandlerFilter(message, target);
1278 return target;
1282 BHandler*
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.");
1294 target = NULL;
1298 return target;
1302 BHandler*
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)
1309 return target;
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);
1335 else
1336 result = filter->Filter(message, &target);
1338 // Is further processing allowed?
1339 if (result == B_SKIP_MESSAGE) {
1340 // no, time to bail out
1341 return NULL;
1348 return target;
1352 void
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
1358 uint32 stack;
1359 if (((addr_t)&stack & ~(B_PAGE_SIZE - 1)) == fCachedStack
1360 || fOwner == find_thread(NULL)) {
1361 return;
1364 debugger("Looper must be locked.");
1368 BHandler*
1369 BLooper::resolve_specifier(BHandler* target, BMessage* message)
1371 // check params
1372 if (!target || !message)
1373 return NULL;
1375 int32 index;
1376 BMessage specifier;
1377 int32 form;
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)
1383 do {
1384 err = message->GetCurrentSpecifier(&index, &specifier, &form,
1385 &property);
1386 if (err != B_OK) {
1387 BMessage reply(B_REPLY);
1388 reply.AddInt32("error", err);
1389 message->SendReply(&reply);
1390 return NULL;
1392 // current target gets what was the new target
1393 target = newTarget;
1394 newTarget = target->ResolveSpecifier(message, index, &specifier, form,
1395 property);
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)
1400 return NULL;
1402 // get current specifier index (may change in ResolveSpecifier())
1403 err = message->GetCurrentSpecifier(&index);
1404 } while (newTarget && newTarget != target && err == B_OK && index >= 0);
1406 return newTarget;
1410 /*! Releases all eventually nested locks. Must be called with the lock
1411 actually held.
1413 void
1414 BLooper::UnlockFully()
1416 AssertLocked();
1418 // Clear the owner count
1419 fOwnerCount = 0;
1420 // Nobody owns the lock now
1421 fOwner = -1;
1422 fCachedStack = 0;
1423 #if DEBUG < 1
1424 // There is now one less thread holding a lock on this looper
1425 int32 atomicCount = atomic_add(&fAtomicCount, -1);
1426 if (atomicCount > 1)
1427 #endif
1428 release_sem(fLockSem);
1432 // #pragma mark -
1435 port_id
1436 _get_looper_port_(const BLooper* looper)
1438 return looper->fMsgPort;