1 /* 2 * Copyright 2001-2008, Haiku. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * Erik Jaesler (erik@cgsoftware.com) 7 * DarkWyrm (bpmagic@columbus.rr.com) 8 * Ingo Weinhold, bonefish@@users.sf.net 9 * Axel Dörfler, axeld@pinc-software.de 10 */ 11 12 /*! BLooper class spawns a thread that runs a message loop. */ 13 14 #include <AppMisc.h> 15 #include <AutoLocker.h> 16 #include <DirectMessageTarget.h> 17 #include <LooperList.h> 18 #include <MessagePrivate.h> 19 #include <TokenSpace.h> 20 21 #include <Autolock.h> 22 #include <Looper.h> 23 #include <Message.h> 24 #include <MessageFilter.h> 25 #include <MessageQueue.h> 26 #include <Messenger.h> 27 #include <PropertyInfo.h> 28 29 #include <new> 30 #include <stdio.h> 31 32 33 // debugging 34 //#define DBG(x) x 35 #define DBG(x) ; 36 #define PRINT(x) DBG({ printf("[%6ld] ", find_thread(NULL)); printf x; }) 37 38 /* 39 #include <Autolock.h> 40 #include <Locker.h> 41 static BLocker sDebugPrintLocker("BLooper debug print"); 42 #define PRINT(x) DBG({ \ 43 BAutolock _(sDebugPrintLocker); \ 44 debug_printf("[%6ld] ", find_thread(NULL)); \ 45 debug_printf x; \ 46 }) 47 */ 48 49 50 #define FILTER_LIST_BLOCK_SIZE 5 51 #define DATA_BLOCK_SIZE 5 52 53 // Globals --------------------------------------------------------------------- 54 using BPrivate::gDefaultTokens; 55 using BPrivate::gLooperList; 56 using BPrivate::BLooperList; 57 58 port_id _get_looper_port_(const BLooper* looper); 59 60 enum { 61 BLOOPER_PROCESS_INTERNALLY = 0, 62 BLOOPER_HANDLER_BY_INDEX 63 }; 64 65 static property_info gLooperPropInfo[] = { 66 { 67 "Handler", 68 {}, 69 {B_INDEX_SPECIFIER, B_REVERSE_INDEX_SPECIFIER}, 70 NULL, BLOOPER_HANDLER_BY_INDEX, 71 {}, 72 {}, 73 {} 74 }, 75 { 76 "Handlers", 77 {B_GET_PROPERTY}, 78 {B_DIRECT_SPECIFIER}, 79 NULL, BLOOPER_PROCESS_INTERNALLY, 80 {B_MESSENGER_TYPE}, 81 {}, 82 {} 83 }, 84 { 85 "Handler", 86 {B_COUNT_PROPERTIES}, 87 {B_DIRECT_SPECIFIER}, 88 NULL, BLOOPER_PROCESS_INTERNALLY, 89 {B_INT32_TYPE}, 90 {}, 91 {} 92 }, 93 {} 94 }; 95 96 struct _loop_data_ { 97 BLooper* looper; 98 thread_id thread; 99 }; 100 101 102 // #pragma mark - 103 104 105 BLooper::BLooper(const char* name, int32 priority, int32 portCapacity) 106 : BHandler(name) 107 { 108 _InitData(name, priority, portCapacity); 109 } 110 111 112 BLooper::~BLooper() 113 { 114 if (fRunCalled && !fTerminating) { 115 debugger("You can't call delete on a BLooper object " 116 "once it is running."); 117 } 118 119 Lock(); 120 121 // In case the looper thread calls Quit() fLastMessage is not deleted. 122 if (fLastMessage) { 123 delete fLastMessage; 124 fLastMessage = NULL; 125 } 126 127 // Close the message port and read and reply to the remaining messages. 128 if (fMsgPort >= 0) 129 close_port(fMsgPort); 130 131 // Clear the queue so our call to IsMessageWaiting() below doesn't give 132 // us bogus info 133 fDirectTarget->Close(); 134 135 BMessage* message; 136 while ((message = fDirectTarget->Queue()->NextMessage()) != NULL) { 137 delete message; 138 // msg will automagically post generic reply 139 } 140 141 do { 142 delete ReadMessageFromPort(0); 143 // msg will automagically post generic reply 144 } while (IsMessageWaiting()); 145 146 fDirectTarget->Release(); 147 delete_port(fMsgPort); 148 149 // Clean up our filters 150 SetCommonFilterList(NULL); 151 152 AutoLocker<BLooperList> ListLock(gLooperList); 153 RemoveHandler(this); 154 155 // Remove all the "child" handlers 156 BHandler* child; 157 while (CountHandlers()) { 158 child = HandlerAt(0); 159 if (child) 160 RemoveHandler(child); 161 } 162 163 Unlock(); 164 gLooperList.RemoveLooper(this); 165 delete_sem(fLockSem); 166 } 167 168 169 BLooper::BLooper(BMessage* data) 170 : BHandler(data) 171 { 172 int32 portCapacity; 173 if (data->FindInt32("_port_cap", &portCapacity) != B_OK || portCapacity < 0) 174 portCapacity = B_LOOPER_PORT_DEFAULT_CAPACITY; 175 176 _InitData(Name(), B_NORMAL_PRIORITY, portCapacity); 177 } 178 179 180 BArchivable* 181 BLooper::Instantiate(BMessage* data) 182 { 183 if (validate_instantiation(data, "BLooper")) 184 return new BLooper(data); 185 186 return NULL; 187 } 188 189 190 status_t 191 BLooper::Archive(BMessage* data, bool deep) const 192 { 193 status_t status = BHandler::Archive(data, deep); 194 if (status < B_OK) 195 return status; 196 197 port_info info; 198 status = get_port_info(fMsgPort, &info); 199 if (status == B_OK) 200 status = data->AddInt32("_port_cap", info.capacity); 201 202 // TODO: what about the thread priority? 203 204 return status; 205 } 206 207 208 status_t 209 BLooper::PostMessage(uint32 command) 210 { 211 BMessage message(command); 212 return _PostMessage(&message, this, NULL); 213 } 214 215 216 status_t 217 BLooper::PostMessage(BMessage* message) 218 { 219 return _PostMessage(message, this, NULL); 220 } 221 222 223 status_t 224 BLooper::PostMessage(uint32 command, BHandler* handler, BHandler* replyTo) 225 { 226 BMessage message(command); 227 return _PostMessage(&message, handler, replyTo); 228 } 229 230 231 status_t 232 BLooper::PostMessage(BMessage* message, BHandler* handler, BHandler* replyTo) 233 { 234 return _PostMessage(message, handler, replyTo); 235 } 236 237 238 void 239 BLooper::DispatchMessage(BMessage* message, BHandler* handler) 240 { 241 PRINT(("BLooper::DispatchMessage(%.4s)\n", (char*)&message->what)); 242 243 switch (message->what) { 244 case _QUIT_: 245 // Can't call Quit() to do this, because of the slight chance 246 // another thread with have us locked between now and then. 247 fTerminating = true; 248 249 // After returning from DispatchMessage(), the looper will be 250 // deleted in _task0_() 251 break; 252 253 case B_QUIT_REQUESTED: 254 if (handler == this) { 255 _QuitRequested(message); 256 break; 257 } 258 259 // fall through 260 261 default: 262 handler->MessageReceived(message); 263 break; 264 } 265 PRINT(("BLooper::DispatchMessage() done\n")); 266 } 267 268 269 void 270 BLooper::MessageReceived(BMessage* message) 271 { 272 // TODO: implement scripting support 273 BHandler::MessageReceived(message); 274 } 275 276 277 BMessage* 278 BLooper::CurrentMessage() const 279 { 280 return fLastMessage; 281 } 282 283 284 BMessage* 285 BLooper::DetachCurrentMessage() 286 { 287 BMessage* message = fLastMessage; 288 fLastMessage = NULL; 289 return message; 290 } 291 292 293 BMessageQueue* 294 BLooper::MessageQueue() const 295 { 296 return fDirectTarget->Queue(); 297 } 298 299 300 bool 301 BLooper::IsMessageWaiting() const 302 { 303 AssertLocked(); 304 305 if (!fDirectTarget->Queue()->IsEmpty()) 306 return true; 307 308 int32 count; 309 do { 310 count = port_buffer_size_etc(fMsgPort, B_RELATIVE_TIMEOUT, 0); 311 } while (count == B_INTERRUPTED); 312 313 return count > 0; 314 } 315 316 317 void 318 BLooper::AddHandler(BHandler* handler) 319 { 320 if (handler == NULL) 321 return; 322 323 AssertLocked(); 324 325 if (handler->Looper() == NULL) { 326 fHandlers.AddItem(handler); 327 handler->SetLooper(this); 328 if (handler != this) // avoid a cycle 329 handler->SetNextHandler(this); 330 } 331 } 332 333 334 bool 335 BLooper::RemoveHandler(BHandler* handler) 336 { 337 if (handler == NULL) 338 return false; 339 340 AssertLocked(); 341 342 if (handler->Looper() == this && fHandlers.RemoveItem(handler)) { 343 if (handler == fPreferred) 344 fPreferred = NULL; 345 346 handler->SetNextHandler(NULL); 347 handler->SetLooper(NULL); 348 return true; 349 } 350 351 return false; 352 } 353 354 355 int32 356 BLooper::CountHandlers() const 357 { 358 AssertLocked(); 359 360 return fHandlers.CountItems(); 361 } 362 363 364 BHandler* 365 BLooper::HandlerAt(int32 index) const 366 { 367 AssertLocked(); 368 369 return (BHandler*)fHandlers.ItemAt(index); 370 } 371 372 373 int32 374 BLooper::IndexOf(BHandler* handler) const 375 { 376 AssertLocked(); 377 378 return fHandlers.IndexOf(handler); 379 } 380 381 382 BHandler* 383 BLooper::PreferredHandler() const 384 { 385 return fPreferred; 386 } 387 388 389 void 390 BLooper::SetPreferredHandler(BHandler* handler) 391 { 392 if (handler && handler->Looper() == this && IndexOf(handler) >= 0) { 393 fPreferred = handler; 394 } else { 395 fPreferred = NULL; 396 } 397 } 398 399 400 thread_id 401 BLooper::Run() 402 { 403 AssertLocked(); 404 405 if (fRunCalled) { 406 // Not allowed to call Run() more than once 407 debugger("can't call BLooper::Run twice!"); 408 return fThread; 409 } 410 411 fThread = spawn_thread(_task0_, Name(), fInitPriority, this); 412 if (fThread < B_OK) 413 return fThread; 414 415 if (fMsgPort < B_OK) 416 return fMsgPort; 417 418 fRunCalled = true; 419 Unlock(); 420 421 status_t err = resume_thread(fThread); 422 if (err < B_OK) 423 return err; 424 425 return fThread; 426 } 427 428 429 void 430 BLooper::Quit() 431 { 432 PRINT(("BLooper::Quit()\n")); 433 434 if (!IsLocked()) { 435 printf("ERROR - you must Lock a looper before calling Quit(), " 436 "team=%ld, looper=%s\n", Team(), Name() ? Name() : "unnamed"); 437 } 438 439 // Try to lock 440 if (!Lock()) { 441 // We're toast already 442 return; 443 } 444 445 PRINT((" is locked\n")); 446 447 if (!fRunCalled) { 448 PRINT((" Run() has not been called yet\n")); 449 fTerminating = true; 450 delete this; 451 } else if (find_thread(NULL) == fThread) { 452 PRINT((" We are the looper thread\n")); 453 fTerminating = true; 454 delete this; 455 exit_thread(0); 456 } else { 457 PRINT((" Run() has already been called and we are not the looper thread\n")); 458 459 // As with sem in _Lock(), we need to cache this here in case the looper 460 // disappears before we get to the wait_for_thread() below 461 thread_id thread = Thread(); 462 463 // We need to unlock here. Otherwise the looper thread can't 464 // dispatch the _QUIT_ message we're going to post. 465 UnlockFully(); 466 467 // As per the BeBook, if we've been called by a thread other than 468 // our own, the rest of the message queue has to get processed. So 469 // we put this in the queue, and when it shows up, we'll call Quit() 470 // from our own thread. 471 // QuitRequested() will not be called in this case. 472 PostMessage(_QUIT_); 473 474 // We have to wait until the looper is done processing any remaining 475 // messages. 476 status_t status; 477 while (wait_for_thread(thread, &status) == B_INTERRUPTED) 478 ; 479 } 480 481 PRINT(("BLooper::Quit() done\n")); 482 } 483 484 485 bool 486 BLooper::QuitRequested() 487 { 488 return true; 489 } 490 491 492 bool 493 BLooper::Lock() 494 { 495 // Defer to global _Lock(); see notes there 496 return _Lock(this, -1, B_INFINITE_TIMEOUT) == B_OK; 497 } 498 499 500 void 501 BLooper::Unlock() 502 { 503 PRINT(("BLooper::Unlock()\n")); 504 // Make sure we're locked to begin with 505 AssertLocked(); 506 507 // Decrement fOwnerCount 508 --fOwnerCount; 509 PRINT((" fOwnerCount now: %ld\n", fOwnerCount)); 510 // Check to see if the owner still wants a lock 511 if (fOwnerCount == 0) { 512 // Set fOwner to invalid thread_id (< 0) 513 fOwner = -1; 514 fCachedStack = 0; 515 516 #if DEBUG < 1 517 // Decrement requested lock count (using fAtomicCount for this) 518 int32 atomicCount = atomic_add(&fAtomicCount, -1); 519 PRINT((" fAtomicCount now: %ld\n", fAtomicCount)); 520 521 // Check if anyone is waiting for a lock 522 // and release if it's the case 523 if (atomicCount > 1) 524 #endif 525 release_sem(fLockSem); 526 } 527 PRINT(("BLooper::Unlock() done\n")); 528 } 529 530 531 bool 532 BLooper::IsLocked() const 533 { 534 if (!gLooperList.IsLooperValid(this)) { 535 // The looper is gone, so of course it's not locked 536 return false; 537 } 538 539 uint32 stack; 540 return ((uint32)&stack & ~(B_PAGE_SIZE - 1)) == fCachedStack 541 || find_thread(NULL) == fOwner; 542 } 543 544 545 status_t 546 BLooper::LockWithTimeout(bigtime_t timeout) 547 { 548 return _Lock(this, -1, timeout); 549 } 550 551 552 thread_id 553 BLooper::Thread() const 554 { 555 return fThread; 556 } 557 558 559 team_id 560 BLooper::Team() const 561 { 562 return BPrivate::current_team(); 563 } 564 565 566 BLooper* 567 BLooper::LooperForThread(thread_id thread) 568 { 569 return gLooperList.LooperForThread(thread); 570 } 571 572 573 thread_id 574 BLooper::LockingThread() const 575 { 576 return fOwner; 577 } 578 579 580 int32 581 BLooper::CountLocks() const 582 { 583 return fOwnerCount; 584 } 585 586 587 int32 588 BLooper::CountLockRequests() const 589 { 590 return fAtomicCount; 591 } 592 593 594 sem_id 595 BLooper::Sem() const 596 { 597 return fLockSem; 598 } 599 600 601 BHandler* 602 BLooper::ResolveSpecifier(BMessage* msg, int32 index, BMessage* specifier, 603 int32 form, const char* property) 604 { 605 /** 606 @note When I was first dumping the results of GetSupportedSuites() from 607 various classes, the use of the extra_data field was quite 608 mysterious to me. Then I dumped BApplication and compared the 609 result against the BeBook's docs for scripting BApplication. A 610 bunch of it isn't documented, but what is tipped me to the idea 611 that the extra_data is being used as a quick and dirty way to tell 612 what scripting "command" has been sent, e.g., for easy use in a 613 switch statement. Would certainly be a lot faster than a bunch of 614 string comparisons -- which wouldn't tell the whole story anyway, 615 because of the same name being used for multiple properties. 616 */ 617 BPropertyInfo propertyInfo(gLooperPropInfo); 618 uint32 data; 619 status_t err = B_OK; 620 const char* errMsg = ""; 621 if (propertyInfo.FindMatch(msg, index, specifier, form, property, &data) 622 >= 0) { 623 switch (data) { 624 case BLOOPER_PROCESS_INTERNALLY: 625 return this; 626 627 case BLOOPER_HANDLER_BY_INDEX: 628 { 629 int32 index = specifier->FindInt32("index"); 630 if (form == B_REVERSE_INDEX_SPECIFIER) { 631 index = CountHandlers() - index; 632 } 633 BHandler* target = HandlerAt(index); 634 if (target) { 635 // Specifier has been fully handled 636 msg->PopSpecifier(); 637 return target; 638 } else { 639 err = B_BAD_INDEX; 640 errMsg = "handler index out of range"; 641 } 642 break; 643 } 644 645 default: 646 err = B_BAD_SCRIPT_SYNTAX; 647 errMsg = "Didn't understand the specifier(s)"; 648 break; 649 } 650 } else { 651 return BHandler::ResolveSpecifier(msg, index, specifier, form, 652 property); 653 } 654 655 BMessage reply(B_MESSAGE_NOT_UNDERSTOOD); 656 reply.AddInt32("error", err); 657 reply.AddString("message", errMsg); 658 msg->SendReply(&reply); 659 660 return NULL; 661 } 662 663 664 status_t 665 BLooper::GetSupportedSuites(BMessage* data) 666 { 667 if (data == NULL) 668 return B_BAD_VALUE; 669 670 status_t status = data->AddString("suites", "suite/vnd.Be-looper"); 671 if (status == B_OK) { 672 BPropertyInfo PropertyInfo(gLooperPropInfo); 673 status = data->AddFlat("messages", &PropertyInfo); 674 if (status == B_OK) 675 status = BHandler::GetSupportedSuites(data); 676 } 677 678 return status; 679 } 680 681 682 void 683 BLooper::AddCommonFilter(BMessageFilter* filter) 684 { 685 if (!filter) 686 return; 687 688 AssertLocked(); 689 690 if (filter->Looper()) { 691 debugger("A MessageFilter can only be used once."); 692 return; 693 } 694 695 if (!fCommonFilters) 696 fCommonFilters = new BList(FILTER_LIST_BLOCK_SIZE); 697 698 filter->SetLooper(this); 699 fCommonFilters->AddItem(filter); 700 } 701 702 703 bool 704 BLooper::RemoveCommonFilter(BMessageFilter* filter) 705 { 706 AssertLocked(); 707 708 if (!fCommonFilters) 709 return false; 710 711 bool result = fCommonFilters->RemoveItem(filter); 712 if (result) 713 filter->SetLooper(NULL); 714 715 return result; 716 } 717 718 719 void 720 BLooper::SetCommonFilterList(BList* filters) 721 { 722 AssertLocked(); 723 724 BMessageFilter* filter; 725 if (filters) { 726 // Check for ownership issues - a filter can only have one owner 727 for (int32 i = 0; i < filters->CountItems(); ++i) { 728 filter = (BMessageFilter*)filters->ItemAt(i); 729 if (filter->Looper()) { 730 debugger("A MessageFilter can only be used once."); 731 return; 732 } 733 } 734 } 735 736 if (fCommonFilters) { 737 for (int32 i = 0; i < fCommonFilters->CountItems(); ++i) { 738 delete (BMessageFilter*)fCommonFilters->ItemAt(i); 739 } 740 741 delete fCommonFilters; 742 fCommonFilters = NULL; 743 } 744 745 // Per the BeBook, we take ownership of the list 746 fCommonFilters = filters; 747 if (fCommonFilters) { 748 for (int32 i = 0; i < fCommonFilters->CountItems(); ++i) { 749 filter = (BMessageFilter*)fCommonFilters->ItemAt(i); 750 filter->SetLooper(this); 751 } 752 } 753 } 754 755 756 BList* 757 BLooper::CommonFilterList() const 758 { 759 return fCommonFilters; 760 } 761 762 763 status_t 764 BLooper::Perform(perform_code d, void* arg) 765 { 766 // This is sort of what we're doing for this function everywhere 767 return BHandler::Perform(d, arg); 768 } 769 770 771 BMessage* 772 BLooper::MessageFromPort(bigtime_t timeout) 773 { 774 return ReadMessageFromPort(timeout); 775 } 776 777 778 void BLooper::_ReservedLooper1() {} 779 void BLooper::_ReservedLooper2() {} 780 void BLooper::_ReservedLooper3() {} 781 void BLooper::_ReservedLooper4() {} 782 void BLooper::_ReservedLooper5() {} 783 void BLooper::_ReservedLooper6() {} 784 785 786 BLooper::BLooper(const BLooper& other) 787 { 788 // Copy construction not allowed 789 } 790 791 792 BLooper& 793 BLooper::operator=(const BLooper& other) 794 { 795 // Looper copying not allowed 796 return *this; 797 } 798 799 800 BLooper::BLooper(int32 priority, port_id port, const char* name) 801 { 802 // This must be a legacy constructor 803 fMsgPort = port; 804 _InitData(name, priority, B_LOOPER_PORT_DEFAULT_CAPACITY); 805 } 806 807 808 status_t 809 BLooper::_PostMessage(BMessage *msg, BHandler *handler, BHandler *replyTo) 810 { 811 AutoLocker<BLooperList> listLocker(gLooperList); 812 if (!listLocker.IsLocked()) 813 return B_ERROR; 814 815 if (!gLooperList.IsLooperValid(this)) 816 return B_BAD_VALUE; 817 818 // Does handler belong to this looper? 819 if (handler && handler->Looper() != this) 820 return B_MISMATCHED_VALUES; 821 822 status_t status; 823 BMessenger messenger(handler, this, &status); 824 listLocker.Unlock(); 825 if (status == B_OK) 826 status = messenger.SendMessage(msg, replyTo, 0); 827 828 return status; 829 } 830 831 832 /*! 833 Locks a looper either by port or using a direct pointer to the looper. 834 835 \param looper looper to lock, if not NULL 836 \param port port to identify the looper in case \a looper is NULL 837 \param timeout timeout for acquiring the lock 838 */ 839 status_t 840 BLooper::_Lock(BLooper* looper, port_id port, bigtime_t timeout) 841 { 842 PRINT(("BLooper::_Lock(%p, %lx)\n", looper, port)); 843 844 // Check params (loop, port) 845 if (looper == NULL && port < 0) { 846 PRINT(("BLooper::_Lock() done 1\n")); 847 return B_BAD_VALUE; 848 } 849 850 thread_id currentThread = find_thread(NULL); 851 int32 oldCount; 852 sem_id sem; 853 854 { 855 AutoLocker<BLooperList> ListLock(gLooperList); 856 if (!ListLock.IsLocked()) 857 return B_BAD_VALUE; 858 859 // Look up looper by port_id, if necessary 860 if (looper == NULL) { 861 looper = gLooperList.LooperForPort(port); 862 if (looper == NULL) { 863 PRINT(("BLooper::_Lock() done 3\n")); 864 return B_BAD_VALUE; 865 } 866 } else if (!gLooperList.IsLooperValid(looper)) { 867 // Check looper validity 868 PRINT(("BLooper::_Lock() done 4\n")); 869 return B_BAD_VALUE; 870 } 871 872 // Check for nested lock attempt 873 if (currentThread == looper->fOwner) { 874 ++looper->fOwnerCount; 875 PRINT(("BLooper::_Lock() done 5: fOwnerCount: %ld\n", loop->fOwnerCount)); 876 return B_OK; 877 } 878 879 // Cache the semaphore, so that we can safely access it after having 880 // unlocked the looper list 881 sem = looper->fLockSem; 882 if (sem < 0) { 883 PRINT(("BLooper::_Lock() done 6\n")); 884 return B_BAD_VALUE; 885 } 886 887 // Bump the requested lock count (using fAtomicCount for this) 888 oldCount = atomic_add(&looper->fAtomicCount, 1); 889 } 890 891 return _LockComplete(looper, oldCount, currentThread, sem, timeout); 892 } 893 894 895 status_t 896 BLooper::_LockComplete(BLooper *looper, int32 oldCount, thread_id thread, 897 sem_id sem, bigtime_t timeout) 898 { 899 status_t err = B_OK; 900 901 #if DEBUG < 1 902 if (oldCount > 0) { 903 #endif 904 do { 905 err = acquire_sem_etc(sem, 1, B_RELATIVE_TIMEOUT, timeout); 906 } while (err == B_INTERRUPTED); 907 #if DEBUG < 1 908 } 909 #endif 910 if (err == B_OK) { 911 looper->fOwner = thread; 912 looper->fCachedStack = (addr_t)&err & ~(B_PAGE_SIZE - 1); 913 looper->fOwnerCount = 1; 914 } 915 916 PRINT(("BLooper::_LockComplete() done: %lx\n", err)); 917 return err; 918 } 919 920 921 void 922 BLooper::_InitData(const char *name, int32 priority, int32 portCapacity) 923 { 924 fOwner = B_ERROR; 925 fCachedStack = 0; 926 fRunCalled = false; 927 fDirectTarget = new (std::nothrow) BPrivate::BDirectMessageTarget(); 928 fCommonFilters = NULL; 929 fLastMessage = NULL; 930 fPreferred = NULL; 931 fThread = B_ERROR; 932 fTerminating = false; 933 fMsgPort = -1; 934 fAtomicCount = 0; 935 936 if (name == NULL) 937 name = "anonymous looper"; 938 939 #if DEBUG 940 fLockSem = create_sem(1, name); 941 #else 942 fLockSem = create_sem(0, name); 943 #endif 944 945 if (portCapacity <= 0) 946 portCapacity = B_LOOPER_PORT_DEFAULT_CAPACITY; 947 948 fMsgPort = create_port(portCapacity, name); 949 950 fInitPriority = priority; 951 952 gLooperList.AddLooper(this); 953 // this will also lock this looper 954 955 AddHandler(this); 956 } 957 958 959 void 960 BLooper::AddMessage(BMessage* message) 961 { 962 _AddMessagePriv(message); 963 964 // wakeup looper when being called from other threads if necessary 965 if (find_thread(NULL) != Thread() 966 && fDirectTarget->Queue()->IsNextMessage(message) 967 && port_count(fMsgPort) <= 0) { 968 // there is currently no message waiting, and we need to wakeup the 969 // looper 970 write_port_etc(fMsgPort, 0, NULL, 0, B_RELATIVE_TIMEOUT, 0); 971 } 972 } 973 974 975 void 976 BLooper::_AddMessagePriv(BMessage* message) 977 { 978 // ToDo: if no target token is specified, set to preferred handler 979 // Others may want to peek into our message queue, so the preferred 980 // handler must be set correctly already if no token was given 981 982 fDirectTarget->Queue()->AddMessage(message); 983 } 984 985 986 status_t 987 BLooper::_task0_(void* arg) 988 { 989 BLooper* looper = (BLooper *)arg; 990 991 PRINT(("LOOPER: _task0_()\n")); 992 993 if (looper->Lock()) { 994 PRINT(("LOOPER: looper locked\n")); 995 looper->task_looper(); 996 997 delete looper; 998 } 999 1000 PRINT(("LOOPER: _task0_() done: thread %ld\n", find_thread(NULL))); 1001 return B_OK; 1002 } 1003 1004 1005 void * 1006 BLooper::ReadRawFromPort(int32* msgCode, bigtime_t timeout) 1007 { 1008 PRINT(("BLooper::ReadRawFromPort()\n")); 1009 uint8 *buffer = NULL; 1010 ssize_t bufferSize; 1011 1012 do { 1013 bufferSize = port_buffer_size_etc(fMsgPort, B_RELATIVE_TIMEOUT, timeout); 1014 } while (bufferSize == B_INTERRUPTED); 1015 1016 if (bufferSize < B_OK) { 1017 PRINT(("BLooper::ReadRawFromPort(): failed: %ld\n", bufferSize)); 1018 return NULL; 1019 } 1020 1021 if (bufferSize > 0) 1022 buffer = (uint8 *)malloc(bufferSize); 1023 1024 // we don't want to wait again here, since that can only mean 1025 // that someone else has read our message and our bufferSize 1026 // is now probably wrong 1027 PRINT(("read_port()...\n")); 1028 bufferSize = read_port_etc(fMsgPort, msgCode, buffer, bufferSize, 1029 B_RELATIVE_TIMEOUT, 0); 1030 1031 if (bufferSize < B_OK) { 1032 free(buffer); 1033 return NULL; 1034 } 1035 1036 PRINT(("BLooper::ReadRawFromPort() read: %.4s, %p (%d bytes)\n", (char *)msgCode, buffer, bufferSize)); 1037 return buffer; 1038 } 1039 1040 1041 BMessage* 1042 BLooper::ReadMessageFromPort(bigtime_t timeout) 1043 { 1044 PRINT(("BLooper::ReadMessageFromPort()\n")); 1045 int32 msgCode; 1046 BMessage *message = NULL; 1047 1048 void *buffer = ReadRawFromPort(&msgCode, timeout); 1049 if (!buffer) 1050 return NULL; 1051 1052 message = ConvertToMessage(buffer, msgCode); 1053 free(buffer); 1054 1055 PRINT(("BLooper::ReadMessageFromPort() done: %p\n", message)); 1056 return message; 1057 } 1058 1059 1060 BMessage* 1061 BLooper::ConvertToMessage(void* buffer, int32 code) 1062 { 1063 PRINT(("BLooper::ConvertToMessage()\n")); 1064 if (!buffer) 1065 return NULL; 1066 1067 BMessage* message = new BMessage(); 1068 if (message->Unflatten((const char*)buffer) != B_OK) { 1069 PRINT(("BLooper::ConvertToMessage(): unflattening message failed\n")); 1070 delete message; 1071 message = NULL; 1072 } 1073 1074 PRINT(("BLooper::ConvertToMessage(): %p\n", message)); 1075 return message; 1076 } 1077 1078 1079 void 1080 BLooper::task_looper() 1081 { 1082 PRINT(("BLooper::task_looper()\n")); 1083 // Check that looper is locked (should be) 1084 AssertLocked(); 1085 // Unlock the looper 1086 Unlock(); 1087 1088 if (IsLocked()) 1089 debugger("looper must not be locked!"); 1090 1091 // loop: As long as we are not terminating. 1092 while (!fTerminating) { 1093 PRINT(("LOOPER: outer loop\n")); 1094 // TODO: timeout determination algo 1095 // Read from message port (how do we determine what the timeout is?) 1096 PRINT(("LOOPER: MessageFromPort()...\n")); 1097 BMessage *msg = MessageFromPort(); 1098 PRINT(("LOOPER: ...done\n")); 1099 1100 // Did we get a message? 1101 if (msg) 1102 _AddMessagePriv(msg); 1103 1104 // Get message count from port 1105 int32 msgCount = port_count(fMsgPort); 1106 for (int32 i = 0; i < msgCount; ++i) { 1107 // Read 'count' messages from port (so we will not block) 1108 // We use zero as our timeout since we know there is stuff there 1109 msg = MessageFromPort(0); 1110 if (msg) 1111 _AddMessagePriv(msg); 1112 } 1113 1114 // loop: As long as there are messages in the queue and the port is 1115 // empty... and we are not terminating, of course. 1116 bool dispatchNextMessage = true; 1117 while (!fTerminating && dispatchNextMessage) { 1118 PRINT(("LOOPER: inner loop\n")); 1119 // Get next message from queue (assign to fLastMessage) 1120 fLastMessage = fDirectTarget->Queue()->NextMessage(); 1121 1122 Lock(); 1123 1124 if (!fLastMessage) { 1125 // No more messages: Unlock the looper and terminate the 1126 // dispatch loop. 1127 dispatchNextMessage = false; 1128 } else { 1129 PRINT(("LOOPER: fLastMessage: 0x%lx: %.4s\n", fLastMessage->what, 1130 (char*)&fLastMessage->what)); 1131 DBG(fLastMessage->PrintToStream()); 1132 1133 // Get the target handler 1134 BHandler *handler = NULL; 1135 BMessage::Private messagePrivate(fLastMessage); 1136 bool usePreferred = messagePrivate.UsePreferredTarget(); 1137 1138 if (usePreferred) { 1139 PRINT(("LOOPER: use preferred target\n")); 1140 handler = fPreferred; 1141 if (handler == NULL) 1142 handler = this; 1143 } else { 1144 gDefaultTokens.GetToken(messagePrivate.GetTarget(), 1145 B_HANDLER_TOKEN, (void **)&handler); 1146 1147 // if this handler doesn't belong to us, we drop the message 1148 if (handler != NULL && handler->Looper() != this) 1149 handler = NULL; 1150 1151 PRINT(("LOOPER: use %ld, handler: %p, this: %p\n", 1152 messagePrivate.GetTarget(), handler, this)); 1153 } 1154 1155 // Is this a scripting message? (BMessage::HasSpecifiers()) 1156 if (handler != NULL && fLastMessage->HasSpecifiers()) { 1157 int32 index = 0; 1158 // Make sure the current specifier is kosher 1159 if (fLastMessage->GetCurrentSpecifier(&index) == B_OK) 1160 handler = resolve_specifier(handler, fLastMessage); 1161 } 1162 1163 if (handler) { 1164 // Do filtering 1165 handler = _TopLevelFilter(fLastMessage, handler); 1166 PRINT(("LOOPER: _TopLevelFilter(): %p\n", handler)); 1167 if (handler && handler->Looper() == this) 1168 DispatchMessage(fLastMessage, handler); 1169 } 1170 } 1171 1172 if (fTerminating) { 1173 // we leave the looper locked when we quit 1174 return; 1175 } 1176 1177 // Unlock the looper 1178 Unlock(); 1179 1180 // Delete the current message (fLastMessage) 1181 if (fLastMessage) { 1182 delete fLastMessage; 1183 fLastMessage = NULL; 1184 } 1185 1186 // Are any messages on the port? 1187 if (port_count(fMsgPort) > 0) { 1188 // Do outer loop 1189 dispatchNextMessage = false; 1190 } 1191 } 1192 } 1193 PRINT(("BLooper::task_looper() done\n")); 1194 } 1195 1196 1197 void 1198 BLooper::_QuitRequested(BMessage* message) 1199 { 1200 bool isQuitting = QuitRequested(); 1201 1202 // We send a reply to the sender, when they're waiting for a reply or 1203 // if the request message contains a boolean "_shutdown_" field with value 1204 // true. In the latter case the message came from the registrar, asking 1205 // the application to shut down. 1206 bool shutdown; 1207 if (message->IsSourceWaiting() 1208 || (message->FindBool("_shutdown_", &shutdown) == B_OK && shutdown)) { 1209 BMessage replyMsg(B_REPLY); 1210 replyMsg.AddBool("result", isQuitting); 1211 replyMsg.AddInt32("thread", fThread); 1212 message->SendReply(&replyMsg); 1213 } 1214 1215 if (isQuitting) 1216 Quit(); 1217 } 1218 1219 1220 bool 1221 BLooper::AssertLocked() const 1222 { 1223 if (!IsLocked()) { 1224 debugger("looper must be locked before proceeding\n"); 1225 return false; 1226 } 1227 1228 return true; 1229 } 1230 1231 1232 BHandler* 1233 BLooper::_TopLevelFilter(BMessage* message, BHandler* target) 1234 { 1235 if (message == NULL) 1236 return target; 1237 1238 // Apply the common filters first 1239 target = _ApplyFilters(CommonFilterList(), message, target); 1240 if (target) { 1241 if (target->Looper() != this) { 1242 debugger("Targeted handler does not belong to the looper."); 1243 target = NULL; 1244 } else { 1245 // Now apply handler-specific filters 1246 target = _HandlerFilter(message, target); 1247 } 1248 } 1249 1250 return target; 1251 } 1252 1253 1254 BHandler* 1255 BLooper::_HandlerFilter(BMessage* message, BHandler* target) 1256 { 1257 // Keep running filters until our handler is NULL, or until the filtering 1258 // handler returns itself as the designated handler 1259 BHandler* previousTarget = NULL; 1260 while (target != NULL && target != previousTarget) { 1261 previousTarget = target; 1262 1263 target = _ApplyFilters(target->FilterList(), message, target); 1264 if (target != NULL && target->Looper() != this) { 1265 debugger("Targeted handler does not belong to the looper."); 1266 target = NULL; 1267 } 1268 } 1269 1270 return target; 1271 } 1272 1273 1274 BHandler* 1275 BLooper::_ApplyFilters(BList* list, BMessage* message, BHandler* target) 1276 { 1277 // This is where the action is! 1278 // Check the parameters 1279 if (!list || !message) 1280 return target; 1281 1282 // For each filter in the provided list 1283 BMessageFilter* filter = NULL; 1284 for (int32 i = 0; i < list->CountItems(); ++i) { 1285 filter = (BMessageFilter*)list->ItemAt(i); 1286 1287 // Check command conditions 1288 if (filter->FiltersAnyCommand() || filter->Command() == message->what) { 1289 // Check delivery conditions 1290 message_delivery delivery = filter->MessageDelivery(); 1291 bool dropped = message->WasDropped(); 1292 if (delivery == B_ANY_DELIVERY 1293 || (delivery == B_DROPPED_DELIVERY && dropped) 1294 || (delivery == B_PROGRAMMED_DELIVERY && !dropped)) { 1295 // Check source conditions 1296 message_source source = filter->MessageSource(); 1297 bool remote = message->IsSourceRemote(); 1298 if (source == B_ANY_SOURCE 1299 || (source == B_REMOTE_SOURCE && remote) 1300 || (source == B_LOCAL_SOURCE && !remote)) { 1301 // Are we using an "external" function? 1302 filter_result result; 1303 filter_hook func = filter->FilterFunction(); 1304 if (func) 1305 result = func(message, &target, filter); 1306 else 1307 result = filter->Filter(message, &target); 1308 1309 // Is further processing allowed? 1310 if (result == B_SKIP_MESSAGE) { 1311 // No; time to bail out 1312 return NULL; 1313 } 1314 } 1315 } 1316 } 1317 } 1318 1319 return target; 1320 } 1321 1322 1323 void 1324 BLooper::check_lock() 1325 { 1326 // This is a cheap variant of AssertLocked() 1327 // It is used in situations where it's clear that the looper is valid, 1328 // ie. from handlers 1329 uint32 stack; 1330 if (((uint32)&stack & ~(B_PAGE_SIZE - 1)) == fCachedStack 1331 || fOwner == find_thread(NULL)) 1332 return; 1333 1334 debugger("Looper must be locked."); 1335 } 1336 1337 1338 BHandler* 1339 BLooper::resolve_specifier(BHandler* target, BMessage* message) 1340 { 1341 // Check params 1342 if (!target || !message) 1343 return NULL; 1344 1345 int32 index; 1346 BMessage specifier; 1347 int32 form; 1348 const char* property; 1349 status_t err = B_OK; 1350 BHandler* newTarget = target; 1351 // Loop to deal with nested specifiers 1352 // (e.g., the 3rd button on the 4th view) 1353 do { 1354 err = message->GetCurrentSpecifier(&index, &specifier, &form, &property); 1355 if (err) { 1356 BMessage reply(B_REPLY); 1357 reply.AddInt32("error", err); 1358 message->SendReply(&reply); 1359 return NULL; 1360 } 1361 // Current target gets what was the new target 1362 target = newTarget; 1363 newTarget = target->ResolveSpecifier(message, index, &specifier, form, 1364 property); 1365 // Check that new target is owned by looper; use IndexOf() to avoid 1366 // dereferencing newTarget (possible race condition with object 1367 // destruction by another looper) 1368 if (!newTarget || IndexOf(newTarget) < 0) 1369 return NULL; 1370 1371 // Get current specifier index (may change in ResolveSpecifier()) 1372 err = message->GetCurrentSpecifier(&index); 1373 } while (newTarget && newTarget != target && !err && index >= 0); 1374 1375 return newTarget; 1376 } 1377 1378 1379 /*! Releases all eventually nested locks. Must be called with the lock 1380 actually held. 1381 */ 1382 void 1383 BLooper::UnlockFully() 1384 { 1385 AssertLocked(); 1386 1387 // Clear the owner count 1388 fOwnerCount = 0; 1389 // Nobody owns the lock now 1390 fOwner = -1; 1391 fCachedStack = 0; 1392 #if DEBUG < 1 1393 // There is now one less thread holding a lock on this looper 1394 int32 atomicCount = atomic_add(&fAtomicCount, -1); 1395 if (atomicCount > 1) 1396 #endif 1397 release_sem(fLockSem); 1398 } 1399 1400 1401 // #pragma mark - 1402 1403 1404 port_id 1405 _get_looper_port_(const BLooper* looper) 1406 { 1407 return looper->fMsgPort; 1408 } 1409 1410