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