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