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