1 //------------------------------------------------------------------------------ 2 // Copyright (c) 2001-2005, Haiku 3 // 4 // Permission is hereby granted, free of charge, to any person obtaining a 5 // copy of this software and associated documentation files (the "Software"), 6 // to deal in the Software without restriction, including without limitation 7 // the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 // and/or sell copies of the Software, and to permit persons to whom the 9 // Software is furnished to do so, subject to the following conditions: 10 // 11 // The above copyright notice and this permission notice shall be included in 12 // all copies or substantial portions of the Software. 13 // 14 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 20 // DEALINGS IN THE SOFTWARE. 21 // 22 // File Name: Looper.cpp 23 // Author(s): Erik Jaesler (erik@cgsoftware.com) 24 // DarkWyrm (bpmagic@columbus.rr.com) 25 // Description: BLooper class spawns a thread that runs a message loop. 26 //------------------------------------------------------------------------------ 27 28 /** 29 @note Although I'm implementing "by the book" for now, I would like to 30 refactor sLooperList and all of the functions that operate on it 31 into their own class in the BPrivate namespace. 32 33 Also considering adding the thread priority when archiving. 34 35 36 */ 37 38 // debugging 39 //#define DBG(x) x 40 #define DBG(x) ; 41 #define PRINT(x) DBG({ printf("[%6ld] ", find_thread(NULL)); printf x; }) 42 43 /* 44 #include <Autolock.h> 45 #include <Locker.h> 46 static BLocker sDebugPrintLocker("BLooper debug print"); 47 #define PRINT(x) DBG({ \ 48 BAutolock _(sDebugPrintLocker); \ 49 debug_printf("[%6ld] ", find_thread(NULL)); \ 50 debug_printf x; \ 51 }) 52 */ 53 54 #include <stdio.h> 55 56 #include <Autolock.h> 57 #include <Looper.h> 58 #include <Message.h> 59 #include <MessageFilter.h> 60 #include <MessageQueue.h> 61 #include <Messenger.h> 62 #include <PropertyInfo.h> 63 64 #include <LooperList.h> 65 #include <ObjectLocker.h> 66 #include <TokenSpace.h> 67 68 69 #define FILTER_LIST_BLOCK_SIZE 5 70 #define DATA_BLOCK_SIZE 5 71 72 // Globals --------------------------------------------------------------------- 73 using BPrivate::gDefaultTokens; 74 using BPrivate::gLooperList; 75 using BPrivate::BObjectLocker; 76 using BPrivate::BLooperList; 77 78 port_id _get_looper_port_(const BLooper* looper); 79 bool _use_preferred_target_(BMessage* msg) { return msg->fPreferred; } 80 int32 _get_message_target_(BMessage* msg) { return msg->fTarget; } 81 82 uint32 BLooper::sLooperID = (uint32)B_ERROR; 83 team_id BLooper::sTeamID = (team_id)B_ERROR; 84 85 enum { 86 BLOOPER_PROCESS_INTERNALLY = 0, 87 BLOOPER_HANDLER_BY_INDEX 88 }; 89 90 static property_info gLooperPropInfo[] = { 91 { 92 "Handler", 93 {}, 94 {B_INDEX_SPECIFIER, B_REVERSE_INDEX_SPECIFIER}, 95 NULL, BLOOPER_HANDLER_BY_INDEX, 96 {}, 97 {}, 98 {} 99 }, 100 { 101 "Handlers", 102 {B_GET_PROPERTY}, 103 {B_DIRECT_SPECIFIER}, 104 NULL, BLOOPER_PROCESS_INTERNALLY, 105 {B_MESSENGER_TYPE}, 106 {}, 107 {} 108 }, 109 { 110 "Handler", 111 {B_COUNT_PROPERTIES}, 112 {B_DIRECT_SPECIFIER}, 113 NULL, BLOOPER_PROCESS_INTERNALLY, 114 {B_INT32_TYPE}, 115 {}, 116 {} 117 }, 118 {} 119 }; 120 121 struct _loop_data_ { 122 BLooper* looper; 123 thread_id thread; 124 }; 125 126 127 // #pragma mark - 128 129 130 BLooper::BLooper(const char* name, int32 priority, int32 port_capacity) 131 : BHandler(name) 132 { 133 InitData(name, priority, port_capacity); 134 } 135 136 137 BLooper::~BLooper() 138 { 139 if (fRunCalled && !fTerminating) { 140 debugger("You can't call delete on a BLooper object " 141 "once it is running."); 142 } 143 144 Lock(); 145 146 // In case the looper thread calls Quit() fLastMessage is not deleted. 147 if (fLastMessage) { 148 delete fLastMessage; 149 fLastMessage = NULL; 150 } 151 152 // Close the message port and read and reply to the remaining messages. 153 if (fMsgPort > 0) 154 close_port(fMsgPort); 155 156 BMessage *msg; 157 // Clear the queue so our call to IsMessageWaiting() below doesn't give 158 // us bogus info 159 while ((msg = fQueue->NextMessage()) != NULL) { 160 delete msg; // msg will automagically post generic reply 161 } 162 163 do { 164 delete ReadMessageFromPort(0); 165 // msg will automagically post generic reply 166 } while (IsMessageWaiting()); 167 168 delete fQueue; 169 delete_port(fMsgPort); 170 171 // Clean up our filters 172 SetCommonFilterList(NULL); 173 174 BObjectLocker<BLooperList> ListLock(gLooperList); 175 RemoveHandler(this); 176 177 // Remove all the "child" handlers 178 BHandler *child; 179 while (CountHandlers()) { 180 child = HandlerAt(0); 181 if (child) 182 RemoveHandler(child); 183 } 184 185 Unlock(); 186 RemoveLooper(this); 187 delete_sem(fLockSem); 188 } 189 190 191 BLooper::BLooper(BMessage *data) 192 : BHandler(data) 193 { 194 int32 portCapacity; 195 if (data->FindInt32("_port_cap", &portCapacity) != B_OK 196 || portCapacity < 0) 197 portCapacity = B_LOOPER_PORT_DEFAULT_CAPACITY; 198 199 InitData(Name(), B_NORMAL_PRIORITY, portCapacity); 200 } 201 202 203 BArchivable * 204 BLooper::Instantiate(BMessage *data) 205 { 206 if (validate_instantiation(data, "BLooper")) 207 return new BLooper(data); 208 209 return NULL; 210 } 211 212 213 status_t 214 BLooper::Archive(BMessage *data, bool deep) const 215 { 216 status_t status = BHandler::Archive(data, deep); 217 if (status < B_OK) 218 return status; 219 220 port_info info; 221 status = get_port_info(fMsgPort, &info); 222 if (status == B_OK) 223 status = data->AddInt32("_port_cap", info.capacity); 224 225 return status; 226 } 227 228 229 status_t 230 BLooper::PostMessage(uint32 command) 231 { 232 BMessage message(command); 233 return _PostMessage(&message, this, NULL); 234 } 235 236 237 status_t 238 BLooper::PostMessage(BMessage *message) 239 { 240 return _PostMessage(message, this, NULL); 241 } 242 243 244 status_t 245 BLooper::PostMessage(uint32 command, BHandler *handler, 246 BHandler *replyTo) 247 { 248 BMessage message(command); 249 return _PostMessage(&message, handler, replyTo); 250 } 251 252 253 status_t 254 BLooper::PostMessage(BMessage *message, BHandler *handler, 255 BHandler *replyTo) 256 { 257 return _PostMessage(message, handler, replyTo); 258 } 259 260 261 void 262 BLooper::DispatchMessage(BMessage *message, BHandler *handler) 263 { 264 PRINT(("BLooper::DispatchMessage(%.4s)\n", (char*)&message->what)); 265 /** @note 266 Initially, DispatchMessage() was locking the looper, calling the 267 filtering API, determining whether to use fPreferred or not, and 268 deleting the message. A look at the BeBook, however, reveals that 269 all this function does is handle its own B_QUIT_REQUESTED messages 270 and pass everything else to handler->MessageReceived(). Clearly the 271 rest must be happening in task_looper(). This makes a lot of sense 272 because otherwise every derived class would have to figure out when 273 to use fPreferred, handle the locking and filtering and delete the 274 message. Even if the BeBook didn't say as much, it would make total 275 sense to hoist that functionality out of here and into task_looper(). 276 */ 277 switch (message->what) { 278 case _QUIT_: 279 // Can't call Quit() to do this, because of the slight chance 280 // another thread with have us locked between now and then. 281 fTerminating = true; 282 283 // After returning from DispatchMessage(), the looper will be 284 // deleted in _task0_() 285 break; 286 287 case B_QUIT_REQUESTED: 288 if (handler == this) { 289 do_quit_requested(message); 290 break; 291 } 292 293 // fall through 294 295 default: 296 handler->MessageReceived(message); 297 break; 298 } 299 PRINT(("BLooper::DispatchMessage() done\n")); 300 } 301 302 303 void 304 BLooper::MessageReceived(BMessage *msg) 305 { 306 // TODO: verify 307 // The BeBook says this "simply calls the inherited function. ...the BLooper 308 // implementation does nothing of importance." Which is not the same as 309 // saying it does nothing. Investigate. 310 BHandler::MessageReceived(msg); 311 } 312 313 314 BMessage* 315 BLooper::CurrentMessage() const 316 { 317 return fLastMessage; 318 } 319 320 321 BMessage* 322 BLooper::DetachCurrentMessage() 323 { 324 Lock(); 325 BMessage* msg = fLastMessage; 326 fLastMessage = NULL; 327 fQueue->RemoveMessage(msg); 328 Unlock(); 329 return msg; 330 } 331 332 333 BMessageQueue* 334 BLooper::MessageQueue() const 335 { 336 return fQueue; 337 } 338 339 340 bool 341 BLooper::IsMessageWaiting() const 342 { 343 AssertLocked(); 344 345 if (!fQueue->IsEmpty()) 346 return true; 347 348 /** 349 @note: What we're doing here differs slightly from the R5 implementation. 350 It appears that they probably return count != 0, which gives an 351 incorrect true result when port_buffer_size_etc() would block -- 352 which indicates that the port's buffer is empty, so we should return 353 false. Since we don't actually care about what the error is, we 354 just return count > 0. This has some interesting consequences in 355 that we will correctly return 'false' if the port is empty 356 (B_WOULD_BLOCK), whereas R5 will return true. We call that a bug 357 where I come from. ;) 358 */ 359 int32 count; 360 do { 361 count = port_buffer_size_etc(fMsgPort, B_RELATIVE_TIMEOUT, 0); 362 } while (count == B_INTERRUPTED); 363 364 return count > 0; 365 } 366 367 368 void 369 BLooper::AddHandler(BHandler* handler) 370 { 371 if (handler == NULL) 372 return; 373 374 AssertLocked(); 375 376 if (handler->Looper() == NULL) { 377 fHandlers.AddItem(handler); 378 handler->SetLooper(this); 379 if (handler != this) // avoid a cycle 380 handler->SetNextHandler(this); 381 382 BList* filters = handler->FilterList(); 383 if (filters) { 384 for (int32 i = 0; i < filters->CountItems(); ++i) { 385 ((BMessageFilter*)filters->ItemAt(i))->SetLooper(this); 386 } 387 } 388 } 389 } 390 391 392 bool 393 BLooper::RemoveHandler(BHandler* handler) 394 { 395 if (handler == NULL) 396 return false; 397 398 AssertLocked(); 399 400 if (handler->Looper() == this && fHandlers.RemoveItem(handler)) { 401 if (handler == fPreferred) 402 fPreferred = NULL; 403 404 handler->SetNextHandler(NULL); 405 handler->SetLooper(NULL); 406 return true; 407 } 408 409 return false; 410 } 411 412 413 int32 414 BLooper::CountHandlers() const 415 { 416 AssertLocked(); 417 418 return fHandlers.CountItems(); 419 } 420 421 422 BHandler* 423 BLooper::HandlerAt(int32 index) const 424 { 425 AssertLocked(); 426 427 return (BHandler*)fHandlers.ItemAt(index); 428 } 429 430 431 int32 432 BLooper::IndexOf(BHandler* handler) const 433 { 434 AssertLocked(); 435 436 return fHandlers.IndexOf(handler); 437 } 438 439 440 BHandler* 441 BLooper::PreferredHandler() const 442 { 443 return fPreferred; 444 } 445 446 447 void 448 BLooper::SetPreferredHandler(BHandler* handler) 449 { 450 if (handler && handler->Looper() == this && IndexOf(handler) >= 0) { 451 fPreferred = handler; 452 } else { 453 fPreferred = NULL; 454 } 455 } 456 457 458 thread_id 459 BLooper::Run() 460 { 461 AssertLocked(); 462 463 if (fRunCalled) { 464 // Not allowed to call Run() more than once 465 debugger("can't call BLooper::Run twice!"); 466 } 467 468 fTaskID = spawn_thread(_task0_, Name(), fInitPriority, this); 469 if (fTaskID < B_OK) 470 return fTaskID; 471 472 if (fMsgPort < B_OK) 473 return fMsgPort; 474 475 fRunCalled = true; 476 Unlock(); 477 478 status_t err = resume_thread(fTaskID); 479 if (err < B_OK) 480 return err; 481 482 return fTaskID; 483 } 484 485 486 void 487 BLooper::Quit() 488 { 489 PRINT(("BLooper::Quit()\n")); 490 491 if (!IsLocked()) { 492 printf("ERROR - you must Lock a looper before calling Quit(), " 493 "team=%ld, looper=%s\n", Team(), Name() ? Name() : "unnamed"); 494 } 495 496 // Try to lock 497 if (!Lock()) { 498 // We're toast already 499 return; 500 } 501 502 PRINT((" is locked\n")); 503 504 if (!fRunCalled) { 505 PRINT((" Run() has not been called yet\n")); 506 fTerminating = true; 507 delete this; 508 } else if (find_thread(NULL) == fTaskID) { 509 PRINT((" We are the looper thread\n")); 510 fTerminating = true; 511 delete this; 512 exit_thread(0); 513 } else { 514 PRINT((" Run() has already been called and we are not the looper thread\n")); 515 516 // As with sem in _Lock(), we need to cache this here in case the looper 517 // disappears before we get to the wait_for_thread() below 518 thread_id tid = Thread(); 519 520 // We need to unlock here. Otherwise the looper thread can't 521 // dispatch the _QUIT_ message we're going to post. 522 UnlockFully(); 523 524 // As per the BeBook, if we've been called by a thread other than 525 // our own, the rest of the message queue has to get processed. So 526 // we put this in the queue, and when it shows up, we'll call Quit() 527 // from our own thread. 528 // A little testing with BMessageFilter shows _QUIT_ is being used here. 529 // I got suspicious when my test QuitRequested() wasn't getting called 530 // when Quit() was invoked from another thread. Makes a nice proof that 531 // this is how it's handled, too. 532 533 while (PostMessage(_QUIT_) == B_WOULD_BLOCK) { 534 // There's a slight chance that PostMessage() will return B_WOULD_BLOCK 535 // because the port is full, so we'll wait a bit and re-post until 536 // we won't block. 537 snooze(25000); 538 } 539 540 // We have to wait until the looper is done processing any remaining messages. 541 int32 temp; 542 while (wait_for_thread(tid, &temp) == B_INTERRUPTED) 543 ; 544 } 545 546 PRINT(("BLooper::Quit() done\n")); 547 } 548 549 550 bool 551 BLooper::QuitRequested() 552 { 553 return true; 554 } 555 556 557 bool 558 BLooper::Lock() 559 { 560 // Defer to global _Lock(); see notes there 561 return _Lock(this, -1, B_INFINITE_TIMEOUT) == B_OK; 562 } 563 564 565 void 566 BLooper::Unlock() 567 { 568 PRINT(("BLooper::Unlock()\n")); 569 // Make sure we're locked to begin with 570 AssertLocked(); 571 572 // Decrement fOwnerCount 573 --fOwnerCount; 574 PRINT((" fOwnerCount now: %ld\n", fOwnerCount)); 575 // Check to see if the owner still wants a lock 576 if (fOwnerCount == 0) { 577 // Set fOwner to invalid thread_id (< 0) 578 fOwner = -1; 579 580 // Decrement requested lock count (using fAtomicCount for this) 581 /* int32 atomicCount =*/ atomic_add(&fAtomicCount, -1); 582 PRINT((" fAtomicCount now: %ld\n", fAtomicCount)); 583 584 // Check if anyone is waiting for a lock 585 // bonefish: Currently _Lock() always acquires the semaphore. 586 // if (atomicCount > 0) 587 { 588 // release the lock 589 release_sem(fLockSem); 590 } 591 } 592 PRINT(("BLooper::Unlock() done\n")); 593 } 594 595 596 bool 597 BLooper::IsLocked() const 598 { 599 // We have to lock the list for the call to IsLooperValid(). Has the side 600 // effect of not letting the looper get deleted while we're here. 601 BObjectLocker<BLooperList> ListLock(gLooperList); 602 603 if (!ListLock.IsLocked()) { 604 // If we can't lock the list, our semaphore is probably toast 605 return false; 606 } 607 608 if (!IsLooperValid(this)) { 609 // The looper is gone, so of course it's not locked 610 return false; 611 } 612 613 // Got this from Jeremy's BLocker implementation 614 return find_thread(NULL) == fOwner; 615 } 616 617 618 status_t 619 BLooper::LockWithTimeout(bigtime_t timeout) 620 { 621 return _Lock(this, -1, timeout); 622 } 623 624 625 thread_id 626 BLooper::Thread() const 627 { 628 return fTaskID; 629 } 630 631 632 team_id 633 BLooper::Team() const 634 { 635 return sTeamID; 636 } 637 638 639 BLooper* 640 BLooper::LooperForThread(thread_id tid) 641 { 642 BObjectLocker<BLooperList> ListLock(gLooperList); 643 if (ListLock.IsLocked()) 644 return gLooperList.LooperForThread(tid); 645 646 return NULL; 647 } 648 649 650 thread_id 651 BLooper::LockingThread() const 652 { 653 return fOwner; 654 } 655 656 657 int32 658 BLooper::CountLocks() const 659 { 660 return fOwnerCount; 661 } 662 663 664 int32 665 BLooper::CountLockRequests() const 666 { 667 return fAtomicCount; 668 } 669 670 671 sem_id 672 BLooper::Sem() const 673 { 674 return fLockSem; 675 } 676 677 678 BHandler* 679 BLooper::ResolveSpecifier(BMessage* msg, int32 index, 680 BMessage* specifier, int32 form, const char* property) 681 { 682 /** 683 @note When I was first dumping the results of GetSupportedSuites() from 684 various classes, the use of the extra_data field was quite 685 mysterious to me. Then I dumped BApplication and compared the 686 result against the BeBook's docs for scripting BApplication. A 687 bunch of it isn't documented, but what is tipped me to the idea 688 that the extra_data is being used as a quick and dirty way to tell 689 what scripting "command" has been sent, e.g., for easy use in a 690 switch statement. Would certainly be a lot faster than a bunch of 691 string comparisons -- which wouldn't tell the whole story anyway, 692 because of the same name being used for multiple properties. 693 */ 694 BPropertyInfo PropertyInfo(gLooperPropInfo); 695 uint32 data; 696 status_t err = B_OK; 697 const char* errMsg = ""; 698 if (PropertyInfo.FindMatch(msg, index, specifier, form, property, &data) >= 0) { 699 switch (data) { 700 case BLOOPER_PROCESS_INTERNALLY: 701 return this; 702 703 case BLOOPER_HANDLER_BY_INDEX: 704 { 705 int32 index = specifier->FindInt32("index"); 706 if (form == B_REVERSE_INDEX_SPECIFIER) { 707 index = CountHandlers() - index; 708 } 709 BHandler* target = HandlerAt(index); 710 if (target) { 711 // Specifier has been fully handled 712 msg->PopSpecifier(); 713 return target; 714 } else { 715 err = B_BAD_INDEX; 716 errMsg = "handler index out of range"; 717 } 718 break; 719 } 720 721 default: 722 err = B_BAD_SCRIPT_SYNTAX; 723 errMsg = "Didn't understand the specifier(s)"; 724 break; 725 } 726 } else { 727 return BHandler::ResolveSpecifier(msg, index, specifier, form, 728 property); 729 } 730 731 BMessage Reply(B_MESSAGE_NOT_UNDERSTOOD); 732 Reply.AddInt32("error", err); 733 Reply.AddString("message", errMsg); 734 msg->SendReply(&Reply); 735 736 return NULL; 737 } 738 739 740 status_t 741 BLooper::GetSupportedSuites(BMessage* data) 742 { 743 if (data == NULL) 744 return B_BAD_VALUE; 745 746 status_t status = data->AddString("Suites", "suite/vnd.Be-handler"); 747 if (status == B_OK) { 748 BPropertyInfo PropertyInfo(gLooperPropInfo); 749 status = data->AddFlat("message", &PropertyInfo); 750 if (status == B_OK) 751 status = BHandler::GetSupportedSuites(data); 752 } 753 754 return status; 755 } 756 757 758 void 759 BLooper::AddCommonFilter(BMessageFilter* filter) 760 { 761 if (!filter) 762 return; 763 764 AssertLocked(); 765 766 if (filter->Looper()) { 767 debugger("A MessageFilter can only be used once."); 768 return; 769 } 770 771 if (!fCommonFilters) 772 fCommonFilters = new BList(FILTER_LIST_BLOCK_SIZE); 773 774 filter->SetLooper(this); 775 fCommonFilters->AddItem(filter); 776 } 777 778 779 bool 780 BLooper::RemoveCommonFilter(BMessageFilter* filter) 781 { 782 AssertLocked(); 783 784 if (!fCommonFilters) 785 return false; 786 787 bool result = fCommonFilters->RemoveItem(filter); 788 if (result) 789 filter->SetLooper(NULL); 790 791 return result; 792 } 793 794 795 void 796 BLooper::SetCommonFilterList(BList* filters) 797 { 798 // We have a somewhat serious problem here. It is entirely possible in R5 799 // to assign a given list of filters to *two* BLoopers simultaneously. This 800 // becomes problematic when the loopers are destroyed: the last looper 801 // destroyed will have a problem when it tries to delete a filter list that 802 // has already been deleted. In R5, this results in a general protection 803 // fault. We fix this by checking the filter list for ownership issues. 804 805 AssertLocked(); 806 807 BMessageFilter* filter; 808 if (filters) { 809 // Check for ownership issues 810 for (int32 i = 0; i < filters->CountItems(); ++i) { 811 filter = (BMessageFilter*)filters->ItemAt(i); 812 if (filter->Looper()) { 813 debugger("A MessageFilter can only be used once."); 814 return; 815 } 816 } 817 } 818 819 if (fCommonFilters) { 820 for (int32 i = 0; i < fCommonFilters->CountItems(); ++i) { 821 delete (BMessageFilter*)fCommonFilters->ItemAt(i); 822 } 823 824 delete fCommonFilters; 825 fCommonFilters = NULL; 826 } 827 828 // Per the BeBook, we take ownership of the list 829 fCommonFilters = filters; 830 if (fCommonFilters) { 831 for (int32 i = 0; i < fCommonFilters->CountItems(); ++i) { 832 filter = (BMessageFilter*)fCommonFilters->ItemAt(i); 833 filter->SetLooper(this); 834 } 835 } 836 } 837 838 839 BList* 840 BLooper::CommonFilterList() const 841 { 842 return fCommonFilters; 843 } 844 845 846 status_t 847 BLooper::Perform(perform_code d, void* arg) 848 { 849 // This is sort of what we're doing for this function everywhere 850 return BHandler::Perform(d, arg); 851 } 852 853 854 BMessage* 855 BLooper::MessageFromPort(bigtime_t timeout) 856 { 857 return ReadMessageFromPort(timeout); 858 } 859 860 861 void BLooper::_ReservedLooper1() {} 862 void BLooper::_ReservedLooper2() {} 863 void BLooper::_ReservedLooper3() {} 864 void BLooper::_ReservedLooper4() {} 865 void BLooper::_ReservedLooper5() {} 866 void BLooper::_ReservedLooper6() {} 867 868 869 BLooper::BLooper(const BLooper&) 870 { 871 // Copy construction not allowed 872 } 873 874 875 BLooper& BLooper::operator=(const BLooper& ) 876 { 877 // Looper copying not allowed 878 return *this; 879 } 880 881 882 BLooper::BLooper(int32 priority, port_id port, const char* name) 883 { 884 // This must be a legacy constructor 885 fMsgPort = port; 886 InitData(name, priority, B_LOOPER_PORT_DEFAULT_CAPACITY); 887 } 888 889 890 status_t 891 BLooper::_PostMessage(BMessage *msg, BHandler *handler, 892 BHandler *replyTo) 893 { 894 BObjectLocker<BLooperList> listLocker(gLooperList); 895 if (!listLocker.IsLocked()) 896 return B_ERROR; 897 898 if (!IsLooperValid(this)) 899 return B_BAD_VALUE; 900 901 // Does handler belong to this looper? 902 if (handler && handler->Looper() != this) 903 return B_MISMATCHED_VALUES; 904 905 status_t status; 906 BMessenger messenger(handler, this, &status); 907 if (status == B_OK) 908 status = messenger.SendMessage(msg, replyTo, 0); 909 910 return status; 911 } 912 913 914 status_t 915 BLooper::_Lock(BLooper* loop, port_id port, bigtime_t timeout) 916 { 917 PRINT(("BLooper::_Lock(%p, %lx)\n", loop, port)); 918 /** 919 @note The assumption I'm under here is that since we can get the port of 920 the BLooper directly from the BLooper itself, the port parameter is 921 for identifying BLoopers by port_id when a pointer to the BLooper in 922 question is not available. So this function has two modes: 923 o When loop != NULL, use it directly 924 o When loop == NULL and port is valid, use the port_id to get 925 the looper 926 I scoured the docs to find out what constitutes a valid port_id to 927 no avail. Since create_port uses the standard error values in its 928 returned port_id, I'll assume that anything less than zero is a safe 929 bet as an *invalid* port_id. I'm guessing that, like thread and 930 semaphore ids, anything >= zero is valid. So, the short version of 931 this reads: if you don't want to find by port_id, make port = -1. 932 933 Another assumption I'm making is that Lock() and LockWithTimeout() 934 are covers for this function. If it turns out that we don't really 935 need this function, I may refactor this code into LockWithTimeout() 936 and have Lock() call it instead. This function could then be 937 removed. 938 */ 939 940 // Check params (loop, port) 941 if (!loop && port < 0) 942 { 943 PRINT(("BLooper::_Lock() done 1\n")); 944 return B_BAD_VALUE; 945 } 946 947 // forward declared so I can use BAutolock on sLooperListLock 948 thread_id curThread; 949 sem_id sem; 950 951 /** 952 @note We lock the looper list at the start of the lock operation to 953 prevent the looper getting removed from the list while we're 954 doing list operations. Also ensures that the looper doesn't 955 get deleted here (since ~BLooper() has to lock the list as 956 well to remove itself). 957 */ 958 { 959 BObjectLocker<BLooperList> ListLock(gLooperList); 960 if (!ListLock.IsLocked()) 961 { 962 // If we can't lock, the semaphore is probably 963 // gone, which leaves us in no-man's land 964 PRINT(("BLooper::_Lock() done 2\n")); 965 return B_BAD_VALUE; 966 } 967 968 // Look up looper by port_id, if necessary 969 if (!loop) 970 { 971 loop = LooperForPort(port); 972 if (!loop) 973 { 974 PRINT(("BLooper::_Lock() done 3\n")); 975 return B_BAD_VALUE; 976 } 977 } 978 else 979 { 980 // Check looper validity 981 if (!IsLooperValid(loop)) 982 { 983 PRINT(("BLooper::_Lock() done 4\n")); 984 return B_BAD_VALUE; 985 } 986 } 987 988 // Is the looper trying to lock itself? 989 // Check for nested lock attempt 990 curThread = find_thread(NULL); 991 if (curThread == loop->fOwner) 992 { 993 // Bump fOwnerCount 994 ++loop->fOwnerCount; 995 PRINT(("BLooper::_Lock() done 5: fOwnerCount: %ld\n", loop->fOwnerCount)); 996 return B_OK; 997 } 998 999 // Something external to the looper is attempting to lock 1000 // Cache the semaphore 1001 sem = loop->fLockSem; 1002 1003 // Validate the semaphore 1004 if (sem < 0) 1005 { 1006 PRINT(("BLooper::_Lock() done 6\n")); 1007 return B_BAD_VALUE; 1008 } 1009 1010 // Bump the requested lock count (using fAtomicCount for this) 1011 atomic_add(&loop->fAtomicCount, 1); 1012 1013 // sLooperListLock automatically released here 1014 } 1015 1016 /** 1017 @note We have to operate with the looper list unlocked during semaphore 1018 acquisition so that the rest of the application doesn't have to 1019 wait for this lock to happen. This is why we cached fLockSem 1020 earlier -- with the list unlocked, the looper might get deleted 1021 right out from under us. This is also why we use a raw semaphore 1022 instead of the easier-to-deal-with BLocker; you can't cache a 1023 BLocker. 1024 */ 1025 // acquire the lock 1026 status_t err; 1027 do 1028 { 1029 err = acquire_sem_etc(sem, 1, B_RELATIVE_TIMEOUT, timeout); 1030 } while (err == B_INTERRUPTED); 1031 1032 if (!err) 1033 { 1034 // Assign current thread to fOwner 1035 loop->fOwner = curThread; 1036 // Reset fOwnerCount to 1 1037 loop->fOwnerCount = 1; 1038 } 1039 1040 PRINT(("BLooper::_Lock() done: %lx\n", err)); 1041 return err; 1042 } 1043 1044 1045 status_t 1046 BLooper::_LockComplete(BLooper *looper, int32 old, thread_id this_tid, 1047 sem_id sem, bigtime_t timeout) 1048 { 1049 // What is this for? Hope I'm not missing something conceptually here ... 1050 return B_ERROR; 1051 } 1052 1053 1054 void 1055 BLooper::InitData() 1056 { 1057 fOwner = B_ERROR; 1058 fRunCalled = false; 1059 fQueue = new BMessageQueue(); 1060 fCommonFilters = NULL; 1061 fLastMessage = NULL; 1062 fPreferred = NULL; 1063 fTaskID = B_ERROR; 1064 fTerminating = false; 1065 fMsgPort = -1; 1066 1067 if (sTeamID == -1) { 1068 thread_info info; 1069 get_thread_info(find_thread(NULL), &info); 1070 sTeamID = info.team; 1071 } 1072 } 1073 1074 1075 void 1076 BLooper::InitData(const char *name, int32 priority, int32 portCapacity) 1077 { 1078 InitData(); 1079 1080 if (name == NULL) 1081 name = "anonymous looper"; 1082 1083 fLockSem = create_sem(1, name); 1084 1085 if (portCapacity <= 0) 1086 portCapacity = B_LOOPER_PORT_DEFAULT_CAPACITY; 1087 1088 fMsgPort = create_port(portCapacity, name); 1089 1090 fInitPriority = priority; 1091 1092 BObjectLocker<BLooperList> ListLock(gLooperList); 1093 AddLooper(this); 1094 AddHandler(this); 1095 } 1096 1097 1098 void 1099 BLooper::AddMessage(BMessage* msg) 1100 { 1101 _AddMessagePriv(msg); 1102 1103 // ToDo: if called from a different thread, we need to wake up the looper 1104 } 1105 1106 1107 void 1108 BLooper::_AddMessagePriv(BMessage* msg) 1109 { 1110 // ToDo: if no target token is specified, set to preferred handler 1111 // Others may want to peek into our message queue, so the preferred 1112 // handler must be set correctly already if no token was given 1113 1114 fQueue->AddMessage(msg); 1115 } 1116 1117 1118 status_t 1119 BLooper::_task0_(void *arg) 1120 { 1121 BLooper *looper = (BLooper *)arg; 1122 1123 PRINT(("LOOPER: _task0_()\n")); 1124 1125 if (looper->Lock()) { 1126 PRINT(("LOOPER: looper locked\n")); 1127 looper->task_looper(); 1128 1129 delete looper; 1130 } 1131 1132 PRINT(("LOOPER: _task0_() done: thread %ld\n", find_thread(NULL))); 1133 return B_OK; 1134 } 1135 1136 1137 void * 1138 BLooper::ReadRawFromPort(int32 *msgCode, bigtime_t timeout) 1139 { 1140 PRINT(("BLooper::ReadRawFromPort()\n")); 1141 int8 *msgBuffer = NULL; 1142 ssize_t bufferSize; 1143 1144 do { 1145 bufferSize = port_buffer_size_etc(fMsgPort, B_RELATIVE_TIMEOUT, timeout); 1146 } while (bufferSize == B_INTERRUPTED); 1147 1148 if (bufferSize < B_OK) { 1149 PRINT(("BLooper::ReadRawFromPort(): failed: %ld\n", bufferSize)); 1150 return NULL; 1151 } 1152 1153 if (bufferSize > 0) 1154 msgBuffer = new int8[bufferSize]; 1155 1156 // we don't want to wait again here, since that can only mean 1157 // that someone else has read our message and our bufferSize 1158 // is now probably wrong 1159 PRINT(("read_port()...\n")); 1160 bufferSize = read_port_etc(fMsgPort, msgCode, msgBuffer, bufferSize, 1161 B_RELATIVE_TIMEOUT, 0); 1162 if (bufferSize < B_OK) { 1163 delete[] msgBuffer; 1164 return NULL; 1165 } 1166 1167 PRINT(("BLooper::ReadRawFromPort() read: %.4s, %p\n", (char *)msgCode, msgBuffer)); 1168 return msgBuffer; 1169 } 1170 1171 1172 BMessage * 1173 BLooper::ReadMessageFromPort(bigtime_t tout) 1174 { 1175 PRINT(("BLooper::ReadMessageFromPort()\n")); 1176 int32 msgcode; 1177 BMessage* bmsg; 1178 1179 void* msgbuffer = ReadRawFromPort(&msgcode, tout); 1180 if (!msgbuffer) 1181 return NULL; 1182 1183 bmsg = ConvertToMessage(msgbuffer, msgcode); 1184 1185 delete[] (int8*)msgbuffer; 1186 1187 PRINT(("BLooper::ReadMessageFromPort() done: %p\n", bmsg)); 1188 return bmsg; 1189 } 1190 //------------------------------------------------------------------------------ 1191 BMessage* BLooper::ConvertToMessage(void* raw, int32 code) 1192 { 1193 PRINT(("BLooper::ConvertToMessage()\n")); 1194 BMessage* bmsg = new BMessage(code); 1195 1196 if (raw != NULL) 1197 { 1198 if (bmsg->Unflatten((const char*)raw) != B_OK) 1199 { 1200 PRINT(("BLooper::ConvertToMessage(): unflattening message failed\n")); 1201 delete bmsg; 1202 bmsg = NULL; 1203 } 1204 } 1205 1206 PRINT(("BLooper::ConvertToMessage(): %p\n", bmsg)); 1207 return bmsg; 1208 } 1209 1210 1211 void 1212 BLooper::task_looper() 1213 { 1214 PRINT(("BLooper::task_looper()\n")); 1215 // Check that looper is locked (should be) 1216 AssertLocked(); 1217 // Unlock the looper 1218 Unlock(); 1219 1220 // loop: As long as we are not terminating. 1221 while (!fTerminating) 1222 { 1223 PRINT(("LOOPER: outer loop\n")); 1224 // TODO: timeout determination algo 1225 // Read from message port (how do we determine what the timeout is?) 1226 PRINT(("LOOPER: MessageFromPort()...\n")); 1227 BMessage *msg = MessageFromPort(); 1228 PRINT(("LOOPER: ...done\n")); 1229 1230 // Did we get a message? 1231 if (msg) 1232 _AddMessagePriv(msg); 1233 1234 // Get message count from port 1235 int32 msgCount = port_count(fMsgPort); 1236 for (int32 i = 0; i < msgCount; ++i) { 1237 // Read 'count' messages from port (so we will not block) 1238 // We use zero as our timeout since we know there is stuff there 1239 msg = MessageFromPort(0); 1240 // Add messages to queue 1241 if (msg) 1242 _AddMessagePriv(msg); 1243 } 1244 1245 // loop: As long as there are messages in the queue and the port is 1246 // empty... and we are not terminating, of course. 1247 bool dispatchNextMessage = true; 1248 while (!fTerminating && dispatchNextMessage) { 1249 PRINT(("LOOPER: inner loop\n")); 1250 // Get next message from queue (assign to fLastMessage) 1251 fLastMessage = fQueue->NextMessage(); 1252 1253 // Lock the looper 1254 Lock(); 1255 if (!fLastMessage) { 1256 // No more messages: Unlock the looper and terminate the 1257 // dispatch loop. 1258 dispatchNextMessage = false; 1259 } else { 1260 PRINT(("LOOPER: fLastMessage: 0x%lx: %.4s\n", fLastMessage->what, 1261 (char*)&fLastMessage->what)); 1262 DBG(fLastMessage->PrintToStream()); 1263 1264 // Get the target handler 1265 // Use BMessage friend functions to determine if we are using the 1266 // preferred handler, or if a target has been specified 1267 BHandler* handler; 1268 if (_use_preferred_target_(fLastMessage)) { 1269 PRINT(("LOOPER: use preferred target\n")); 1270 handler = fPreferred; 1271 } else { 1272 PRINT(("LOOPER: don't use preferred target\n")); 1273 /** 1274 @note Here is where all the token stuff starts to 1275 make sense. How, exactly, do we determine 1276 what the target BHandler is? If we look at 1277 BMessage, we see an int32 field, fTarget. 1278 Amazingly, we happen to have a global mapping 1279 of BHandler pointers to int32s! 1280 */ 1281 PRINT(("LOOPER: use: %ld\n", _get_message_target_(fLastMessage))); 1282 gDefaultTokens.GetToken(_get_message_target_(fLastMessage), 1283 B_HANDLER_TOKEN, (void **)&handler); 1284 PRINT(("LOOPER: handler: %p, this: %p\n", handler, this)); 1285 } 1286 1287 if (!handler) { 1288 PRINT(("LOOPER: no target handler, use this\n")); 1289 handler = this; 1290 } 1291 1292 // Is this a scripting message? (BMessage::HasSpecifiers()) 1293 if (fLastMessage->HasSpecifiers()) { 1294 int32 index = 0; 1295 // Make sure the current specifier is kosher 1296 if (fLastMessage->GetCurrentSpecifier(&index) == B_OK) 1297 handler = resolve_specifier(handler, fLastMessage); 1298 } else { 1299 PRINT(("LOOPER: no scripting message\n")); 1300 } 1301 1302 if (handler) { 1303 // Do filtering 1304 handler = top_level_filter(fLastMessage, handler); 1305 PRINT(("LOOPER: top_level_filter(): %p\n", handler)); 1306 if (handler && handler->Looper() == this) 1307 DispatchMessage(fLastMessage, handler); 1308 } 1309 } 1310 1311 // Unlock the looper 1312 Unlock(); 1313 1314 // Delete the current message (fLastMessage) 1315 if (fLastMessage) { 1316 delete fLastMessage; 1317 fLastMessage = NULL; 1318 } 1319 1320 // Are any messages on the port? 1321 if (port_count(fMsgPort) > 0) { 1322 // Do outer loop 1323 dispatchNextMessage = false; 1324 } 1325 } 1326 } 1327 PRINT(("BLooper::task_looper() done\n")); 1328 } 1329 1330 1331 void 1332 BLooper::do_quit_requested(BMessage *msg) 1333 { 1334 bool isQuitting = QuitRequested(); 1335 1336 // We send a reply to the sender, when they're waiting for a reply or 1337 // if the request message contains a boolean "_shutdown_" field with value 1338 // true. In the latter case the message came from the registrar, asking 1339 // the application to shut down. 1340 bool shutdown; 1341 if (msg->IsSourceWaiting() 1342 || (msg->FindBool("_shutdown_", &shutdown) == B_OK && shutdown)) { 1343 BMessage ReplyMsg(B_REPLY); 1344 ReplyMsg.AddBool("result", isQuitting); 1345 ReplyMsg.AddInt32("thread", fTaskID); 1346 msg->SendReply(&ReplyMsg); 1347 } 1348 1349 if (isQuitting) 1350 Quit(); 1351 } 1352 1353 1354 bool 1355 BLooper::AssertLocked() const 1356 { 1357 if (!IsLocked()) { 1358 debugger("looper must be locked before proceeding\n"); 1359 return false; 1360 } 1361 1362 return true; 1363 } 1364 1365 1366 BHandler * 1367 BLooper::top_level_filter(BMessage* msg, BHandler* target) 1368 { 1369 if (msg) { 1370 // Apply the common filters first 1371 target = apply_filters(CommonFilterList(), msg, target); 1372 if (target) { 1373 if (target->Looper() != this) { 1374 debugger("Targeted handler does not belong to the looper."); 1375 target = NULL; 1376 } else { 1377 // Now apply handler-specific filters 1378 target = handler_only_filter(msg, target); 1379 } 1380 } 1381 } 1382 1383 return target; 1384 } 1385 1386 1387 BHandler * 1388 BLooper::handler_only_filter(BMessage* msg, BHandler* target) 1389 { 1390 // Keep running filters until our handler is NULL, or until the filtering 1391 // handler returns itself as the designated handler 1392 BHandler* oldTarget = NULL; 1393 while (target != NULL && target != oldTarget) { 1394 oldTarget = target; 1395 target = apply_filters(oldTarget->FilterList(), msg, oldTarget); 1396 if (target && (target->Looper() != this)) { 1397 debugger("Targeted handler does not belong to the looper."); 1398 target = NULL; 1399 } 1400 } 1401 1402 return target; 1403 } 1404 1405 1406 BHandler * 1407 BLooper::apply_filters(BList* list, BMessage* msg, BHandler* target) 1408 { 1409 // This is where the action is! 1410 // Check the parameters 1411 if (!list || !msg) 1412 return target; 1413 1414 // For each filter in the provided list 1415 BMessageFilter* filter = NULL; 1416 for (int32 i = 0; i < list->CountItems(); ++i) { 1417 filter = (BMessageFilter*)list->ItemAt(i); 1418 1419 // Check command conditions 1420 if (filter->FiltersAnyCommand() || (filter->Command() == msg->what)) { 1421 // Check delivery conditions 1422 message_delivery delivery = filter->MessageDelivery(); 1423 bool dropped = msg->WasDropped(); 1424 if (delivery == B_ANY_DELIVERY || 1425 ((delivery == B_DROPPED_DELIVERY) && dropped) || 1426 ((delivery == B_PROGRAMMED_DELIVERY) && !dropped)) { 1427 // Check source conditions 1428 message_source source = filter->MessageSource(); 1429 bool remote = msg->IsSourceRemote(); 1430 if (source == B_ANY_SOURCE 1431 || (source == B_REMOTE_SOURCE && remote) 1432 || (source == B_LOCAL_SOURCE && !remote)) { 1433 // Are we using an "external" function? 1434 filter_result result; 1435 filter_hook func = filter->FilterFunction(); 1436 if (func) 1437 result = func(msg, &target, filter); 1438 else 1439 result = filter->Filter(msg, &target); 1440 1441 // Is further processing allowed? 1442 if (result == B_SKIP_MESSAGE) { 1443 // No; time to bail out 1444 return NULL; 1445 } 1446 } 1447 } 1448 } 1449 } 1450 1451 return target; 1452 } 1453 1454 1455 void 1456 BLooper::check_lock() 1457 { 1458 // This is a cheap variant of AssertLocked() 1459 // It is used in situations where it's clear that the looper is valid, 1460 // ie. from handlers 1461 if (fOwner == -1 || fOwner != find_thread(NULL)) 1462 debugger("Looper must be locked."); 1463 } 1464 1465 1466 BHandler * 1467 BLooper::resolve_specifier(BHandler* target, BMessage* msg) 1468 { 1469 // Check params 1470 if (!target || !msg) 1471 return NULL; 1472 1473 int32 index; 1474 BMessage specifier; 1475 int32 form; 1476 const char* property; 1477 status_t err = B_OK; 1478 BHandler* newTarget = target; 1479 // Loop to deal with nested specifiers 1480 // (e.g., the 3rd button on the 4th view) 1481 do { 1482 err = msg->GetCurrentSpecifier(&index, &specifier, &form, &property); 1483 if (err) { 1484 BMessage reply(B_REPLY); 1485 reply.AddInt32("error", err); 1486 msg->SendReply(&reply); 1487 return NULL; 1488 } 1489 // Current target gets what was the new target 1490 target = newTarget; 1491 newTarget = target->ResolveSpecifier(msg, index, &specifier, form, 1492 property); 1493 // Check that new target is owned by looper; 1494 // use IndexOf() to avoid dereferencing newTarget 1495 // (possible race condition with object destruction 1496 // by another looper) 1497 if (!newTarget || IndexOf(newTarget) < 0) 1498 return NULL; 1499 1500 // Get current specifier index (may change in ResolveSpecifier()) 1501 msg->GetCurrentSpecifier(&index); 1502 } while (newTarget && newTarget != target && index >= 0); 1503 1504 return newTarget; 1505 } 1506 1507 1508 void 1509 BLooper::UnlockFully() 1510 { 1511 AssertLocked(); 1512 1513 /** 1514 @note What we're doing here is completely undoing the current owner's lock 1515 on the looper. This is actually pretty easy, since the owner only 1516 has a single aquisition on the semaphore; every subsequent "lock" 1517 is just an increment to the owner count. The whole thing is quite 1518 similar to Unlock(), except that we clear the ownership variables, 1519 rather than merely decrementing them. 1520 */ 1521 // Clear the owner count 1522 fOwnerCount = 0; 1523 // Nobody owns the lock now 1524 fOwner = -1; 1525 // There is now one less thread holding a lock on this looper 1526 // bonefish: Currently _Lock() always acquires the semaphore. 1527 /* long atomicCount = */atomic_add(&fAtomicCount, -1); 1528 // if (atomicCount > 0) 1529 { 1530 release_sem(fLockSem); 1531 } 1532 } 1533 1534 1535 void 1536 BLooper::AddLooper(BLooper *looper) 1537 { 1538 if (gLooperList.IsLocked()) 1539 gLooperList.AddLooper(looper); 1540 } 1541 1542 1543 bool 1544 BLooper::IsLooperValid(const BLooper *looper) 1545 { 1546 if (gLooperList.IsLocked()) 1547 return gLooperList.IsLooperValid(looper); 1548 1549 return false; 1550 } 1551 1552 1553 void 1554 BLooper::RemoveLooper(BLooper *looper) 1555 { 1556 if (gLooperList.IsLocked()) 1557 gLooperList.RemoveLooper(looper); 1558 } 1559 1560 1561 void 1562 BLooper::GetLooperList(BList* list) 1563 { 1564 BObjectLocker<BLooperList> ListLock(gLooperList); 1565 if (ListLock.IsLocked()) 1566 gLooperList.GetLooperList(list); 1567 } 1568 1569 1570 BLooper * 1571 BLooper::LooperForName(const char* name) 1572 { 1573 if (gLooperList.IsLocked()) 1574 return gLooperList.LooperForName(name); 1575 1576 return NULL; 1577 } 1578 1579 1580 BLooper * 1581 BLooper::LooperForPort(port_id port) 1582 { 1583 if (gLooperList.IsLocked()) 1584 return gLooperList.LooperForPort(port); 1585 1586 return NULL; 1587 } 1588 1589 1590 // #pragma mark - 1591 1592 1593 port_id 1594 _get_looper_port_(const BLooper *looper) 1595 { 1596 return looper->fMsgPort; 1597 } 1598 1599