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(%lx)...\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 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 DBG(OUT("LOOPER: _task0_() done: thread %ld\n", find_thread(NULL))); 1195 return B_OK; 1196 } 1197 //------------------------------------------------------------------------------ 1198 void* BLooper::ReadRawFromPort(int32* msgcode, bigtime_t tout) 1199 { 1200 DBG(OUT("BLooper::ReadRawFromPort()\n")); 1201 int8* msgbuffer = NULL; 1202 ssize_t buffersize; 1203 ssize_t bytesread; 1204 1205 if (tout == B_INFINITE_TIMEOUT) 1206 { 1207 buffersize = port_buffer_size(fMsgPort); 1208 DBG(OUT("BLooper::ReadRawFromPort(): buffersize: %ld\n", buffersize)); 1209 } 1210 else 1211 { 1212 buffersize = port_buffer_size_etc(fMsgPort, 0, tout); 1213 if (buffersize == B_TIMED_OUT || buffersize == B_BAD_PORT_ID || 1214 buffersize == B_WOULD_BLOCK) 1215 { 1216 DBG(OUT("BLooper::ReadRawFromPort() done 1\n")); 1217 return NULL; 1218 } 1219 } 1220 1221 if (buffersize > 0) 1222 msgbuffer = new int8[buffersize]; 1223 1224 if (tout == B_INFINITE_TIMEOUT) 1225 { 1226 DBG(OUT("read_port()...\n")); 1227 bytesread = read_port(fMsgPort, msgcode, msgbuffer, buffersize); 1228 DBG(OUT("read_port() done: %ld\n", bytesread)); 1229 DBG(OUT("BLooper::ReadRawFromPort() read: %.4s\n", (char*)msgcode)); 1230 } 1231 else 1232 { 1233 bytesread = read_port_etc(fMsgPort, msgcode, msgbuffer, buffersize, 1234 B_TIMEOUT, tout); 1235 } 1236 1237 DBG(OUT("BLooper::ReadRawFromPort() done: %p\n", msgbuffer)); 1238 return msgbuffer; 1239 } 1240 //------------------------------------------------------------------------------ 1241 BMessage* BLooper::ReadMessageFromPort(bigtime_t tout) 1242 { 1243 DBG(OUT("BLooper::ReadMessageFromPort()\n")); 1244 int32 msgcode; 1245 BMessage* bmsg; 1246 1247 void* msgbuffer = ReadRawFromPort(&msgcode, tout); 1248 1249 bmsg = ConvertToMessage(msgbuffer, msgcode); 1250 1251 if (msgbuffer) 1252 { 1253 delete[] msgbuffer; 1254 } 1255 1256 DBG(OUT("BLooper::ReadMessageFromPort() done: %p\n", bmsg)); 1257 return bmsg; 1258 } 1259 //------------------------------------------------------------------------------ 1260 BMessage* BLooper::ConvertToMessage(void* raw, int32 code) 1261 { 1262 DBG(OUT("BLooper::ConvertToMessage()\n")); 1263 BMessage* bmsg = new BMessage(code); 1264 1265 if (raw != NULL) 1266 { 1267 if (bmsg->Unflatten((const char*)raw) != B_OK) 1268 { 1269 DBG(OUT("BLooper::ConvertToMessage(): unflattening message failed\n")); 1270 delete bmsg; 1271 bmsg = NULL; 1272 } 1273 } 1274 1275 DBG(OUT("BLooper::ConvertToMessage(): %p\n", bmsg)); 1276 return bmsg; 1277 } 1278 //------------------------------------------------------------------------------ 1279 void BLooper::task_looper() 1280 { 1281 DBG(OUT("BLooper::task_looper()\n")); 1282 // Check that looper is locked (should be) 1283 AssertLocked(); 1284 // Unlock the looper 1285 Unlock(); 1286 1287 // loop: As long as we are not terminating. 1288 while (!fTerminating) 1289 { 1290 DBG(OUT("LOOPER: outer loop\n")); 1291 // TODO: timeout determination algo 1292 // Read from message port (how do we determine what the timeout is?) 1293 DBG(OUT("LOOPER: MessageFromPort()...\n")); 1294 BMessage* msg = MessageFromPort(); 1295 DBG(OUT("LOOPER: ...done\n")); 1296 1297 // Did we get a message? 1298 if (msg) 1299 { 1300 DBG(OUT("LOOPER: got message\n")); 1301 // Add to queue 1302 fQueue->AddMessage(msg); 1303 } 1304 else 1305 DBG(OUT("LOOPER: got no message\n")); 1306 1307 // Get message count from port 1308 int32 msgCount = port_count(fMsgPort); 1309 for (int32 i = 0; i < msgCount; ++i) 1310 { 1311 // Read 'count' messages from port (so we will not block) 1312 // We use zero as our timeout since we know there is stuff there 1313 msg = MessageFromPort(0); 1314 // Add messages to queue 1315 if (msg) 1316 { 1317 fQueue->AddMessage(msg); 1318 } 1319 } 1320 1321 // loop: As long as there are messages in the queue and the port is 1322 // empty... and we are not terminating, of course. 1323 bool dispatchNextMessage = true; 1324 while (!fTerminating && dispatchNextMessage) 1325 { 1326 DBG(OUT("LOOPER: inner loop\n")); 1327 // Get next message from queue (assign to fLastMessage) 1328 fLastMessage = fQueue->NextMessage(); 1329 1330 // Lock the looper 1331 Lock(); 1332 if (!fLastMessage) 1333 { 1334 // No more messages: Unlock the looper and terminate the 1335 // dispatch loop. 1336 dispatchNextMessage = false; 1337 } 1338 else 1339 { 1340 DBG(OUT("LOOPER: fLastMessage: 0x%lx: %.4s\n", fLastMessage->what, 1341 (char*)&fLastMessage->what)); 1342 DBG(fLastMessage->PrintToStream()); 1343 // Get the target handler 1344 // Use BMessage friend functions to determine if we are using the 1345 // preferred handler, or if a target has been specified 1346 BHandler* handler; 1347 if (_use_preferred_target_(fLastMessage)) 1348 { 1349 DBG(OUT("LOOPER: use preferred target\n")); 1350 handler = fPreferred; 1351 } 1352 else 1353 { 1354 DBG(OUT("LOOPER: don't use preferred target\n")); 1355 /** 1356 @note Here is where all the token stuff starts to 1357 make sense. How, exactly, do we determine 1358 what the target BHandler is? If we look at 1359 BMessage, we see an int32 field, fTarget. 1360 Amazingly, we happen to have a global mapping 1361 of BHandler pointers to int32s! 1362 */ 1363 DBG(OUT("LOOPER: use: %ld\n", _get_message_target_(fLastMessage))); 1364 gDefaultTokens.GetToken(_get_message_target_(fLastMessage), 1365 B_HANDLER_TOKEN, 1366 (void**)&handler); 1367 DBG(OUT("LOOPER: handler: %p, this: %p\n", handler, this)); 1368 } 1369 1370 if (!handler) 1371 { 1372 DBG(OUT("LOOPER: no target handler, use this\n")); 1373 handler = this; 1374 } 1375 1376 // Is this a scripting message? (BMessage::HasSpecifiers()) 1377 if (fLastMessage->HasSpecifiers()) 1378 { 1379 int32 index = 0; 1380 // Make sure the current specifier is kosher 1381 if (fLastMessage->GetCurrentSpecifier(&index) == B_OK) 1382 { 1383 handler = resolve_specifier(handler, fLastMessage); 1384 } 1385 } 1386 else 1387 DBG(OUT("LOOPER: no scripting message\n")); 1388 1389 if (handler) 1390 { 1391 // Do filtering 1392 handler = top_level_filter(fLastMessage, handler); 1393 DBG(OUT("LOOPER: top_level_filter(): %p\n", handler)); 1394 if (handler && handler->Looper() == this) 1395 { 1396 DispatchMessage(fLastMessage, handler); 1397 } 1398 } 1399 } 1400 1401 // Unlock the looper 1402 Unlock(); 1403 1404 // Delete the current message (fLastMessage) 1405 if (fLastMessage) 1406 { 1407 delete fLastMessage; 1408 fLastMessage = NULL; 1409 } 1410 1411 // Are any messages on the port? 1412 if (port_count(fMsgPort) > 0) 1413 { 1414 // Do outer loop 1415 dispatchNextMessage = false; 1416 } 1417 } 1418 } 1419 DBG(OUT("BLooper::task_looper() done\n")); 1420 } 1421 //------------------------------------------------------------------------------ 1422 void BLooper::do_quit_requested(BMessage* msg) 1423 { 1424 /** 1425 @note I couldn't figure out why do_quit_requested() was necessary; why not 1426 just call Quit()? Then, while writing the PostMessage() code, I 1427 realized that the sender of the B_QUIT_REQUESTED message just might 1428 be waiting for a reply. A quick test, and yes, we get a reply 1429 which consists of: 1430 what: B_REPLY 1431 "result" (bool) return of QuitRequested() 1432 "thread" (int32) the looper's thread id 1433 1434 While Quit() could use fLastMessage, it makes more sense that 1435 do_quit_requested() would handle it since it gets passed the 1436 message. 1437 */ 1438 1439 bool isQuitting = QuitRequested(); 1440 1441 if (msg->IsSourceWaiting()) 1442 { 1443 BMessage ReplyMsg(B_REPLY); 1444 ReplyMsg.AddBool("result", isQuitting); 1445 ReplyMsg.AddInt32("thread", fTaskID); 1446 msg->SendReply(&ReplyMsg); 1447 } 1448 1449 if (isQuitting) 1450 { 1451 Quit(); 1452 } 1453 } 1454 //------------------------------------------------------------------------------ 1455 bool BLooper::AssertLocked() const 1456 { 1457 if (!IsLocked()) 1458 { 1459 debugger("looper must be locked before proceeding\n"); 1460 return false; 1461 } 1462 1463 return true; 1464 } 1465 //------------------------------------------------------------------------------ 1466 BHandler* BLooper::top_level_filter(BMessage* msg, BHandler* target) 1467 { 1468 if (msg) 1469 { 1470 // Apply the common filters first 1471 target = apply_filters(CommonFilterList(), msg, target); 1472 if (target) 1473 { 1474 if (target->Looper() != this) 1475 { 1476 // TODO: debugger message? 1477 target = NULL; 1478 } 1479 else 1480 { 1481 // Now apply handler-specific filters 1482 target = handler_only_filter(msg, target); 1483 } 1484 } 1485 } 1486 1487 return target; 1488 } 1489 //------------------------------------------------------------------------------ 1490 BHandler* BLooper::handler_only_filter(BMessage* msg, BHandler* target) 1491 { 1492 // Keep running filters until our handler is NULL, or until the filtering 1493 // handler returns itself as the designated handler 1494 BHandler* oldTarget = NULL; 1495 while (target && (target != oldTarget)) 1496 { 1497 oldTarget = target; 1498 target = apply_filters(oldTarget->FilterList(), msg, oldTarget); 1499 if (target && (target->Looper() != this)) 1500 { 1501 // TODO: debugger message? 1502 target = NULL; 1503 } 1504 } 1505 1506 return target; 1507 } 1508 //------------------------------------------------------------------------------ 1509 BHandler* BLooper::apply_filters(BList* list, BMessage* msg, BHandler* target) 1510 { 1511 // This is where the action is! 1512 // Check the parameters 1513 if (!list || !msg) 1514 { 1515 return target; 1516 } 1517 1518 // For each filter in the provided list 1519 BMessageFilter* filter = NULL; 1520 for (int32 i = 0; i < list->CountItems(); ++i) 1521 { 1522 filter = (BMessageFilter*)list->ItemAt(i); 1523 1524 // Check command conditions 1525 if (filter->FiltersAnyCommand() || (filter->Command() == msg->what)) 1526 { 1527 // Check delivery conditions 1528 message_delivery delivery = filter->MessageDelivery(); 1529 bool dropped = msg->WasDropped(); 1530 if (delivery == B_ANY_DELIVERY || 1531 ((delivery == B_DROPPED_DELIVERY) && dropped) || 1532 ((delivery == B_PROGRAMMED_DELIVERY) && !dropped)) 1533 { 1534 // Check source conditions 1535 message_source source = filter->MessageSource(); 1536 bool remote = msg->IsSourceRemote(); 1537 if (source == B_ANY_SOURCE || 1538 ((source == B_REMOTE_SOURCE) && remote) || 1539 ((source == B_LOCAL_SOURCE) && !remote)) 1540 { 1541 filter_result result; 1542 // Are we using an "external" function? 1543 filter_hook func = filter->FilterFunction(); 1544 if (func) 1545 { 1546 result = func(msg, &target, filter); 1547 } 1548 else 1549 { 1550 result = filter->Filter(msg, &target); 1551 } 1552 1553 // Is further processing allowed? 1554 if (result == B_SKIP_MESSAGE) 1555 { 1556 // No; time to bail out 1557 return NULL; 1558 } 1559 } 1560 } 1561 } 1562 } 1563 1564 return target; 1565 } 1566 //------------------------------------------------------------------------------ 1567 void BLooper::check_lock() 1568 { 1569 // NOTE: any use for this? 1570 } 1571 //------------------------------------------------------------------------------ 1572 BHandler* BLooper::resolve_specifier(BHandler* target, BMessage* msg) 1573 { 1574 // TODO: implement 1575 return NULL; 1576 } 1577 //------------------------------------------------------------------------------ 1578 void BLooper::UnlockFully() 1579 { 1580 AssertLocked(); 1581 1582 /** 1583 @note What we're doing here is completely undoing the current owner's lock 1584 on the looper. This is actually pretty easy, since the owner only 1585 has a single aquisition on the semaphore; every subsequent "lock" 1586 is just an increment to the owner count. The whole thing is quite 1587 similar to Unlock(), except that we clear the ownership variables, 1588 rather than merely decrementing them. 1589 */ 1590 // Clear the owner count 1591 fOwnerCount = 0; 1592 // Nobody owns the lock now 1593 fOwner = -1; 1594 // There is now one less thread holding a lock on this looper 1595 // bonefish: Currently _Lock() always acquires the semaphore. 1596 /* long atomicCount = */atomic_add(&fAtomicCount, -1); 1597 // if (atomicCount > 0) 1598 { 1599 release_sem(fLockSem); 1600 } 1601 } 1602 //------------------------------------------------------------------------------ 1603 void BLooper::AddLooper(BLooper* loop) 1604 { 1605 if (gLooperList.IsLocked()) 1606 { 1607 gLooperList.AddLooper(loop); 1608 } 1609 } 1610 //------------------------------------------------------------------------------ 1611 bool BLooper::IsLooperValid(const BLooper* l) 1612 { 1613 if (gLooperList.IsLocked()) 1614 { 1615 return gLooperList.IsLooperValid(l); 1616 } 1617 1618 return false; 1619 } 1620 //------------------------------------------------------------------------------ 1621 void BLooper::RemoveLooper(BLooper* l) 1622 { 1623 if (gLooperList.IsLocked()) 1624 { 1625 gLooperList.RemoveLooper(l); 1626 } 1627 } 1628 //------------------------------------------------------------------------------ 1629 void BLooper::GetLooperList(BList* list) 1630 { 1631 BObjectLocker<BLooperList> ListLock(gLooperList); 1632 if (ListLock.IsLocked()) 1633 { 1634 gLooperList.GetLooperList(list); 1635 } 1636 } 1637 //------------------------------------------------------------------------------ 1638 BLooper* BLooper::LooperForName(const char* name) 1639 { 1640 if (gLooperList.IsLocked()) 1641 { 1642 return gLooperList.LooperForName(name); 1643 } 1644 1645 return NULL; 1646 } 1647 //------------------------------------------------------------------------------ 1648 BLooper* BLooper::LooperForPort(port_id port) 1649 { 1650 if (gLooperList.IsLocked()) 1651 { 1652 return gLooperList.LooperForPort(port); 1653 } 1654 1655 return NULL; 1656 } 1657 //------------------------------------------------------------------------------ 1658 1659 1660 //------------------------------------------------------------------------------ 1661 port_id _get_looper_port_(const BLooper* looper) 1662 { 1663 return looper->fMsgPort; 1664 } 1665 //------------------------------------------------------------------------------ 1666 1667 /* 1668 * $Log $ 1669 * 1670 * $Id $ 1671 * 1672 */ 1673 1674