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