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