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 #if DEBUG < 1 555 // Decrement requested lock count (using fAtomicCount for this) 556 int32 atomicCount = atomic_add(&fAtomicCount, -1); 557 PRINT((" fAtomicCount now: %ld\n", fAtomicCount)); 558 559 // Check if anyone is waiting for a lock 560 // and release if it's the case 561 if (atomicCount > 1) 562 #endif 563 release_sem(fLockSem); 564 } 565 PRINT(("BLooper::Unlock() done\n")); 566 } 567 568 569 bool 570 BLooper::IsLocked() const 571 { 572 // We have to lock the list for the call to IsLooperValid(). Has the side 573 // effect of not letting the looper get deleted while we're here. 574 BObjectLocker<BLooperList> ListLock(gLooperList); 575 576 if (!ListLock.IsLocked()) { 577 // If we can't lock the list, our semaphore is probably toast 578 return false; 579 } 580 581 if (!IsLooperValid(this)) { 582 // The looper is gone, so of course it's not locked 583 return false; 584 } 585 586 // Got this from Jeremy's BLocker implementation 587 return find_thread(NULL) == fOwner; 588 } 589 590 591 status_t 592 BLooper::LockWithTimeout(bigtime_t timeout) 593 { 594 return _Lock(this, -1, timeout); 595 } 596 597 598 thread_id 599 BLooper::Thread() const 600 { 601 return fTaskID; 602 } 603 604 605 team_id 606 BLooper::Team() const 607 { 608 return sTeamID; 609 } 610 611 612 BLooper* 613 BLooper::LooperForThread(thread_id tid) 614 { 615 BObjectLocker<BLooperList> ListLock(gLooperList); 616 if (ListLock.IsLocked()) 617 return gLooperList.LooperForThread(tid); 618 619 return NULL; 620 } 621 622 623 thread_id 624 BLooper::LockingThread() const 625 { 626 return fOwner; 627 } 628 629 630 int32 631 BLooper::CountLocks() const 632 { 633 return fOwnerCount; 634 } 635 636 637 int32 638 BLooper::CountLockRequests() const 639 { 640 return fAtomicCount; 641 } 642 643 644 sem_id 645 BLooper::Sem() const 646 { 647 return fLockSem; 648 } 649 650 651 BHandler* 652 BLooper::ResolveSpecifier(BMessage* msg, int32 index, 653 BMessage* specifier, int32 form, const char* property) 654 { 655 /** 656 @note When I was first dumping the results of GetSupportedSuites() from 657 various classes, the use of the extra_data field was quite 658 mysterious to me. Then I dumped BApplication and compared the 659 result against the BeBook's docs for scripting BApplication. A 660 bunch of it isn't documented, but what is tipped me to the idea 661 that the extra_data is being used as a quick and dirty way to tell 662 what scripting "command" has been sent, e.g., for easy use in a 663 switch statement. Would certainly be a lot faster than a bunch of 664 string comparisons -- which wouldn't tell the whole story anyway, 665 because of the same name being used for multiple properties. 666 */ 667 BPropertyInfo PropertyInfo(gLooperPropInfo); 668 uint32 data; 669 status_t err = B_OK; 670 const char* errMsg = ""; 671 if (PropertyInfo.FindMatch(msg, index, specifier, form, property, &data) >= 0) { 672 switch (data) { 673 case BLOOPER_PROCESS_INTERNALLY: 674 return this; 675 676 case BLOOPER_HANDLER_BY_INDEX: 677 { 678 int32 index = specifier->FindInt32("index"); 679 if (form == B_REVERSE_INDEX_SPECIFIER) { 680 index = CountHandlers() - index; 681 } 682 BHandler* target = HandlerAt(index); 683 if (target) { 684 // Specifier has been fully handled 685 msg->PopSpecifier(); 686 return target; 687 } else { 688 err = B_BAD_INDEX; 689 errMsg = "handler index out of range"; 690 } 691 break; 692 } 693 694 default: 695 err = B_BAD_SCRIPT_SYNTAX; 696 errMsg = "Didn't understand the specifier(s)"; 697 break; 698 } 699 } else { 700 return BHandler::ResolveSpecifier(msg, index, specifier, form, 701 property); 702 } 703 704 BMessage Reply(B_MESSAGE_NOT_UNDERSTOOD); 705 Reply.AddInt32("error", err); 706 Reply.AddString("message", errMsg); 707 msg->SendReply(&Reply); 708 709 return NULL; 710 } 711 712 713 status_t 714 BLooper::GetSupportedSuites(BMessage* data) 715 { 716 if (data == NULL) 717 return B_BAD_VALUE; 718 719 status_t status = data->AddString("Suites", "suite/vnd.Be-handler"); 720 if (status == B_OK) { 721 BPropertyInfo PropertyInfo(gLooperPropInfo); 722 status = data->AddFlat("message", &PropertyInfo); 723 if (status == B_OK) 724 status = BHandler::GetSupportedSuites(data); 725 } 726 727 return status; 728 } 729 730 731 void 732 BLooper::AddCommonFilter(BMessageFilter* filter) 733 { 734 if (!filter) 735 return; 736 737 AssertLocked(); 738 739 if (filter->Looper()) { 740 debugger("A MessageFilter can only be used once."); 741 return; 742 } 743 744 if (!fCommonFilters) 745 fCommonFilters = new BList(FILTER_LIST_BLOCK_SIZE); 746 747 filter->SetLooper(this); 748 fCommonFilters->AddItem(filter); 749 } 750 751 752 bool 753 BLooper::RemoveCommonFilter(BMessageFilter* filter) 754 { 755 AssertLocked(); 756 757 if (!fCommonFilters) 758 return false; 759 760 bool result = fCommonFilters->RemoveItem(filter); 761 if (result) 762 filter->SetLooper(NULL); 763 764 return result; 765 } 766 767 768 void 769 BLooper::SetCommonFilterList(BList* filters) 770 { 771 // We have a somewhat serious problem here. It is entirely possible in R5 772 // to assign a given list of filters to *two* BLoopers simultaneously. This 773 // becomes problematic when the loopers are destroyed: the last looper 774 // destroyed will have a problem when it tries to delete a filter list that 775 // has already been deleted. In R5, this results in a general protection 776 // fault. We fix this by checking the filter list for ownership issues. 777 778 AssertLocked(); 779 780 BMessageFilter* filter; 781 if (filters) { 782 // Check for ownership issues 783 for (int32 i = 0; i < filters->CountItems(); ++i) { 784 filter = (BMessageFilter*)filters->ItemAt(i); 785 if (filter->Looper()) { 786 debugger("A MessageFilter can only be used once."); 787 return; 788 } 789 } 790 } 791 792 if (fCommonFilters) { 793 for (int32 i = 0; i < fCommonFilters->CountItems(); ++i) { 794 delete (BMessageFilter*)fCommonFilters->ItemAt(i); 795 } 796 797 delete fCommonFilters; 798 fCommonFilters = NULL; 799 } 800 801 // Per the BeBook, we take ownership of the list 802 fCommonFilters = filters; 803 if (fCommonFilters) { 804 for (int32 i = 0; i < fCommonFilters->CountItems(); ++i) { 805 filter = (BMessageFilter*)fCommonFilters->ItemAt(i); 806 filter->SetLooper(this); 807 } 808 } 809 } 810 811 812 BList* 813 BLooper::CommonFilterList() const 814 { 815 return fCommonFilters; 816 } 817 818 819 status_t 820 BLooper::Perform(perform_code d, void* arg) 821 { 822 // This is sort of what we're doing for this function everywhere 823 return BHandler::Perform(d, arg); 824 } 825 826 827 BMessage* 828 BLooper::MessageFromPort(bigtime_t timeout) 829 { 830 return ReadMessageFromPort(timeout); 831 } 832 833 834 void BLooper::_ReservedLooper1() {} 835 void BLooper::_ReservedLooper2() {} 836 void BLooper::_ReservedLooper3() {} 837 void BLooper::_ReservedLooper4() {} 838 void BLooper::_ReservedLooper5() {} 839 void BLooper::_ReservedLooper6() {} 840 841 842 BLooper::BLooper(const BLooper&) 843 { 844 // Copy construction not allowed 845 } 846 847 848 BLooper& BLooper::operator=(const BLooper& ) 849 { 850 // Looper copying not allowed 851 return *this; 852 } 853 854 855 BLooper::BLooper(int32 priority, port_id port, const char* name) 856 { 857 // This must be a legacy constructor 858 fMsgPort = port; 859 InitData(name, priority, B_LOOPER_PORT_DEFAULT_CAPACITY); 860 } 861 862 863 status_t 864 BLooper::_PostMessage(BMessage *msg, BHandler *handler, 865 BHandler *replyTo) 866 { 867 BObjectLocker<BLooperList> listLocker(gLooperList); 868 if (!listLocker.IsLocked()) 869 return B_ERROR; 870 871 if (!IsLooperValid(this)) 872 return B_BAD_VALUE; 873 874 // Does handler belong to this looper? 875 if (handler && handler->Looper() != this) 876 return B_MISMATCHED_VALUES; 877 878 status_t status; 879 BMessenger messenger(handler, this, &status); 880 if (status == B_OK) 881 status = messenger.SendMessage(msg, replyTo, 0); 882 883 return status; 884 } 885 886 887 status_t 888 BLooper::_Lock(BLooper* loop, port_id port, bigtime_t timeout) 889 { 890 PRINT(("BLooper::_Lock(%p, %lx)\n", loop, port)); 891 /** 892 @note The assumption I'm under here is that since we can get the port of 893 the BLooper directly from the BLooper itself, the port parameter is 894 for identifying BLoopers by port_id when a pointer to the BLooper in 895 question is not available. So this function has two modes: 896 o When loop != NULL, use it directly 897 o When loop == NULL and port is valid, use the port_id to get 898 the looper 899 I scoured the docs to find out what constitutes a valid port_id to 900 no avail. Since create_port uses the standard error values in its 901 returned port_id, I'll assume that anything less than zero is a safe 902 bet as an *invalid* port_id. I'm guessing that, like thread and 903 semaphore ids, anything >= zero is valid. So, the short version of 904 this reads: if you don't want to find by port_id, make port = -1. 905 906 Another assumption I'm making is that Lock() and LockWithTimeout() 907 are covers for this function. If it turns out that we don't really 908 need this function, I may refactor this code into LockWithTimeout() 909 and have Lock() call it instead. This function could then be 910 removed. 911 */ 912 913 // Check params (loop, port) 914 if (!loop && port < 0) 915 { 916 PRINT(("BLooper::_Lock() done 1\n")); 917 return B_BAD_VALUE; 918 } 919 920 // forward declared so I can use BAutolock on sLooperListLock 921 thread_id curThread; 922 sem_id sem; 923 924 /** 925 @note We lock the looper list at the start of the lock operation to 926 prevent the looper getting removed from the list while we're 927 doing list operations. Also ensures that the looper doesn't 928 get deleted here (since ~BLooper() has to lock the list as 929 well to remove itself). 930 */ 931 int32 oldCount; 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 oldCount = 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 for real 1000 return _LockComplete(loop, oldCount, curThread, sem, timeout); 1001 } 1002 1003 1004 status_t 1005 BLooper::_LockComplete(BLooper *looper, int32 oldCount, thread_id thread, sem_id sem, bigtime_t timeout) 1006 { 1007 status_t err = B_OK; 1008 1009 #if DEBUG < 1 1010 if (oldCount > 0) { 1011 #endif 1012 do { 1013 err = acquire_sem_etc(sem, 1, B_RELATIVE_TIMEOUT, timeout); 1014 } while (err == B_INTERRUPTED); 1015 #if DEBUG < 1 1016 } 1017 #endif 1018 if (err == B_OK) { 1019 // Assign current thread to fOwner 1020 looper->fOwner = thread; 1021 // Reset fOwnerCount to 1 1022 looper->fOwnerCount = 1; 1023 } 1024 PRINT(("BLooper::_LockComplete() done: %lx\n", err)); 1025 return err; 1026 } 1027 1028 1029 void 1030 BLooper::InitData() 1031 { 1032 fOwner = B_ERROR; 1033 fRunCalled = false; 1034 fQueue = new BMessageQueue(); 1035 fCommonFilters = NULL; 1036 fLastMessage = NULL; 1037 fPreferred = NULL; 1038 fTaskID = B_ERROR; 1039 fTerminating = false; 1040 fMsgPort = -1; 1041 fAtomicCount = 0; 1042 1043 if (sTeamID == -1) { 1044 thread_info info; 1045 get_thread_info(find_thread(NULL), &info); 1046 sTeamID = info.team; 1047 } 1048 } 1049 1050 1051 void 1052 BLooper::InitData(const char *name, int32 priority, int32 portCapacity) 1053 { 1054 InitData(); 1055 1056 if (name == NULL) 1057 name = "anonymous looper"; 1058 1059 #if DEBUG 1060 fLockSem = create_sem(1, name); 1061 #else 1062 fLockSem = create_sem(0, name); 1063 #endif 1064 1065 if (portCapacity <= 0) 1066 portCapacity = B_LOOPER_PORT_DEFAULT_CAPACITY; 1067 1068 fMsgPort = create_port(portCapacity, name); 1069 1070 fInitPriority = priority; 1071 1072 BObjectLocker<BLooperList> ListLock(gLooperList); 1073 AddLooper(this); 1074 AddHandler(this); 1075 } 1076 1077 1078 void 1079 BLooper::AddMessage(BMessage* msg) 1080 { 1081 _AddMessagePriv(msg); 1082 1083 // ToDo: if called from a different thread, we need to wake up the looper 1084 } 1085 1086 1087 void 1088 BLooper::_AddMessagePriv(BMessage* msg) 1089 { 1090 // ToDo: if no target token is specified, set to preferred handler 1091 // Others may want to peek into our message queue, so the preferred 1092 // handler must be set correctly already if no token was given 1093 1094 fQueue->AddMessage(msg); 1095 } 1096 1097 1098 status_t 1099 BLooper::_task0_(void *arg) 1100 { 1101 BLooper *looper = (BLooper *)arg; 1102 1103 PRINT(("LOOPER: _task0_()\n")); 1104 1105 if (looper->Lock()) { 1106 PRINT(("LOOPER: looper locked\n")); 1107 looper->task_looper(); 1108 1109 delete looper; 1110 } 1111 1112 PRINT(("LOOPER: _task0_() done: thread %ld\n", find_thread(NULL))); 1113 return B_OK; 1114 } 1115 1116 1117 void * 1118 BLooper::ReadRawFromPort(int32 *msgCode, bigtime_t timeout) 1119 { 1120 PRINT(("BLooper::ReadRawFromPort()\n")); 1121 uint8 *buffer = NULL; 1122 ssize_t bufferSize; 1123 1124 do { 1125 bufferSize = port_buffer_size_etc(fMsgPort, B_RELATIVE_TIMEOUT, timeout); 1126 } while (bufferSize == B_INTERRUPTED); 1127 1128 if (bufferSize < B_OK) { 1129 PRINT(("BLooper::ReadRawFromPort(): failed: %ld\n", bufferSize)); 1130 return NULL; 1131 } 1132 1133 if (bufferSize > 0) 1134 buffer = (uint8 *)malloc(bufferSize); 1135 1136 // we don't want to wait again here, since that can only mean 1137 // that someone else has read our message and our bufferSize 1138 // is now probably wrong 1139 PRINT(("read_port()...\n")); 1140 bufferSize = read_port_etc(fMsgPort, msgCode, buffer, bufferSize, 1141 B_RELATIVE_TIMEOUT, 0); 1142 1143 if (bufferSize < B_OK) { 1144 free(buffer); 1145 return NULL; 1146 } 1147 1148 PRINT(("BLooper::ReadRawFromPort() read: %.4s, %p (%d bytes)\n", (char *)msgCode, buffer, bufferSize)); 1149 return buffer; 1150 } 1151 1152 1153 BMessage * 1154 BLooper::ReadMessageFromPort(bigtime_t timeout) 1155 { 1156 PRINT(("BLooper::ReadMessageFromPort()\n")); 1157 int32 msgCode; 1158 BMessage *message = NULL; 1159 1160 void *buffer = ReadRawFromPort(&msgCode, timeout); 1161 if (!buffer) 1162 return NULL; 1163 1164 message = ConvertToMessage(buffer, msgCode); 1165 free(buffer); 1166 1167 PRINT(("BLooper::ReadMessageFromPort() done: %p\n", message)); 1168 return message; 1169 } 1170 1171 1172 BMessage * 1173 BLooper::ConvertToMessage(void *buffer, int32 code) 1174 { 1175 PRINT(("BLooper::ConvertToMessage()\n")); 1176 if (!buffer) 1177 return NULL; 1178 1179 BMessage *message = new BMessage(); 1180 if (message->Unflatten((const char *)buffer) != B_OK) { 1181 PRINT(("BLooper::ConvertToMessage(): unflattening message failed\n")); 1182 delete message; 1183 message = NULL; 1184 } 1185 1186 PRINT(("BLooper::ConvertToMessage(): %p\n", message)); 1187 return message; 1188 } 1189 1190 1191 void 1192 BLooper::task_looper() 1193 { 1194 PRINT(("BLooper::task_looper()\n")); 1195 // Check that looper is locked (should be) 1196 AssertLocked(); 1197 // Unlock the looper 1198 Unlock(); 1199 1200 if (IsLocked()) 1201 debugger("looper must not be locked!"); 1202 1203 // loop: As long as we are not terminating. 1204 while (!fTerminating) { 1205 PRINT(("LOOPER: outer loop\n")); 1206 // TODO: timeout determination algo 1207 // Read from message port (how do we determine what the timeout is?) 1208 PRINT(("LOOPER: MessageFromPort()...\n")); 1209 BMessage *msg = MessageFromPort(); 1210 PRINT(("LOOPER: ...done\n")); 1211 1212 // Did we get a message? 1213 if (msg) 1214 _AddMessagePriv(msg); 1215 1216 // Get message count from port 1217 int32 msgCount = port_count(fMsgPort); 1218 for (int32 i = 0; i < msgCount; ++i) { 1219 // Read 'count' messages from port (so we will not block) 1220 // We use zero as our timeout since we know there is stuff there 1221 msg = MessageFromPort(0); 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(); 1235 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 BHandler *handler = NULL; 1247 BMessage::Private messagePrivate(fLastMessage); 1248 bool usePreferred = messagePrivate.UsePreferredTarget(); 1249 1250 if (usePreferred) { 1251 PRINT(("LOOPER: use preferred target\n")); 1252 handler = fPreferred; 1253 if (handler == NULL) 1254 handler = this; 1255 } else { 1256 gDefaultTokens.GetToken(messagePrivate.GetTarget(), 1257 B_HANDLER_TOKEN, (void **)&handler); 1258 1259 // if this handler doesn't belong to us, we drop the message 1260 if (handler != NULL && handler->Looper() != this) 1261 handler = NULL; 1262 1263 PRINT(("LOOPER: use %ld, handler: %p, this: %p\n", 1264 messagePrivate.GetTarget(), handler, this)); 1265 } 1266 1267 // Is this a scripting message? (BMessage::HasSpecifiers()) 1268 if (handler != NULL && fLastMessage->HasSpecifiers()) { 1269 int32 index = 0; 1270 // Make sure the current specifier is kosher 1271 if (fLastMessage->GetCurrentSpecifier(&index) == B_OK) 1272 handler = resolve_specifier(handler, fLastMessage); 1273 } 1274 1275 if (handler) { 1276 // Do filtering 1277 handler = _TopLevelFilter(fLastMessage, handler); 1278 PRINT(("LOOPER: _TopLevelFilter(): %p\n", handler)); 1279 if (handler && handler->Looper() == this) 1280 DispatchMessage(fLastMessage, handler); 1281 } 1282 } 1283 1284 if (fTerminating) { 1285 // we leave the looper locked when we quit 1286 return; 1287 } 1288 1289 // Unlock the looper 1290 Unlock(); 1291 1292 // Delete the current message (fLastMessage) 1293 if (fLastMessage) { 1294 delete fLastMessage; 1295 fLastMessage = NULL; 1296 } 1297 1298 // Are any messages on the port? 1299 if (port_count(fMsgPort) > 0) { 1300 // Do outer loop 1301 dispatchNextMessage = false; 1302 } 1303 } 1304 } 1305 PRINT(("BLooper::task_looper() done\n")); 1306 } 1307 1308 1309 void 1310 BLooper::_QuitRequested(BMessage *msg) 1311 { 1312 bool isQuitting = QuitRequested(); 1313 1314 // We send a reply to the sender, when they're waiting for a reply or 1315 // if the request message contains a boolean "_shutdown_" field with value 1316 // true. In the latter case the message came from the registrar, asking 1317 // the application to shut down. 1318 bool shutdown; 1319 if (msg->IsSourceWaiting() 1320 || (msg->FindBool("_shutdown_", &shutdown) == B_OK && shutdown)) { 1321 BMessage ReplyMsg(B_REPLY); 1322 ReplyMsg.AddBool("result", isQuitting); 1323 ReplyMsg.AddInt32("thread", fTaskID); 1324 msg->SendReply(&ReplyMsg); 1325 } 1326 1327 if (isQuitting) 1328 Quit(); 1329 } 1330 1331 1332 bool 1333 BLooper::AssertLocked() const 1334 { 1335 if (!IsLocked()) { 1336 debugger("looper must be locked before proceeding\n"); 1337 return false; 1338 } 1339 1340 return true; 1341 } 1342 1343 1344 BHandler * 1345 BLooper::_TopLevelFilter(BMessage* msg, BHandler* target) 1346 { 1347 if (msg) { 1348 // Apply the common filters first 1349 target = _ApplyFilters(CommonFilterList(), msg, target); 1350 if (target) { 1351 if (target->Looper() != this) { 1352 debugger("Targeted handler does not belong to the looper."); 1353 target = NULL; 1354 } else { 1355 // Now apply handler-specific filters 1356 target = _HandlerFilter(msg, target); 1357 } 1358 } 1359 } 1360 1361 return target; 1362 } 1363 1364 1365 BHandler * 1366 BLooper::_HandlerFilter(BMessage* msg, BHandler* target) 1367 { 1368 // Keep running filters until our handler is NULL, or until the filtering 1369 // handler returns itself as the designated handler 1370 BHandler* previousTarget = NULL; 1371 while (target != NULL && target != previousTarget) { 1372 previousTarget = target; 1373 1374 target = _ApplyFilters(target->FilterList(), msg, target); 1375 if (target != NULL && target->Looper() != this) { 1376 debugger("Targeted handler does not belong to the looper."); 1377 target = NULL; 1378 } 1379 } 1380 1381 return target; 1382 } 1383 1384 1385 BHandler * 1386 BLooper::_ApplyFilters(BList* list, BMessage* msg, BHandler* target) 1387 { 1388 // This is where the action is! 1389 // Check the parameters 1390 if (!list || !msg) 1391 return target; 1392 1393 // For each filter in the provided list 1394 BMessageFilter* filter = NULL; 1395 for (int32 i = 0; i < list->CountItems(); ++i) { 1396 filter = (BMessageFilter*)list->ItemAt(i); 1397 1398 // Check command conditions 1399 if (filter->FiltersAnyCommand() || (filter->Command() == msg->what)) { 1400 // Check delivery conditions 1401 message_delivery delivery = filter->MessageDelivery(); 1402 bool dropped = msg->WasDropped(); 1403 if (delivery == B_ANY_DELIVERY 1404 || (delivery == B_DROPPED_DELIVERY && dropped) 1405 || (delivery == B_PROGRAMMED_DELIVERY && !dropped)) { 1406 // Check source conditions 1407 message_source source = filter->MessageSource(); 1408 bool remote = msg->IsSourceRemote(); 1409 if (source == B_ANY_SOURCE 1410 || (source == B_REMOTE_SOURCE && remote) 1411 || (source == B_LOCAL_SOURCE && !remote)) { 1412 // Are we using an "external" function? 1413 filter_result result; 1414 filter_hook func = filter->FilterFunction(); 1415 if (func) 1416 result = func(msg, &target, filter); 1417 else 1418 result = filter->Filter(msg, &target); 1419 1420 // Is further processing allowed? 1421 if (result == B_SKIP_MESSAGE) { 1422 // No; time to bail out 1423 return NULL; 1424 } 1425 } 1426 } 1427 } 1428 } 1429 1430 return target; 1431 } 1432 1433 1434 void 1435 BLooper::check_lock() 1436 { 1437 // This is a cheap variant of AssertLocked() 1438 // It is used in situations where it's clear that the looper is valid, 1439 // ie. from handlers 1440 if (fOwner == -1 || fOwner != find_thread(NULL)) 1441 debugger("Looper must be locked."); 1442 } 1443 1444 1445 BHandler * 1446 BLooper::resolve_specifier(BHandler* target, BMessage* msg) 1447 { 1448 // Check params 1449 if (!target || !msg) 1450 return NULL; 1451 1452 int32 index; 1453 BMessage specifier; 1454 int32 form; 1455 const char* property; 1456 status_t err = B_OK; 1457 BHandler* newTarget = target; 1458 // Loop to deal with nested specifiers 1459 // (e.g., the 3rd button on the 4th view) 1460 do { 1461 err = msg->GetCurrentSpecifier(&index, &specifier, &form, &property); 1462 if (err) { 1463 BMessage reply(B_REPLY); 1464 reply.AddInt32("error", err); 1465 msg->SendReply(&reply); 1466 return NULL; 1467 } 1468 // Current target gets what was the new target 1469 target = newTarget; 1470 newTarget = target->ResolveSpecifier(msg, index, &specifier, form, 1471 property); 1472 // Check that new target is owned by looper; 1473 // use IndexOf() to avoid dereferencing newTarget 1474 // (possible race condition with object destruction 1475 // by another looper) 1476 if (!newTarget || IndexOf(newTarget) < 0) 1477 return NULL; 1478 1479 // Get current specifier index (may change in ResolveSpecifier()) 1480 msg->GetCurrentSpecifier(&index); 1481 } while (newTarget && newTarget != target && index >= 0); 1482 1483 return newTarget; 1484 } 1485 1486 1487 void 1488 BLooper::UnlockFully() 1489 { 1490 AssertLocked(); 1491 1492 /** 1493 @note What we're doing here is completely undoing the current owner's lock 1494 on the looper. This is actually pretty easy, since the owner only 1495 has a single aquisition on the semaphore; every subsequent "lock" 1496 is just an increment to the owner count. The whole thing is quite 1497 similar to Unlock(), except that we clear the ownership variables, 1498 rather than merely decrementing them. 1499 */ 1500 // Clear the owner count 1501 fOwnerCount = 0; 1502 // Nobody owns the lock now 1503 fOwner = -1; 1504 #if DEBUG < 1 1505 // There is now one less thread holding a lock on this looper 1506 int32 atomicCount = atomic_add(&fAtomicCount, -1); 1507 if (atomicCount > 1) 1508 #endif 1509 release_sem(fLockSem); 1510 } 1511 1512 1513 void 1514 BLooper::AddLooper(BLooper *looper) 1515 { 1516 if (gLooperList.IsLocked()) 1517 gLooperList.AddLooper(looper); 1518 } 1519 1520 1521 bool 1522 BLooper::IsLooperValid(const BLooper *looper) 1523 { 1524 if (gLooperList.IsLocked()) 1525 return gLooperList.IsLooperValid(looper); 1526 1527 return false; 1528 } 1529 1530 1531 void 1532 BLooper::RemoveLooper(BLooper *looper) 1533 { 1534 if (gLooperList.IsLocked()) 1535 gLooperList.RemoveLooper(looper); 1536 } 1537 1538 1539 void 1540 BLooper::GetLooperList(BList* list) 1541 { 1542 BObjectLocker<BLooperList> ListLock(gLooperList); 1543 if (ListLock.IsLocked()) 1544 gLooperList.GetLooperList(list); 1545 } 1546 1547 1548 BLooper * 1549 BLooper::LooperForName(const char* name) 1550 { 1551 if (gLooperList.IsLocked()) 1552 return gLooperList.LooperForName(name); 1553 1554 return NULL; 1555 } 1556 1557 1558 BLooper * 1559 BLooper::LooperForPort(port_id port) 1560 { 1561 if (gLooperList.IsLocked()) 1562 return gLooperList.LooperForPort(port); 1563 1564 return NULL; 1565 } 1566 1567 1568 // #pragma mark - 1569 1570 1571 port_id 1572 _get_looper_port_(const BLooper *looper) 1573 { 1574 return looper->fMsgPort; 1575 } 1576 1577