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