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