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