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