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 = B_ERROR; 77 team_id BLooper::sTeamID = 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()...\n")); 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 if (PropertyInfo.FindMatch(msg, index, specifier, form, property, &data) >= 0) 733 { 734 switch (data) 735 { 736 case BLOOPER_PROCESS_INTERNALLY: 737 return this; 738 739 case BLOOPER_HANDLER_BY_INDEX: 740 { 741 int32 index = specifier->FindInt32("index"); 742 if (form == B_REVERSE_INDEX_SPECIFIER) 743 { 744 index = CountHandlers() - index; 745 } 746 BHandler* target = HandlerAt(index); 747 if (target) 748 { 749 // Specifier has been fully handled 750 msg->PopSpecifier(); 751 return target; 752 } 753 else 754 { 755 // TODO: test and implement 756 } 757 } 758 } 759 } 760 else 761 { 762 return BHandler::ResolveSpecifier(msg, index, specifier, form, 763 property); 764 } 765 766 BMessage Reply(B_MESSAGE_NOT_UNDERSTOOD); 767 Reply.AddInt32("error", B_BAD_SCRIPT_SYNTAX); 768 Reply.AddString("message", "Didn't understand the specifier(s)"); 769 msg->SendReply(&Reply); 770 771 return NULL; 772 } 773 //------------------------------------------------------------------------------ 774 status_t BLooper::GetSupportedSuites(BMessage* data) 775 { 776 status_t err; 777 if (!data) 778 { 779 err = B_BAD_VALUE; 780 } 781 782 if (!err) 783 { 784 err = data->AddString("Suites", "suite/vnd.Be-handler"); 785 if (!err) 786 { 787 BPropertyInfo PropertyInfo(gLooperPropInfo); 788 err = data->AddFlat("message", &PropertyInfo); 789 if (!err) 790 { 791 err = BHandler::GetSupportedSuites(data); 792 } 793 } 794 } 795 796 return err; 797 } 798 //------------------------------------------------------------------------------ 799 void BLooper::AddCommonFilter(BMessageFilter* filter) 800 { 801 if (!filter) 802 { 803 return; 804 } 805 806 if (!IsLocked()) 807 { 808 debugger("Owning Looper must be locked before calling AddCommonFilter"); 809 return; 810 } 811 812 if (filter->Looper()) 813 { 814 debugger("A MessageFilter can only be used once."); 815 return; 816 } 817 818 if (!fCommonFilters) 819 { 820 fCommonFilters = new BList(FILTER_LIST_BLOCK_SIZE); 821 } 822 filter->SetLooper(this); 823 fCommonFilters->AddItem(filter); 824 } 825 //------------------------------------------------------------------------------ 826 bool BLooper::RemoveCommonFilter(BMessageFilter* filter) 827 { 828 if (!IsLocked()) 829 { 830 debugger("Owning Looper must be locked before calling " 831 "RemoveCommonFilter"); 832 return false; 833 } 834 835 if (!fCommonFilters) 836 { 837 return false; 838 } 839 840 bool result = fCommonFilters->RemoveItem(filter); 841 if (result) 842 { 843 filter->SetLooper(NULL); 844 } 845 846 return result; 847 } 848 //------------------------------------------------------------------------------ 849 void BLooper::SetCommonFilterList(BList* filters) 850 { 851 // We have a somewhat serious problem here. It is entirely possible in R5 852 // to assign a given list of filters to *two* BLoopers simultaneously. This 853 // becomes problematic when the loopers are destroyed: the last looper 854 // destroyed will have a problem when it tries to delete a filter list that 855 // has already been deleted. In R5, this results in a general protection 856 // fault. We fix this by checking the filter list for ownership issues. 857 858 if (!IsLocked()) 859 { 860 debugger("Owning Looper must be locked before calling " 861 "SetCommonFilterList"); 862 return; 863 } 864 865 BMessageFilter* filter; 866 if (filters) 867 { 868 // Check for ownership issues 869 for (int32 i = 0; i < filters->CountItems(); ++i) 870 { 871 filter = (BMessageFilter*)filters->ItemAt(i); 872 if (filter->Looper()) 873 { 874 debugger("A MessageFilter can only be used once."); 875 return; 876 } 877 } 878 } 879 880 if (fCommonFilters) 881 { 882 for (int32 i = 0; i < fCommonFilters->CountItems(); ++i) 883 { 884 delete fCommonFilters->ItemAt(i); 885 } 886 887 delete fCommonFilters; 888 fCommonFilters = NULL; 889 } 890 891 // Per the BeBook, we take ownership of the list 892 fCommonFilters = filters; 893 if (fCommonFilters) 894 { 895 for (int32 i = 0; i < fCommonFilters->CountItems(); ++i) 896 { 897 filter = (BMessageFilter*)fCommonFilters->ItemAt(i); 898 filter->SetLooper(this); 899 } 900 } 901 } 902 //------------------------------------------------------------------------------ 903 BList* BLooper::CommonFilterList() const 904 { 905 return fCommonFilters; 906 } 907 //------------------------------------------------------------------------------ 908 status_t BLooper::Perform(perform_code d, void* arg) 909 { 910 // This is sort of what we're doing for this function everywhere 911 return BHandler::Perform(d, arg); 912 } 913 //------------------------------------------------------------------------------ 914 BMessage* BLooper::MessageFromPort(bigtime_t timeout) 915 { 916 return ReadMessageFromPort(timeout); 917 } 918 //------------------------------------------------------------------------------ 919 void BLooper::_ReservedLooper1() 920 { 921 } 922 //------------------------------------------------------------------------------ 923 void BLooper::_ReservedLooper2() 924 { 925 } 926 //------------------------------------------------------------------------------ 927 void BLooper::_ReservedLooper3() 928 { 929 } 930 //------------------------------------------------------------------------------ 931 void BLooper::_ReservedLooper4() 932 { 933 } 934 //------------------------------------------------------------------------------ 935 void BLooper::_ReservedLooper5() 936 { 937 } 938 //------------------------------------------------------------------------------ 939 void BLooper::_ReservedLooper6() 940 { 941 } 942 //------------------------------------------------------------------------------ 943 BLooper::BLooper(const BLooper&) 944 { 945 // Copy construction not allowed 946 } 947 //------------------------------------------------------------------------------ 948 BLooper& BLooper::operator=(const BLooper& ) 949 { 950 // Looper copying not allowed 951 return *this; 952 } 953 //------------------------------------------------------------------------------ 954 BLooper::BLooper(int32 priority, port_id port, const char* name) 955 { 956 // This must be a legacy constructor 957 fMsgPort = port; 958 InitData(name, priority, B_LOOPER_PORT_DEFAULT_CAPACITY); 959 } 960 //------------------------------------------------------------------------------ 961 status_t BLooper::_PostMessage(BMessage* msg, BHandler* handler, 962 BHandler* reply_to) 963 { 964 BObjectLocker<BLooperList> ListLock(gLooperList); 965 if (!ListLock.IsLocked()) 966 { 967 return B_BAD_VALUE; 968 } 969 970 if (!IsLooperValid(this)) 971 { 972 return B_BAD_VALUE; 973 } 974 975 // Does handler belong to this looper? 976 if (handler && handler->Looper() != this) 977 { 978 return B_MISMATCHED_VALUES; 979 } 980 981 status_t err = B_OK; 982 983 BMessenger Messenger(handler, this, &err); 984 985 if (!err) 986 { 987 err = Messenger.SendMessage(msg, reply_to, 0); 988 } 989 990 return err; 991 } 992 //------------------------------------------------------------------------------ 993 status_t BLooper::_Lock(BLooper* loop, port_id port, bigtime_t timeout) 994 { 995 DBG(OUT("BLooper::_Lock(%p, %lx)\n", loop, port)); 996 /** 997 @note The assumption I'm under here is that since we can get the port of 998 the BLooper directly from the BLooper itself, the port parameter is 999 for identifying BLoopers by port_id when a pointer to the BLooper in 1000 question is not available. So this function has two modes: 1001 o When loop != NULL, use it directly 1002 o When loop == NULL and port is valid, use the port_id to get 1003 the looper 1004 I scoured the docs to find out what constitutes a valid port_id to 1005 no avail. Since create_port uses the standard error values in its 1006 returned port_id, I'll assume that anything less than zero is a safe 1007 bet as an *invalid* port_id. I'm guessing that, like thread and 1008 semaphore ids, anything >= zero is valid. So, the short version of 1009 this reads: if you don't want to find by port_id, make port = -1. 1010 1011 Another assumption I'm making is that Lock() and LockWithTimeout() 1012 are covers for this function. If it turns out that we don't really 1013 need this function, I may refactor this code into LockWithTimeout() 1014 and have Lock() call it instead. This function could then be 1015 removed. 1016 */ 1017 1018 // Check params (loop, port) 1019 if (!loop && port < 0) 1020 { 1021 DBG(OUT("BLooper::_Lock() done 1\n")); 1022 return B_BAD_VALUE; 1023 } 1024 1025 // forward declared so I can use BAutolock on sLooperListLock 1026 thread_id curThread; 1027 sem_id sem; 1028 1029 /** 1030 @note We lock the looper list at the start of the lock operation to 1031 prevent the looper getting removed from the list while we're 1032 doing list operations. Also ensures that the looper doesn't 1033 get deleted here (since ~BLooper() has to lock the list as 1034 well to remove itself). 1035 */ 1036 { 1037 BObjectLocker<BLooperList> ListLock(gLooperList); 1038 if (!ListLock.IsLocked()) 1039 { 1040 // If we can't lock, the semaphore is probably 1041 // gone, which leaves us in no-man's land 1042 DBG(OUT("BLooper::_Lock() done 2\n")); 1043 return B_BAD_VALUE; 1044 } 1045 1046 // Look up looper by port_id, if necessary 1047 if (!loop) 1048 { 1049 loop = LooperForPort(port); 1050 if (!loop) 1051 { 1052 DBG(OUT("BLooper::_Lock() done 3\n")); 1053 return B_BAD_VALUE; 1054 } 1055 } 1056 else 1057 { 1058 // Check looper validity 1059 if (!IsLooperValid(loop)) 1060 { 1061 DBG(OUT("BLooper::_Lock() done 4\n")); 1062 return B_BAD_VALUE; 1063 } 1064 } 1065 1066 // Is the looper trying to lock itself? 1067 // Check for nested lock attempt 1068 curThread = find_thread(NULL); 1069 if (curThread == loop->fOwner) 1070 { 1071 // Bump fOwnerCount 1072 ++loop->fOwnerCount; 1073 DBG(OUT("BLooper::_Lock() done 5: fOwnerCount: %ld\n", loop->fOwnerCount)); 1074 return B_OK; 1075 } 1076 1077 // Something external to the looper is attempting to lock 1078 // Cache the semaphore 1079 sem = loop->fLockSem; 1080 1081 // Validate the semaphore 1082 if (sem < 0) 1083 { 1084 DBG(OUT("BLooper::_Lock() done 6\n")); 1085 return B_BAD_VALUE; 1086 } 1087 1088 // Bump the requested lock count (using fAtomicCount for this) 1089 atomic_add(&loop->fAtomicCount, 1); 1090 1091 // sLooperListLock automatically released here 1092 } 1093 1094 /** 1095 @note We have to operate with the looper list unlocked during semaphore 1096 acquisition so that the rest of the application doesn't have to 1097 wait for this lock to happen. This is why we cached fLockSem 1098 earlier -- with the list unlocked, the looper might get deleted 1099 right out from under us. This is also why we use a raw semaphore 1100 instead of the easier-to-deal-with BLocker; you can't cache a 1101 BLocker. 1102 */ 1103 // acquire the lock 1104 status_t err; 1105 do 1106 { 1107 err = acquire_sem_etc(sem, 1, B_RELATIVE_TIMEOUT, timeout); 1108 } while (err == B_INTERRUPTED); 1109 1110 if (!err) 1111 { 1112 // Assign current thread to fOwner 1113 loop->fOwner = curThread; 1114 // Reset fOwnerCount to 1 1115 loop->fOwnerCount = 1; 1116 } 1117 1118 DBG(OUT("BLooper::_Lock() done: %lx\n", err)); 1119 return err; 1120 } 1121 //------------------------------------------------------------------------------ 1122 status_t BLooper::_LockComplete(BLooper* loop, int32 old, thread_id this_tid, 1123 sem_id sem, bigtime_t timeout) 1124 { 1125 // What is this for? Hope I'm not missing something conceptually here ... 1126 return B_ERROR; 1127 } 1128 //------------------------------------------------------------------------------ 1129 void BLooper::InitData() 1130 { 1131 fOwner = B_ERROR; 1132 fRunCalled = false; 1133 fQueue = new BMessageQueue(); 1134 fCommonFilters = NULL; 1135 fLastMessage = NULL; 1136 fPreferred = NULL; 1137 fTaskID = B_ERROR; 1138 fTerminating = false; 1139 fMsgPort = -1; 1140 1141 if (sTeamID == -1) 1142 { 1143 thread_info info; 1144 get_thread_info(find_thread(NULL), &info); 1145 sTeamID = info.team; 1146 } 1147 1148 } 1149 //------------------------------------------------------------------------------ 1150 void BLooper::InitData(const char* name, int32 priority, int32 port_capacity) 1151 { 1152 InitData(); 1153 1154 fLockSem = create_sem(1, name); 1155 1156 if (port_capacity <= 0) 1157 { 1158 port_capacity = B_LOOPER_PORT_DEFAULT_CAPACITY; 1159 } 1160 1161 fMsgPort = create_port(port_capacity, name ? name : "LooperPort"); 1162 1163 fInitPriority = priority; 1164 1165 BObjectLocker<BLooperList> ListLock(gLooperList); 1166 AddLooper(this); 1167 AddHandler(this); 1168 } 1169 //------------------------------------------------------------------------------ 1170 void BLooper::AddMessage(BMessage* msg) 1171 { 1172 // NOTE: Why is this here? 1173 } 1174 //------------------------------------------------------------------------------ 1175 void BLooper::_AddMessagePriv(BMessage* msg) 1176 { 1177 // NOTE: No, really; why the hell is this here?? 1178 } 1179 //------------------------------------------------------------------------------ 1180 status_t BLooper::_task0_(void* arg) 1181 { 1182 DBG(OUT("LOOPER: _task0_()\n")); 1183 BLooper* obj = (BLooper*)arg; 1184 1185 DBG(OUT("LOOPER: locking looper...\n")); 1186 if (obj->Lock()) 1187 { 1188 DBG(OUT("LOOPER: looper locked\n")); 1189 obj->task_looper(); 1190 obj->fTerminating = true; 1191 delete obj; 1192 } 1193 1194 return B_OK; 1195 } 1196 //------------------------------------------------------------------------------ 1197 void* BLooper::ReadRawFromPort(int32* msgcode, bigtime_t tout) 1198 { 1199 DBG(OUT("BLooper::ReadRawFromPort()\n")); 1200 int8* msgbuffer = NULL; 1201 ssize_t buffersize; 1202 ssize_t bytesread; 1203 1204 if (tout == B_INFINITE_TIMEOUT) 1205 { 1206 buffersize = port_buffer_size(fMsgPort); 1207 DBG(OUT("BLooper::ReadRawFromPort(): buffersize: %ld\n", buffersize)); 1208 } 1209 else 1210 { 1211 buffersize = port_buffer_size_etc(fMsgPort, 0, tout); 1212 if (buffersize == B_TIMED_OUT || buffersize == B_BAD_PORT_ID || 1213 buffersize == B_WOULD_BLOCK) 1214 { 1215 DBG(OUT("BLooper::ReadRawFromPort() done 1\n")); 1216 return NULL; 1217 } 1218 } 1219 1220 if (buffersize > 0) 1221 msgbuffer = new int8[buffersize]; 1222 1223 if (tout == B_INFINITE_TIMEOUT) 1224 { 1225 DBG(OUT("read_port()...\n")); 1226 bytesread = read_port(fMsgPort, msgcode, msgbuffer, buffersize); 1227 DBG(OUT("read_port() done: %ld\n", bytesread)); 1228 DBG(OUT("BLooper::ReadRawFromPort() read: %.4s\n", (char*)msgcode)); 1229 } 1230 else 1231 { 1232 bytesread = read_port_etc(fMsgPort, msgcode, msgbuffer, buffersize, 1233 B_TIMEOUT, tout); 1234 } 1235 1236 DBG(OUT("BLooper::ReadRawFromPort() done: %p\n", msgbuffer)); 1237 return msgbuffer; 1238 } 1239 //------------------------------------------------------------------------------ 1240 BMessage* BLooper::ReadMessageFromPort(bigtime_t tout) 1241 { 1242 DBG(OUT("BLooper::ReadMessageFromPort()\n")); 1243 int32 msgcode; 1244 BMessage* bmsg; 1245 1246 void* msgbuffer = ReadRawFromPort(&msgcode, tout); 1247 1248 bmsg = ConvertToMessage(msgbuffer, msgcode); 1249 1250 if (msgbuffer) 1251 { 1252 delete[] msgbuffer; 1253 } 1254 1255 DBG(OUT("BLooper::ReadMessageFromPort() done: %p\n", bmsg)); 1256 return bmsg; 1257 } 1258 //------------------------------------------------------------------------------ 1259 BMessage* BLooper::ConvertToMessage(void* raw, int32 code) 1260 { 1261 DBG(OUT("BLooper::ConvertToMessage()\n")); 1262 BMessage* bmsg = new BMessage(code); 1263 1264 if (raw != NULL) 1265 { 1266 if (bmsg->Unflatten((const char*)raw) != B_OK) 1267 { 1268 DBG(OUT("BLooper::ConvertToMessage(): unflattening message failed\n")); 1269 delete bmsg; 1270 bmsg = NULL; 1271 } 1272 } 1273 1274 DBG(OUT("BLooper::ConvertToMessage(): %p\n", bmsg)); 1275 return bmsg; 1276 } 1277 //------------------------------------------------------------------------------ 1278 void BLooper::task_looper() 1279 { 1280 DBG(OUT("BLooper::task_looper()\n")); 1281 // Check that looper is locked (should be) 1282 AssertLocked(); 1283 // Unlock the looper 1284 Unlock(); 1285 1286 // loop: As long as we are not terminating. 1287 while (!fTerminating) 1288 { 1289 DBG(OUT("LOOPER: outer loop\n")); 1290 // TODO: timeout determination algo 1291 // Read from message port (how do we determine what the timeout is?) 1292 DBG(OUT("LOOPER: MessageFromPort()...\n")); 1293 BMessage* msg = MessageFromPort(); 1294 DBG(OUT("LOOPER: ...done\n")); 1295 1296 // Did we get a message? 1297 if (msg) 1298 { 1299 DBG(OUT("LOOPER: got message\n")); 1300 // Add to queue 1301 fQueue->AddMessage(msg); 1302 } 1303 else 1304 DBG(OUT("LOOPER: got no message\n")); 1305 1306 // Get message count from port 1307 int32 msgCount = port_count(fMsgPort); 1308 for (int32 i = 0; i < msgCount; ++i) 1309 { 1310 // Read 'count' messages from port (so we will not block) 1311 // We use zero as our timeout since we know there is stuff there 1312 msg = MessageFromPort(0); 1313 // Add messages to queue 1314 if (msg) 1315 { 1316 fQueue->AddMessage(msg); 1317 } 1318 } 1319 1320 // loop: As long as there are messages in the queue and the port is 1321 // empty... and we are not terminating, of course. 1322 bool dispatchNextMessage = true; 1323 while (!fTerminating && dispatchNextMessage) 1324 { 1325 DBG(OUT("LOOPER: inner loop\n")); 1326 // Get next message from queue (assign to fLastMessage) 1327 fLastMessage = fQueue->NextMessage(); 1328 1329 // Lock the looper 1330 Lock(); 1331 if (!fLastMessage) 1332 { 1333 // No more messages: Unlock the looper and terminate the 1334 // dispatch loop. 1335 dispatchNextMessage = false; 1336 } 1337 else 1338 { 1339 DBG(OUT("LOOPER: fLastMessage: 0x%lx: %.4s\n", fLastMessage->what, 1340 (char*)&fLastMessage->what)); 1341 DBG(fLastMessage->PrintToStream()); 1342 // Get the target handler 1343 // Use BMessage friend functions to determine if we are using the 1344 // preferred handler, or if a target has been specified 1345 BHandler* handler; 1346 if (_use_preferred_target_(fLastMessage)) 1347 { 1348 DBG(OUT("LOOPER: use preferred target\n")); 1349 handler = fPreferred; 1350 } 1351 else 1352 { 1353 DBG(OUT("LOOPER: don't use preferred target\n")); 1354 /** 1355 @note Here is where all the token stuff starts to 1356 make sense. How, exactly, do we determine 1357 what the target BHandler is? If we look at 1358 BMessage, we see an int32 field, fTarget. 1359 Amazingly, we happen to have a global mapping 1360 of BHandler pointers to int32s! 1361 */ 1362 DBG(OUT("LOOPER: use: %ld\n", _get_message_target_(fLastMessage))); 1363 gDefaultTokens.GetToken(_get_message_target_(fLastMessage), 1364 B_HANDLER_TOKEN, 1365 (void**)&handler); 1366 DBG(OUT("LOOPER: handler: %p, this: %p\n", handler, this)); 1367 } 1368 1369 if (!handler) 1370 { 1371 DBG(OUT("LOOPER: no target handler, use this\n")); 1372 handler = this; 1373 } 1374 1375 // Is this a scripting message? (BMessage::HasSpecifiers()) 1376 if (fLastMessage->HasSpecifiers()) 1377 { 1378 int32 index = 0; 1379 // Make sure the current specifier is kosher 1380 if (fLastMessage->GetCurrentSpecifier(&index) == B_OK) 1381 { 1382 handler = resolve_specifier(handler, fLastMessage); 1383 } 1384 } 1385 else 1386 DBG(OUT("LOOPER: no scripting message\n")); 1387 1388 if (handler) 1389 { 1390 // Do filtering 1391 handler = top_level_filter(fLastMessage, handler); 1392 DBG(OUT("LOOPER: top_level_filter(): %p\n", handler)); 1393 if (handler && handler->Looper() == this) 1394 { 1395 DispatchMessage(fLastMessage, handler); 1396 } 1397 } 1398 } 1399 1400 // Unlock the looper 1401 Unlock(); 1402 1403 // Delete the current message (fLastMessage) 1404 if (fLastMessage) 1405 { 1406 delete fLastMessage; 1407 fLastMessage = NULL; 1408 } 1409 1410 // Are any messages on the port? 1411 if (port_count(fMsgPort) > 0) 1412 { 1413 // Do outer loop 1414 dispatchNextMessage = false; 1415 } 1416 } 1417 } 1418 DBG(OUT("BLooper::task_looper() done\n")); 1419 } 1420 //------------------------------------------------------------------------------ 1421 void BLooper::do_quit_requested(BMessage* msg) 1422 { 1423 /** 1424 @note I couldn't figure out why do_quit_requested() was necessary; why not 1425 just call Quit()? Then, while writing the PostMessage() code, I 1426 realized that the sender of the B_QUIT_REQUESTED message just might 1427 be waiting for a reply. A quick test, and yes, we get a reply 1428 which consists of: 1429 what: B_REPLY 1430 "result" (bool) return of QuitRequested() 1431 "thread" (int32) the looper's thread id 1432 1433 While Quit() could use fLastMessage, it makes more sense that 1434 do_quit_requested() would handle it since it gets passed the 1435 message. 1436 */ 1437 1438 bool isQuitting = QuitRequested(); 1439 1440 if (msg->IsSourceWaiting()) 1441 { 1442 BMessage ReplyMsg(B_REPLY); 1443 ReplyMsg.AddBool("result", isQuitting); 1444 ReplyMsg.AddInt32("thread", fTaskID); 1445 msg->SendReply(&ReplyMsg); 1446 } 1447 1448 if (isQuitting) 1449 { 1450 Quit(); 1451 } 1452 } 1453 //------------------------------------------------------------------------------ 1454 bool BLooper::AssertLocked() const 1455 { 1456 if (!IsLocked()) 1457 { 1458 debugger("looper must be locked before proceeding\n"); 1459 return false; 1460 } 1461 1462 return true; 1463 } 1464 //------------------------------------------------------------------------------ 1465 BHandler* BLooper::top_level_filter(BMessage* msg, BHandler* target) 1466 { 1467 if (msg) 1468 { 1469 // Apply the common filters first 1470 target = apply_filters(CommonFilterList(), msg, target); 1471 if (target) 1472 { 1473 if (target->Looper() != this) 1474 { 1475 // TODO: debugger message? 1476 target = NULL; 1477 } 1478 else 1479 { 1480 // Now apply handler-specific filters 1481 target = handler_only_filter(msg, target); 1482 } 1483 } 1484 } 1485 1486 return target; 1487 } 1488 //------------------------------------------------------------------------------ 1489 BHandler* BLooper::handler_only_filter(BMessage* msg, BHandler* target) 1490 { 1491 // Keep running filters until our handler is NULL, or until the filtering 1492 // handler returns itself as the designated handler 1493 BHandler* oldTarget = NULL; 1494 while (target && (target != oldTarget)) 1495 { 1496 oldTarget = target; 1497 target = apply_filters(oldTarget->FilterList(), msg, oldTarget); 1498 if (target && (target->Looper() != this)) 1499 { 1500 // TODO: debugger message? 1501 target = NULL; 1502 } 1503 } 1504 1505 return target; 1506 } 1507 //------------------------------------------------------------------------------ 1508 BHandler* BLooper::apply_filters(BList* list, BMessage* msg, BHandler* target) 1509 { 1510 // This is where the action is! 1511 // Check the parameters 1512 if (!list || !msg) 1513 { 1514 return target; 1515 } 1516 1517 // For each filter in the provided list 1518 BMessageFilter* filter = NULL; 1519 for (int32 i = 0; i < list->CountItems(); ++i) 1520 { 1521 filter = (BMessageFilter*)list->ItemAt(i); 1522 1523 // Check command conditions 1524 if (filter->FiltersAnyCommand() || (filter->Command() == msg->what)) 1525 { 1526 // Check delivery conditions 1527 message_delivery delivery = filter->MessageDelivery(); 1528 bool dropped = msg->WasDropped(); 1529 if (delivery == B_ANY_DELIVERY || 1530 ((delivery == B_DROPPED_DELIVERY) && dropped) || 1531 ((delivery == B_PROGRAMMED_DELIVERY) && !dropped)) 1532 { 1533 // Check source conditions 1534 message_source source = filter->MessageSource(); 1535 bool remote = msg->IsSourceRemote(); 1536 if (source == B_ANY_SOURCE || 1537 ((source == B_REMOTE_SOURCE) && remote) || 1538 ((source == B_LOCAL_SOURCE) && !remote)) 1539 { 1540 filter_result result; 1541 // Are we using an "external" function? 1542 filter_hook func = filter->FilterFunction(); 1543 if (func) 1544 { 1545 result = func(msg, &target, filter); 1546 } 1547 else 1548 { 1549 result = filter->Filter(msg, &target); 1550 } 1551 1552 // Is further processing allowed? 1553 if (result == B_SKIP_MESSAGE) 1554 { 1555 // No; time to bail out 1556 return NULL; 1557 } 1558 } 1559 } 1560 } 1561 } 1562 1563 return target; 1564 } 1565 //------------------------------------------------------------------------------ 1566 void BLooper::check_lock() 1567 { 1568 // NOTE: any use for this? 1569 } 1570 //------------------------------------------------------------------------------ 1571 BHandler* BLooper::resolve_specifier(BHandler* target, BMessage* msg) 1572 { 1573 // TODO: implement 1574 return NULL; 1575 } 1576 //------------------------------------------------------------------------------ 1577 void BLooper::UnlockFully() 1578 { 1579 AssertLocked(); 1580 1581 /** 1582 @note What we're doing here is completely undoing the current owner's lock 1583 on the looper. This is actually pretty easy, since the owner only 1584 has a single aquisition on the semaphore; every subsequent "lock" 1585 is just an increment to the owner count. The whole thing is quite 1586 similar to Unlock(), except that we clear the ownership variables, 1587 rather than merely decrementing them. 1588 */ 1589 // Clear the owner count 1590 fOwnerCount = 0; 1591 // Nobody owns the lock now 1592 fOwner = -1; 1593 // There is now one less thread holding a lock on this looper 1594 long atomicCount = atomic_add(&fAtomicCount, -1); 1595 if (atomicCount > 0) 1596 { 1597 release_sem(fLockSem); 1598 } 1599 } 1600 //------------------------------------------------------------------------------ 1601 void BLooper::AddLooper(BLooper* loop) 1602 { 1603 if (gLooperList.IsLocked()) 1604 { 1605 gLooperList.AddLooper(loop); 1606 } 1607 } 1608 //------------------------------------------------------------------------------ 1609 bool BLooper::IsLooperValid(const BLooper* l) 1610 { 1611 if (gLooperList.IsLocked()) 1612 { 1613 return gLooperList.IsLooperValid(l); 1614 } 1615 1616 return false; 1617 } 1618 //------------------------------------------------------------------------------ 1619 void BLooper::RemoveLooper(BLooper* l) 1620 { 1621 if (gLooperList.IsLocked()) 1622 { 1623 gLooperList.RemoveLooper(l); 1624 } 1625 } 1626 //------------------------------------------------------------------------------ 1627 void BLooper::GetLooperList(BList* list) 1628 { 1629 BObjectLocker<BLooperList> ListLock(gLooperList); 1630 if (ListLock.IsLocked()) 1631 { 1632 gLooperList.GetLooperList(list); 1633 } 1634 } 1635 //------------------------------------------------------------------------------ 1636 BLooper* BLooper::LooperForName(const char* name) 1637 { 1638 if (gLooperList.IsLocked()) 1639 { 1640 return gLooperList.LooperForName(name); 1641 } 1642 1643 return NULL; 1644 } 1645 //------------------------------------------------------------------------------ 1646 BLooper* BLooper::LooperForPort(port_id port) 1647 { 1648 if (gLooperList.IsLocked()) 1649 { 1650 return gLooperList.LooperForPort(port); 1651 } 1652 1653 return NULL; 1654 } 1655 //------------------------------------------------------------------------------ 1656 1657 1658 //------------------------------------------------------------------------------ 1659 port_id _get_looper_port_(const BLooper* looper) 1660 { 1661 return looper->fMsgPort; 1662 } 1663 //------------------------------------------------------------------------------ 1664 1665 /* 1666 * $Log $ 1667 * 1668 * $Id $ 1669 * 1670 */ 1671 1672