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 }; 99 100 struct _loop_data_ { 101 BLooper* looper; 102 thread_id thread; 103 }; 104 105 106 // #pragma mark - 107 108 109 BLooper::BLooper(const char* name, int32 priority, int32 portCapacity) 110 : 111 BHandler(name) 112 { 113 _InitData(name, priority, -1, portCapacity); 114 } 115 116 117 BLooper::~BLooper() 118 { 119 if (fRunCalled && !fTerminating) { 120 debugger("You can't call delete on a BLooper object " 121 "once it is running."); 122 } 123 124 Lock(); 125 126 // In case the looper thread calls Quit() fLastMessage is not deleted. 127 if (fLastMessage) { 128 delete fLastMessage; 129 fLastMessage = NULL; 130 } 131 132 // Close the message port and read and reply to the remaining messages. 133 if (fMsgPort >= 0 && fOwnsPort) 134 close_port(fMsgPort); 135 136 // Clear the queue so our call to IsMessageWaiting() below doesn't give 137 // us bogus info 138 fDirectTarget->Close(); 139 140 BMessage* message; 141 while ((message = fDirectTarget->Queue()->NextMessage()) != NULL) { 142 delete message; 143 // msg will automagically post generic reply 144 } 145 146 if (fOwnsPort) { 147 do { 148 delete ReadMessageFromPort(0); 149 // msg will automagically post generic reply 150 } while (IsMessageWaiting()); 151 152 delete_port(fMsgPort); 153 } 154 fDirectTarget->Release(); 155 156 // Clean up our filters 157 SetCommonFilterList(NULL); 158 159 AutoLocker<BLooperList> ListLock(gLooperList); 160 RemoveHandler(this); 161 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); 168 } 169 fHandlers.MakeEmpty(); 170 171 Unlock(); 172 gLooperList.RemoveLooper(this); 173 delete_sem(fLockSem); 174 } 175 176 177 BLooper::BLooper(BMessage* data) 178 : BHandler(data) 179 { 180 int32 portCapacity; 181 if (data->FindInt32("_port_cap", &portCapacity) != B_OK || portCapacity < 0) 182 portCapacity = B_LOOPER_PORT_DEFAULT_CAPACITY; 183 184 int32 priority; 185 if (data->FindInt32("_prio", &priority) != B_OK) 186 priority = B_NORMAL_PRIORITY; 187 188 _InitData(Name(), priority, -1, portCapacity); 189 } 190 191 192 BArchivable* 193 BLooper::Instantiate(BMessage* data) 194 { 195 if (validate_instantiation(data, "BLooper")) 196 return new BLooper(data); 197 198 return NULL; 199 } 200 201 202 status_t 203 BLooper::Archive(BMessage* data, bool deep) const 204 { 205 status_t status = BHandler::Archive(data, deep); 206 if (status < B_OK) 207 return status; 208 209 port_info info; 210 status = get_port_info(fMsgPort, &info); 211 if (status == B_OK) 212 status = data->AddInt32("_port_cap", info.capacity); 213 214 thread_info threadInfo; 215 if (get_thread_info(Thread(), &threadInfo) == B_OK) 216 status = data->AddInt32("_prio", threadInfo.priority); 217 218 return status; 219 } 220 221 222 status_t 223 BLooper::PostMessage(uint32 command) 224 { 225 BMessage message(command); 226 return _PostMessage(&message, this, NULL); 227 } 228 229 230 status_t 231 BLooper::PostMessage(BMessage* message) 232 { 233 return _PostMessage(message, this, NULL); 234 } 235 236 237 status_t 238 BLooper::PostMessage(uint32 command, BHandler* handler, BHandler* replyTo) 239 { 240 BMessage message(command); 241 return _PostMessage(&message, handler, replyTo); 242 } 243 244 245 status_t 246 BLooper::PostMessage(BMessage* message, BHandler* handler, BHandler* replyTo) 247 { 248 return _PostMessage(message, handler, replyTo); 249 } 250 251 252 void 253 BLooper::DispatchMessage(BMessage* message, BHandler* handler) 254 { 255 PRINT(("BLooper::DispatchMessage(%.4s)\n", (char*)&message->what)); 256 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; 262 263 // After returning from DispatchMessage(), the looper will be 264 // deleted in _task0_() 265 break; 266 267 case B_QUIT_REQUESTED: 268 if (handler == this) { 269 _QuitRequested(message); 270 break; 271 } 272 273 // fall through 274 275 default: 276 handler->MessageReceived(message); 277 break; 278 } 279 PRINT(("BLooper::DispatchMessage() done\n")); 280 } 281 282 283 void 284 BLooper::MessageReceived(BMessage* message) 285 { 286 // TODO: implement scripting support 287 BHandler::MessageReceived(message); 288 } 289 290 291 BMessage* 292 BLooper::CurrentMessage() const 293 { 294 return fLastMessage; 295 } 296 297 298 BMessage* 299 BLooper::DetachCurrentMessage() 300 { 301 BMessage* message = fLastMessage; 302 fLastMessage = NULL; 303 return message; 304 } 305 306 307 void 308 BLooper::DispatchExternalMessage(BMessage* message, BHandler* handler, 309 bool& _detached) 310 { 311 AssertLocked(); 312 313 BMessage* previousMessage = fLastMessage; 314 fLastMessage = message; 315 316 DispatchMessage(message, handler); 317 318 _detached = fLastMessage == NULL; 319 fLastMessage = previousMessage; 320 } 321 322 323 BMessageQueue* 324 BLooper::MessageQueue() const 325 { 326 return fDirectTarget->Queue(); 327 } 328 329 330 bool 331 BLooper::IsMessageWaiting() const 332 { 333 AssertLocked(); 334 335 if (!fDirectTarget->Queue()->IsEmpty()) 336 return true; 337 338 int32 count; 339 do { 340 count = port_buffer_size_etc(fMsgPort, B_RELATIVE_TIMEOUT, 0); 341 } while (count == B_INTERRUPTED); 342 343 return count > 0; 344 } 345 346 347 void 348 BLooper::AddHandler(BHandler* handler) 349 { 350 if (handler == NULL) 351 return; 352 353 AssertLocked(); 354 355 if (handler->Looper() == NULL) { 356 fHandlers.AddItem(handler); 357 handler->SetLooper(this); 358 if (handler != this) // avoid a cycle 359 handler->SetNextHandler(this); 360 } 361 } 362 363 364 bool 365 BLooper::RemoveHandler(BHandler* handler) 366 { 367 if (handler == NULL) 368 return false; 369 370 AssertLocked(); 371 372 if (handler->Looper() == this && fHandlers.RemoveItem(handler)) { 373 if (handler == fPreferred) 374 fPreferred = NULL; 375 376 handler->SetNextHandler(NULL); 377 handler->SetLooper(NULL); 378 return true; 379 } 380 381 return false; 382 } 383 384 385 int32 386 BLooper::CountHandlers() const 387 { 388 AssertLocked(); 389 390 return fHandlers.CountItems(); 391 } 392 393 394 BHandler* 395 BLooper::HandlerAt(int32 index) const 396 { 397 AssertLocked(); 398 399 return (BHandler*)fHandlers.ItemAt(index); 400 } 401 402 403 int32 404 BLooper::IndexOf(BHandler* handler) const 405 { 406 AssertLocked(); 407 408 return fHandlers.IndexOf(handler); 409 } 410 411 412 BHandler* 413 BLooper::PreferredHandler() const 414 { 415 return fPreferred; 416 } 417 418 419 void 420 BLooper::SetPreferredHandler(BHandler* handler) 421 { 422 if (handler && handler->Looper() == this && IndexOf(handler) >= 0) { 423 fPreferred = handler; 424 } else { 425 fPreferred = NULL; 426 } 427 } 428 429 430 thread_id 431 BLooper::Run() 432 { 433 AssertLocked(); 434 435 if (fRunCalled) { 436 // Not allowed to call Run() more than once 437 debugger("can't call BLooper::Run twice!"); 438 return fThread; 439 } 440 441 fThread = spawn_thread(_task0_, Name(), fInitPriority, this); 442 if (fThread < B_OK) 443 return fThread; 444 445 if (fMsgPort < B_OK) 446 return fMsgPort; 447 448 fRunCalled = true; 449 Unlock(); 450 451 status_t err = resume_thread(fThread); 452 if (err < B_OK) 453 return err; 454 455 return fThread; 456 } 457 458 459 void 460 BLooper::Quit() 461 { 462 PRINT(("BLooper::Quit()\n")); 463 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"); 468 } 469 470 // Try to lock 471 if (!Lock()) { 472 // We're toast already 473 return; 474 } 475 476 PRINT((" is locked\n")); 477 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")); 489 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(); 493 494 // We need to unlock here. Otherwise the looper thread can't 495 // dispatch the _QUIT_ message we're going to post. 496 UnlockFully(); 497 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_); 504 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) 509 ; 510 } 511 512 PRINT(("BLooper::Quit() done\n")); 513 } 514 515 516 bool 517 BLooper::QuitRequested() 518 { 519 return true; 520 } 521 522 523 bool 524 BLooper::Lock() 525 { 526 // Defer to global _Lock(); see notes there 527 return _Lock(this, -1, B_INFINITE_TIMEOUT) == B_OK; 528 } 529 530 531 void 532 BLooper::Unlock() 533 { 534 PRINT(("BLooper::Unlock()\n")); 535 // Make sure we're locked to begin with 536 AssertLocked(); 537 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; 546 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)); 551 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); 557 } 558 PRINT(("BLooper::Unlock() done\n")); 559 } 560 561 562 bool 563 BLooper::IsLocked() const 564 { 565 if (!gLooperList.IsLooperValid(this)) { 566 // The looper is gone, so of course it's not locked 567 return false; 568 } 569 570 uint32 stack; 571 return ((addr_t)&stack & ~(B_PAGE_SIZE - 1)) == fCachedStack 572 || find_thread(NULL) == fOwner; 573 } 574 575 576 status_t 577 BLooper::LockWithTimeout(bigtime_t timeout) 578 { 579 return _Lock(this, -1, timeout); 580 } 581 582 583 thread_id 584 BLooper::Thread() const 585 { 586 return fThread; 587 } 588 589 590 team_id 591 BLooper::Team() const 592 { 593 return BPrivate::current_team(); 594 } 595 596 597 BLooper* 598 BLooper::LooperForThread(thread_id thread) 599 { 600 return gLooperList.LooperForThread(thread); 601 } 602 603 604 thread_id 605 BLooper::LockingThread() const 606 { 607 return fOwner; 608 } 609 610 611 int32 612 BLooper::CountLocks() const 613 { 614 return fOwnerCount; 615 } 616 617 618 int32 619 BLooper::CountLockRequests() const 620 { 621 return fAtomicCount; 622 } 623 624 625 sem_id 626 BLooper::Sem() const 627 { 628 return fLockSem; 629 } 630 631 632 BHandler* 633 BLooper::ResolveSpecifier(BMessage* message, int32 index, BMessage* specifier, 634 int32 what, const char* property) 635 { 636 /** 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. 647 */ 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; 657 658 case BLOOPER_HANDLER_BY_INDEX: 659 { 660 int32 index = specifier->FindInt32("index"); 661 if (what == B_REVERSE_INDEX_SPECIFIER) { 662 index = CountHandlers() - index; 663 } 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"; 672 } 673 break; 674 } 675 676 default: 677 err = B_BAD_SCRIPT_SYNTAX; 678 errMsg = "Didn't understand the specifier(s)"; 679 } 680 } else { 681 return BHandler::ResolveSpecifier(message, index, specifier, what, 682 property); 683 } 684 685 BMessage reply(B_MESSAGE_NOT_UNDERSTOOD); 686 reply.AddInt32("error", err); 687 reply.AddString("message", errMsg); 688 message->SendReply(&reply); 689 690 return NULL; 691 } 692 693 694 status_t 695 BLooper::GetSupportedSuites(BMessage* data) 696 { 697 if (data == NULL) 698 return B_BAD_VALUE; 699 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); 706 } 707 708 return status; 709 } 710 711 712 void 713 BLooper::AddCommonFilter(BMessageFilter* filter) 714 { 715 if (filter == NULL) 716 return; 717 718 AssertLocked(); 719 720 if (filter->Looper()) { 721 debugger("A MessageFilter can only be used once."); 722 return; 723 } 724 725 if (fCommonFilters == NULL) 726 fCommonFilters = new BList(FILTER_LIST_BLOCK_SIZE); 727 728 filter->SetLooper(this); 729 fCommonFilters->AddItem(filter); 730 } 731 732 733 bool 734 BLooper::RemoveCommonFilter(BMessageFilter* filter) 735 { 736 AssertLocked(); 737 738 if (fCommonFilters == NULL) 739 return false; 740 741 bool result = fCommonFilters->RemoveItem(filter); 742 if (result) 743 filter->SetLooper(NULL); 744 745 return result; 746 } 747 748 749 void 750 BLooper::SetCommonFilterList(BList* filters) 751 { 752 AssertLocked(); 753 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; 762 } 763 } 764 } 765 766 if (fCommonFilters) { 767 for (int32 i = 0; i < fCommonFilters->CountItems(); ++i) { 768 delete (BMessageFilter*)fCommonFilters->ItemAt(i); 769 } 770 771 delete fCommonFilters; 772 fCommonFilters = NULL; 773 } 774 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); 781 } 782 } 783 } 784 785 786 BList* 787 BLooper::CommonFilterList() const 788 { 789 return fCommonFilters; 790 } 791 792 793 status_t 794 BLooper::Perform(perform_code d, void* arg) 795 { 796 // This is sort of what we're doing for this function everywhere 797 return BHandler::Perform(d, arg); 798 } 799 800 801 BMessage* 802 BLooper::MessageFromPort(bigtime_t timeout) 803 { 804 return ReadMessageFromPort(timeout); 805 } 806 807 808 void BLooper::_ReservedLooper1() {} 809 void BLooper::_ReservedLooper2() {} 810 void BLooper::_ReservedLooper3() {} 811 void BLooper::_ReservedLooper4() {} 812 void BLooper::_ReservedLooper5() {} 813 void BLooper::_ReservedLooper6() {} 814 815 816 BLooper::BLooper(const BLooper& other) 817 { 818 // Copy construction not allowed 819 } 820 821 822 BLooper& 823 BLooper::operator=(const BLooper& other) 824 { 825 // Looper copying not allowed 826 return *this; 827 } 828 829 830 BLooper::BLooper(int32 priority, port_id port, const char* name) 831 { 832 _InitData(name, priority, port, B_LOOPER_PORT_DEFAULT_CAPACITY); 833 } 834 835 836 status_t 837 BLooper::_PostMessage(BMessage* msg, BHandler* handler, BHandler* replyTo) 838 { 839 status_t status; 840 BMessenger messenger(handler, this, &status); 841 if (status == B_OK) 842 return messenger.SendMessage(msg, replyTo, 0); 843 844 return status; 845 } 846 847 848 /*! 849 Locks a looper either by port or using a direct pointer to the looper. 850 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 854 */ 855 status_t 856 BLooper::_Lock(BLooper* looper, port_id port, bigtime_t timeout) 857 { 858 PRINT(("BLooper::_Lock(%p, %lx)\n", looper, port)); 859 860 // Check params (loop, port) 861 if (looper == NULL && port < 0) { 862 PRINT(("BLooper::_Lock() done 1\n")); 863 return B_BAD_VALUE; 864 } 865 866 thread_id currentThread = find_thread(NULL); 867 int32 oldCount; 868 sem_id sem; 869 870 { 871 AutoLocker<BLooperList> ListLock(gLooperList); 872 if (!ListLock.IsLocked()) 873 return B_BAD_VALUE; 874 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; 881 } 882 } else if (!gLooperList.IsLooperValid(looper)) { 883 // Check looper validity 884 PRINT(("BLooper::_Lock() done 4\n")); 885 return B_BAD_VALUE; 886 } 887 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; 893 } 894 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; 901 } 902 903 // Bump the requested lock count (using fAtomicCount for this) 904 oldCount = atomic_add(&looper->fAtomicCount, 1); 905 } 906 907 return _LockComplete(looper, oldCount, currentThread, sem, timeout); 908 } 909 910 911 status_t 912 BLooper::_LockComplete(BLooper* looper, int32 oldCount, thread_id thread, 913 sem_id sem, bigtime_t timeout) 914 { 915 status_t err = B_OK; 916 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 924 } 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; 930 } 931 932 PRINT(("BLooper::_LockComplete() done: %lx\n", err)); 933 return err; 934 } 935 936 937 void 938 BLooper::_InitData(const char* name, int32 priority, port_id port, 939 int32 portCapacity) 940 { 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; 953 954 if (name == NULL) 955 name = "anonymous looper"; 956 957 #if DEBUG 958 fLockSem = create_sem(1, name); 959 #else 960 fLockSem = create_sem(0, name); 961 #endif 962 963 if (portCapacity <= 0) 964 portCapacity = B_LOOPER_PORT_DEFAULT_CAPACITY; 965 966 if (port >= 0) 967 fMsgPort = port; 968 else 969 fMsgPort = create_port(portCapacity, name); 970 971 fInitPriority = priority; 972 973 gLooperList.AddLooper(this); 974 // this will also lock this looper 975 976 AddHandler(this); 977 } 978 979 980 void 981 BLooper::AddMessage(BMessage* message) 982 { 983 _AddMessagePriv(message); 984 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); 992 } 993 } 994 995 996 void 997 BLooper::_AddMessagePriv(BMessage* message) 998 { 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 1002 1003 fDirectTarget->Queue()->AddMessage(message); 1004 } 1005 1006 1007 status_t 1008 BLooper::_task0_(void* arg) 1009 { 1010 BLooper* looper = (BLooper*)arg; 1011 1012 PRINT(("LOOPER: _task0_()\n")); 1013 1014 if (looper->Lock()) { 1015 PRINT(("LOOPER: looper locked\n")); 1016 looper->task_looper(); 1017 1018 delete looper; 1019 } 1020 1021 PRINT(("LOOPER: _task0_() done: thread %ld\n", find_thread(NULL))); 1022 return B_OK; 1023 } 1024 1025 1026 void* 1027 BLooper::ReadRawFromPort(int32* msgCode, bigtime_t timeout) 1028 { 1029 PRINT(("BLooper::ReadRawFromPort()\n")); 1030 uint8* buffer = NULL; 1031 ssize_t bufferSize; 1032 1033 do { 1034 bufferSize = port_buffer_size_etc(fMsgPort, B_RELATIVE_TIMEOUT, timeout); 1035 } while (bufferSize == B_INTERRUPTED); 1036 1037 if (bufferSize < B_OK) { 1038 PRINT(("BLooper::ReadRawFromPort(): failed: %ld\n", bufferSize)); 1039 return NULL; 1040 } 1041 1042 if (bufferSize > 0) 1043 buffer = (uint8*)malloc(bufferSize); 1044 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); 1051 1052 if (bufferSize < B_OK) { 1053 free(buffer); 1054 return NULL; 1055 } 1056 1057 PRINT(("BLooper::ReadRawFromPort() read: %.4s, %p (%d bytes)\n", 1058 (char*)msgCode, buffer, bufferSize)); 1059 1060 return buffer; 1061 } 1062 1063 1064 BMessage* 1065 BLooper::ReadMessageFromPort(bigtime_t timeout) 1066 { 1067 PRINT(("BLooper::ReadMessageFromPort()\n")); 1068 int32 msgCode; 1069 BMessage* message = NULL; 1070 1071 void* buffer = ReadRawFromPort(&msgCode, timeout); 1072 if (buffer == NULL) 1073 return NULL; 1074 1075 message = ConvertToMessage(buffer, msgCode); 1076 free(buffer); 1077 1078 PRINT(("BLooper::ReadMessageFromPort() done: %p\n", message)); 1079 return message; 1080 } 1081 1082 1083 BMessage* 1084 BLooper::ConvertToMessage(void* buffer, int32 code) 1085 { 1086 PRINT(("BLooper::ConvertToMessage()\n")); 1087 if (buffer == NULL) 1088 return NULL; 1089 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; 1095 } 1096 1097 PRINT(("BLooper::ConvertToMessage(): %p\n", message)); 1098 return message; 1099 } 1100 1101 1102 void 1103 BLooper::task_looper() 1104 { 1105 PRINT(("BLooper::task_looper()\n")); 1106 // Check that looper is locked (should be) 1107 AssertLocked(); 1108 // Unlock the looper 1109 Unlock(); 1110 1111 if (IsLocked()) 1112 debugger("looper must not be locked!"); 1113 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")); 1122 1123 // Did we get a message? 1124 if (msg) 1125 _AddMessagePriv(msg); 1126 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); 1135 } 1136 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(); 1145 1146 Lock(); 1147 1148 fLastMessage = message; 1149 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()); 1158 1159 // Get the target handler 1160 BHandler* handler = NULL; 1161 BMessage::Private messagePrivate(fLastMessage); 1162 bool usePreferred = messagePrivate.UsePreferredTarget(); 1163 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); 1172 1173 // if this handler doesn't belong to us, we drop the message 1174 if (handler != NULL && handler->Looper() != this) 1175 handler = NULL; 1176 1177 PRINT(("LOOPER: use %ld, handler: %p, this: %p\n", 1178 messagePrivate.GetTarget(), handler, this)); 1179 } 1180 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); 1187 } 1188 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); 1195 } 1196 } 1197 1198 if (fTerminating) { 1199 // we leave the looper locked when we quit 1200 return; 1201 } 1202 1203 message = fLastMessage; 1204 fLastMessage = NULL; 1205 1206 // Unlock the looper 1207 Unlock(); 1208 1209 // Delete the current message (fLastMessage) 1210 if (message != NULL) 1211 delete message; 1212 1213 // Are any messages on the port? 1214 if (port_count(fMsgPort) > 0) { 1215 // Do outer loop 1216 dispatchNextMessage = false; 1217 } 1218 } 1219 } 1220 PRINT(("BLooper::task_looper() done\n")); 1221 } 1222 1223 1224 void 1225 BLooper::_QuitRequested(BMessage* message) 1226 { 1227 bool isQuitting = QuitRequested(); 1228 int32 thread = fThread; 1229 1230 if (isQuitting) 1231 Quit(); 1232 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); 1244 } 1245 } 1246 1247 1248 bool 1249 BLooper::AssertLocked() const 1250 { 1251 if (!IsLocked()) { 1252 debugger("looper must be locked before proceeding\n"); 1253 return false; 1254 } 1255 1256 return true; 1257 } 1258 1259 1260 BHandler* 1261 BLooper::_TopLevelFilter(BMessage* message, BHandler* target) 1262 { 1263 if (message == NULL) 1264 return target; 1265 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); 1275 } 1276 } 1277 1278 return target; 1279 } 1280 1281 1282 BHandler* 1283 BLooper::_HandlerFilter(BMessage* message, BHandler* target) 1284 { 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; 1290 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; 1295 } 1296 } 1297 1298 return target; 1299 } 1300 1301 1302 BHandler* 1303 BLooper::_ApplyFilters(BList* list, BMessage* message, BHandler* target) 1304 { 1305 // This is where the action is! 1306 1307 // check the parameters 1308 if (list == NULL || message == NULL) 1309 return target; 1310 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); 1315 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); 1337 1338 // Is further processing allowed? 1339 if (result == B_SKIP_MESSAGE) { 1340 // no, time to bail out 1341 return NULL; 1342 } 1343 } 1344 } 1345 } 1346 } 1347 1348 return target; 1349 } 1350 1351 1352 void 1353 BLooper::check_lock() 1354 { 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; 1362 } 1363 1364 debugger("Looper must be locked."); 1365 } 1366 1367 1368 BHandler* 1369 BLooper::resolve_specifier(BHandler* target, BMessage* message) 1370 { 1371 // check params 1372 if (!target || !message) 1373 return NULL; 1374 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; 1391 } 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; 1401 1402 // get current specifier index (may change in ResolveSpecifier()) 1403 err = message->GetCurrentSpecifier(&index); 1404 } while (newTarget && newTarget != target && err == B_OK && index >= 0); 1405 1406 return newTarget; 1407 } 1408 1409 1410 /*! Releases all eventually nested locks. Must be called with the lock 1411 actually held. 1412 */ 1413 void 1414 BLooper::UnlockFully() 1415 { 1416 AssertLocked(); 1417 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); 1429 } 1430 1431 1432 // #pragma mark - 1433 1434 1435 port_id 1436 _get_looper_port_(const BLooper* looper) 1437 { 1438 return looper->fMsgPort; 1439 } 1440