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=%" B_PRId32 ", looper=%s\n", Team(), 447 Name() ? Name() : "unnamed"); 448 } 449 450 // Try to lock 451 if (!Lock()) { 452 // We're toast already 453 return; 454 } 455 456 PRINT((" is locked\n")); 457 458 if (!fRunCalled) { 459 PRINT((" Run() has not been called yet\n")); 460 fTerminating = true; 461 delete this; 462 } else if (find_thread(NULL) == fThread) { 463 PRINT((" We are the looper thread\n")); 464 fTerminating = true; 465 delete this; 466 exit_thread(0); 467 } else { 468 PRINT((" Run() has already been called and we are not the looper thread\n")); 469 470 // As with sem in _Lock(), we need to cache this here in case the looper 471 // disappears before we get to the wait_for_thread() below 472 thread_id thread = Thread(); 473 474 // We need to unlock here. Otherwise the looper thread can't 475 // dispatch the _QUIT_ message we're going to post. 476 UnlockFully(); 477 478 // As per the BeBook, if we've been called by a thread other than 479 // our own, the rest of the message queue has to get processed. So 480 // we put this in the queue, and when it shows up, we'll call Quit() 481 // from our own thread. 482 // QuitRequested() will not be called in this case. 483 PostMessage(_QUIT_); 484 485 // We have to wait until the looper is done processing any remaining 486 // messages. 487 status_t status; 488 while (wait_for_thread(thread, &status) == B_INTERRUPTED) 489 ; 490 } 491 492 PRINT(("BLooper::Quit() done\n")); 493 } 494 495 496 bool 497 BLooper::QuitRequested() 498 { 499 return true; 500 } 501 502 503 bool 504 BLooper::Lock() 505 { 506 // Defer to global _Lock(); see notes there 507 return _Lock(this, -1, B_INFINITE_TIMEOUT) == B_OK; 508 } 509 510 511 void 512 BLooper::Unlock() 513 { 514 PRINT(("BLooper::Unlock()\n")); 515 // Make sure we're locked to begin with 516 AssertLocked(); 517 518 // Decrement fOwnerCount 519 --fOwnerCount; 520 PRINT((" fOwnerCount now: %ld\n", fOwnerCount)); 521 // Check to see if the owner still wants a lock 522 if (fOwnerCount == 0) { 523 // Set fOwner to invalid thread_id (< 0) 524 fOwner = -1; 525 fCachedStack = 0; 526 527 #if DEBUG < 1 528 // Decrement requested lock count (using fAtomicCount for this) 529 int32 atomicCount = atomic_add(&fAtomicCount, -1); 530 PRINT((" fAtomicCount now: %ld\n", fAtomicCount)); 531 532 // Check if anyone is waiting for a lock 533 // and release if it's the case 534 if (atomicCount > 1) 535 #endif 536 release_sem(fLockSem); 537 } 538 PRINT(("BLooper::Unlock() done\n")); 539 } 540 541 542 bool 543 BLooper::IsLocked() const 544 { 545 if (!gLooperList.IsLooperValid(this)) { 546 // The looper is gone, so of course it's not locked 547 return false; 548 } 549 550 uint32 stack; 551 return ((addr_t)&stack & ~(B_PAGE_SIZE - 1)) == fCachedStack 552 || find_thread(NULL) == fOwner; 553 } 554 555 556 status_t 557 BLooper::LockWithTimeout(bigtime_t timeout) 558 { 559 return _Lock(this, -1, timeout); 560 } 561 562 563 thread_id 564 BLooper::Thread() const 565 { 566 return fThread; 567 } 568 569 570 team_id 571 BLooper::Team() const 572 { 573 return BPrivate::current_team(); 574 } 575 576 577 BLooper* 578 BLooper::LooperForThread(thread_id thread) 579 { 580 return gLooperList.LooperForThread(thread); 581 } 582 583 584 thread_id 585 BLooper::LockingThread() const 586 { 587 return fOwner; 588 } 589 590 591 int32 592 BLooper::CountLocks() const 593 { 594 return fOwnerCount; 595 } 596 597 598 int32 599 BLooper::CountLockRequests() const 600 { 601 return fAtomicCount; 602 } 603 604 605 sem_id 606 BLooper::Sem() const 607 { 608 return fLockSem; 609 } 610 611 612 BHandler* 613 BLooper::ResolveSpecifier(BMessage* msg, int32 index, BMessage* specifier, 614 int32 form, const char* property) 615 { 616 /** 617 @note When I was first dumping the results of GetSupportedSuites() from 618 various classes, the use of the extra_data field was quite 619 mysterious to me. Then I dumped BApplication and compared the 620 result against the BeBook's docs for scripting BApplication. A 621 bunch of it isn't documented, but what is tipped me to the idea 622 that the extra_data is being used as a quick and dirty way to tell 623 what scripting "command" has been sent, e.g., for easy use in a 624 switch statement. Would certainly be a lot faster than a bunch of 625 string comparisons -- which wouldn't tell the whole story anyway, 626 because of the same name being used for multiple properties. 627 */ 628 BPropertyInfo propertyInfo(sLooperPropInfo); 629 uint32 data; 630 status_t err = B_OK; 631 const char* errMsg = ""; 632 if (propertyInfo.FindMatch(msg, index, specifier, form, property, &data) 633 >= 0) { 634 switch (data) { 635 case BLOOPER_PROCESS_INTERNALLY: 636 return this; 637 638 case BLOOPER_HANDLER_BY_INDEX: 639 { 640 int32 index = specifier->FindInt32("index"); 641 if (form == B_REVERSE_INDEX_SPECIFIER) { 642 index = CountHandlers() - index; 643 } 644 BHandler* target = HandlerAt(index); 645 if (target) { 646 // Specifier has been fully handled 647 msg->PopSpecifier(); 648 return target; 649 } else { 650 err = B_BAD_INDEX; 651 errMsg = "handler index out of range"; 652 } 653 break; 654 } 655 656 default: 657 err = B_BAD_SCRIPT_SYNTAX; 658 errMsg = "Didn't understand the specifier(s)"; 659 break; 660 } 661 } else { 662 return BHandler::ResolveSpecifier(msg, index, specifier, form, 663 property); 664 } 665 666 BMessage reply(B_MESSAGE_NOT_UNDERSTOOD); 667 reply.AddInt32("error", err); 668 reply.AddString("message", errMsg); 669 msg->SendReply(&reply); 670 671 return NULL; 672 } 673 674 675 status_t 676 BLooper::GetSupportedSuites(BMessage* data) 677 { 678 if (data == NULL) 679 return B_BAD_VALUE; 680 681 status_t status = data->AddString("suites", "suite/vnd.Be-looper"); 682 if (status == B_OK) { 683 BPropertyInfo PropertyInfo(sLooperPropInfo); 684 status = data->AddFlat("messages", &PropertyInfo); 685 if (status == B_OK) 686 status = BHandler::GetSupportedSuites(data); 687 } 688 689 return status; 690 } 691 692 693 void 694 BLooper::AddCommonFilter(BMessageFilter* filter) 695 { 696 if (!filter) 697 return; 698 699 AssertLocked(); 700 701 if (filter->Looper()) { 702 debugger("A MessageFilter can only be used once."); 703 return; 704 } 705 706 if (!fCommonFilters) 707 fCommonFilters = new BList(FILTER_LIST_BLOCK_SIZE); 708 709 filter->SetLooper(this); 710 fCommonFilters->AddItem(filter); 711 } 712 713 714 bool 715 BLooper::RemoveCommonFilter(BMessageFilter* filter) 716 { 717 AssertLocked(); 718 719 if (!fCommonFilters) 720 return false; 721 722 bool result = fCommonFilters->RemoveItem(filter); 723 if (result) 724 filter->SetLooper(NULL); 725 726 return result; 727 } 728 729 730 void 731 BLooper::SetCommonFilterList(BList* filters) 732 { 733 AssertLocked(); 734 735 BMessageFilter* filter; 736 if (filters) { 737 // Check for ownership issues - a filter can only have one owner 738 for (int32 i = 0; i < filters->CountItems(); ++i) { 739 filter = (BMessageFilter*)filters->ItemAt(i); 740 if (filter->Looper()) { 741 debugger("A MessageFilter can only be used once."); 742 return; 743 } 744 } 745 } 746 747 if (fCommonFilters) { 748 for (int32 i = 0; i < fCommonFilters->CountItems(); ++i) { 749 delete (BMessageFilter*)fCommonFilters->ItemAt(i); 750 } 751 752 delete fCommonFilters; 753 fCommonFilters = NULL; 754 } 755 756 // Per the BeBook, we take ownership of the list 757 fCommonFilters = filters; 758 if (fCommonFilters) { 759 for (int32 i = 0; i < fCommonFilters->CountItems(); ++i) { 760 filter = (BMessageFilter*)fCommonFilters->ItemAt(i); 761 filter->SetLooper(this); 762 } 763 } 764 } 765 766 767 BList* 768 BLooper::CommonFilterList() const 769 { 770 return fCommonFilters; 771 } 772 773 774 status_t 775 BLooper::Perform(perform_code d, void* arg) 776 { 777 // This is sort of what we're doing for this function everywhere 778 return BHandler::Perform(d, arg); 779 } 780 781 782 BMessage* 783 BLooper::MessageFromPort(bigtime_t timeout) 784 { 785 return ReadMessageFromPort(timeout); 786 } 787 788 789 void BLooper::_ReservedLooper1() {} 790 void BLooper::_ReservedLooper2() {} 791 void BLooper::_ReservedLooper3() {} 792 void BLooper::_ReservedLooper4() {} 793 void BLooper::_ReservedLooper5() {} 794 void BLooper::_ReservedLooper6() {} 795 796 797 BLooper::BLooper(const BLooper& other) 798 { 799 // Copy construction not allowed 800 } 801 802 803 BLooper& 804 BLooper::operator=(const BLooper& other) 805 { 806 // Looper copying not allowed 807 return *this; 808 } 809 810 811 BLooper::BLooper(int32 priority, port_id port, const char* name) 812 { 813 // This must be a legacy constructor 814 fMsgPort = port; 815 _InitData(name, priority, B_LOOPER_PORT_DEFAULT_CAPACITY); 816 } 817 818 819 status_t 820 BLooper::_PostMessage(BMessage *msg, BHandler *handler, BHandler *replyTo) 821 { 822 AutoLocker<BLooperList> listLocker(gLooperList); 823 if (!listLocker.IsLocked()) 824 return B_ERROR; 825 826 if (!gLooperList.IsLooperValid(this)) 827 return B_BAD_VALUE; 828 829 // Does handler belong to this looper? 830 if (handler && handler->Looper() != this) 831 return B_MISMATCHED_VALUES; 832 833 status_t status; 834 BMessenger messenger(handler, this, &status); 835 listLocker.Unlock(); 836 if (status == B_OK) 837 status = messenger.SendMessage(msg, replyTo, 0); 838 839 return status; 840 } 841 842 843 /*! 844 Locks a looper either by port or using a direct pointer to the looper. 845 846 \param looper looper to lock, if not NULL 847 \param port port to identify the looper in case \a looper is NULL 848 \param timeout timeout for acquiring the lock 849 */ 850 status_t 851 BLooper::_Lock(BLooper* looper, port_id port, bigtime_t timeout) 852 { 853 PRINT(("BLooper::_Lock(%p, %lx)\n", looper, port)); 854 855 // Check params (loop, port) 856 if (looper == NULL && port < 0) { 857 PRINT(("BLooper::_Lock() done 1\n")); 858 return B_BAD_VALUE; 859 } 860 861 thread_id currentThread = find_thread(NULL); 862 int32 oldCount; 863 sem_id sem; 864 865 { 866 AutoLocker<BLooperList> ListLock(gLooperList); 867 if (!ListLock.IsLocked()) 868 return B_BAD_VALUE; 869 870 // Look up looper by port_id, if necessary 871 if (looper == NULL) { 872 looper = gLooperList.LooperForPort(port); 873 if (looper == NULL) { 874 PRINT(("BLooper::_Lock() done 3\n")); 875 return B_BAD_VALUE; 876 } 877 } else if (!gLooperList.IsLooperValid(looper)) { 878 // Check looper validity 879 PRINT(("BLooper::_Lock() done 4\n")); 880 return B_BAD_VALUE; 881 } 882 883 // Check for nested lock attempt 884 if (currentThread == looper->fOwner) { 885 ++looper->fOwnerCount; 886 PRINT(("BLooper::_Lock() done 5: fOwnerCount: %ld\n", loop->fOwnerCount)); 887 return B_OK; 888 } 889 890 // Cache the semaphore, so that we can safely access it after having 891 // unlocked the looper list 892 sem = looper->fLockSem; 893 if (sem < 0) { 894 PRINT(("BLooper::_Lock() done 6\n")); 895 return B_BAD_VALUE; 896 } 897 898 // Bump the requested lock count (using fAtomicCount for this) 899 oldCount = atomic_add(&looper->fAtomicCount, 1); 900 } 901 902 return _LockComplete(looper, oldCount, currentThread, sem, timeout); 903 } 904 905 906 status_t 907 BLooper::_LockComplete(BLooper *looper, int32 oldCount, thread_id thread, 908 sem_id sem, bigtime_t timeout) 909 { 910 status_t err = B_OK; 911 912 #if DEBUG < 1 913 if (oldCount > 0) { 914 #endif 915 do { 916 err = acquire_sem_etc(sem, 1, B_RELATIVE_TIMEOUT, timeout); 917 } while (err == B_INTERRUPTED); 918 #if DEBUG < 1 919 } 920 #endif 921 if (err == B_OK) { 922 looper->fOwner = thread; 923 looper->fCachedStack = (addr_t)&err & ~(B_PAGE_SIZE - 1); 924 looper->fOwnerCount = 1; 925 } 926 927 PRINT(("BLooper::_LockComplete() done: %lx\n", err)); 928 return err; 929 } 930 931 932 void 933 BLooper::_InitData(const char *name, int32 priority, int32 portCapacity) 934 { 935 fOwner = B_ERROR; 936 fCachedStack = 0; 937 fRunCalled = false; 938 fDirectTarget = new (std::nothrow) BPrivate::BDirectMessageTarget(); 939 fCommonFilters = NULL; 940 fLastMessage = NULL; 941 fPreferred = NULL; 942 fThread = B_ERROR; 943 fTerminating = false; 944 fMsgPort = -1; 945 fAtomicCount = 0; 946 947 if (name == NULL) 948 name = "anonymous looper"; 949 950 #if DEBUG 951 fLockSem = create_sem(1, name); 952 #else 953 fLockSem = create_sem(0, name); 954 #endif 955 956 if (portCapacity <= 0) 957 portCapacity = B_LOOPER_PORT_DEFAULT_CAPACITY; 958 959 fMsgPort = create_port(portCapacity, name); 960 961 fInitPriority = priority; 962 963 gLooperList.AddLooper(this); 964 // this will also lock this looper 965 966 AddHandler(this); 967 } 968 969 970 void 971 BLooper::AddMessage(BMessage* message) 972 { 973 _AddMessagePriv(message); 974 975 // wakeup looper when being called from other threads if necessary 976 if (find_thread(NULL) != Thread() 977 && fDirectTarget->Queue()->IsNextMessage(message) 978 && port_count(fMsgPort) <= 0) { 979 // there is currently no message waiting, and we need to wakeup the 980 // looper 981 write_port_etc(fMsgPort, 0, NULL, 0, B_RELATIVE_TIMEOUT, 0); 982 } 983 } 984 985 986 void 987 BLooper::_AddMessagePriv(BMessage* message) 988 { 989 // ToDo: if no target token is specified, set to preferred handler 990 // Others may want to peek into our message queue, so the preferred 991 // handler must be set correctly already if no token was given 992 993 fDirectTarget->Queue()->AddMessage(message); 994 } 995 996 997 status_t 998 BLooper::_task0_(void* arg) 999 { 1000 BLooper* looper = (BLooper *)arg; 1001 1002 PRINT(("LOOPER: _task0_()\n")); 1003 1004 if (looper->Lock()) { 1005 PRINT(("LOOPER: looper locked\n")); 1006 looper->task_looper(); 1007 1008 delete looper; 1009 } 1010 1011 PRINT(("LOOPER: _task0_() done: thread %ld\n", find_thread(NULL))); 1012 return B_OK; 1013 } 1014 1015 1016 void * 1017 BLooper::ReadRawFromPort(int32* msgCode, bigtime_t timeout) 1018 { 1019 PRINT(("BLooper::ReadRawFromPort()\n")); 1020 uint8 *buffer = NULL; 1021 ssize_t bufferSize; 1022 1023 do { 1024 bufferSize = port_buffer_size_etc(fMsgPort, B_RELATIVE_TIMEOUT, timeout); 1025 } while (bufferSize == B_INTERRUPTED); 1026 1027 if (bufferSize < B_OK) { 1028 PRINT(("BLooper::ReadRawFromPort(): failed: %ld\n", bufferSize)); 1029 return NULL; 1030 } 1031 1032 if (bufferSize > 0) 1033 buffer = (uint8 *)malloc(bufferSize); 1034 1035 // we don't want to wait again here, since that can only mean 1036 // that someone else has read our message and our bufferSize 1037 // is now probably wrong 1038 PRINT(("read_port()...\n")); 1039 bufferSize = read_port_etc(fMsgPort, msgCode, buffer, bufferSize, 1040 B_RELATIVE_TIMEOUT, 0); 1041 1042 if (bufferSize < B_OK) { 1043 free(buffer); 1044 return NULL; 1045 } 1046 1047 PRINT(("BLooper::ReadRawFromPort() read: %.4s, %p (%d bytes)\n", (char *)msgCode, buffer, bufferSize)); 1048 return buffer; 1049 } 1050 1051 1052 BMessage* 1053 BLooper::ReadMessageFromPort(bigtime_t timeout) 1054 { 1055 PRINT(("BLooper::ReadMessageFromPort()\n")); 1056 int32 msgCode; 1057 BMessage *message = NULL; 1058 1059 void *buffer = ReadRawFromPort(&msgCode, timeout); 1060 if (!buffer) 1061 return NULL; 1062 1063 message = ConvertToMessage(buffer, msgCode); 1064 free(buffer); 1065 1066 PRINT(("BLooper::ReadMessageFromPort() done: %p\n", message)); 1067 return message; 1068 } 1069 1070 1071 BMessage* 1072 BLooper::ConvertToMessage(void* buffer, int32 code) 1073 { 1074 PRINT(("BLooper::ConvertToMessage()\n")); 1075 if (!buffer) 1076 return NULL; 1077 1078 BMessage* message = new BMessage(); 1079 if (message->Unflatten((const char*)buffer) != B_OK) { 1080 PRINT(("BLooper::ConvertToMessage(): unflattening message failed\n")); 1081 delete message; 1082 message = NULL; 1083 } 1084 1085 PRINT(("BLooper::ConvertToMessage(): %p\n", message)); 1086 return message; 1087 } 1088 1089 1090 void 1091 BLooper::task_looper() 1092 { 1093 PRINT(("BLooper::task_looper()\n")); 1094 // Check that looper is locked (should be) 1095 AssertLocked(); 1096 // Unlock the looper 1097 Unlock(); 1098 1099 if (IsLocked()) 1100 debugger("looper must not be locked!"); 1101 1102 // loop: As long as we are not terminating. 1103 while (!fTerminating) { 1104 PRINT(("LOOPER: outer loop\n")); 1105 // TODO: timeout determination algo 1106 // Read from message port (how do we determine what the timeout is?) 1107 PRINT(("LOOPER: MessageFromPort()...\n")); 1108 BMessage *msg = MessageFromPort(); 1109 PRINT(("LOOPER: ...done\n")); 1110 1111 // Did we get a message? 1112 if (msg) 1113 _AddMessagePriv(msg); 1114 1115 // Get message count from port 1116 int32 msgCount = port_count(fMsgPort); 1117 for (int32 i = 0; i < msgCount; ++i) { 1118 // Read 'count' messages from port (so we will not block) 1119 // We use zero as our timeout since we know there is stuff there 1120 msg = MessageFromPort(0); 1121 if (msg) 1122 _AddMessagePriv(msg); 1123 } 1124 1125 // loop: As long as there are messages in the queue and the port is 1126 // empty... and we are not terminating, of course. 1127 bool dispatchNextMessage = true; 1128 while (!fTerminating && dispatchNextMessage) { 1129 PRINT(("LOOPER: inner loop\n")); 1130 // Get next message from queue (assign to fLastMessage) 1131 fLastMessage = fDirectTarget->Queue()->NextMessage(); 1132 1133 Lock(); 1134 1135 if (!fLastMessage) { 1136 // No more messages: Unlock the looper and terminate the 1137 // dispatch loop. 1138 dispatchNextMessage = false; 1139 } else { 1140 PRINT(("LOOPER: fLastMessage: 0x%lx: %.4s\n", fLastMessage->what, 1141 (char*)&fLastMessage->what)); 1142 DBG(fLastMessage->PrintToStream()); 1143 1144 // Get the target handler 1145 BHandler *handler = NULL; 1146 BMessage::Private messagePrivate(fLastMessage); 1147 bool usePreferred = messagePrivate.UsePreferredTarget(); 1148 1149 if (usePreferred) { 1150 PRINT(("LOOPER: use preferred target\n")); 1151 handler = fPreferred; 1152 if (handler == NULL) 1153 handler = this; 1154 } else { 1155 gDefaultTokens.GetToken(messagePrivate.GetTarget(), 1156 B_HANDLER_TOKEN, (void **)&handler); 1157 1158 // if this handler doesn't belong to us, we drop the message 1159 if (handler != NULL && handler->Looper() != this) 1160 handler = NULL; 1161 1162 PRINT(("LOOPER: use %ld, handler: %p, this: %p\n", 1163 messagePrivate.GetTarget(), handler, this)); 1164 } 1165 1166 // Is this a scripting message? (BMessage::HasSpecifiers()) 1167 if (handler != NULL && fLastMessage->HasSpecifiers()) { 1168 int32 index = 0; 1169 // Make sure the current specifier is kosher 1170 if (fLastMessage->GetCurrentSpecifier(&index) == B_OK) 1171 handler = resolve_specifier(handler, fLastMessage); 1172 } 1173 1174 if (handler) { 1175 // Do filtering 1176 handler = _TopLevelFilter(fLastMessage, handler); 1177 PRINT(("LOOPER: _TopLevelFilter(): %p\n", handler)); 1178 if (handler && handler->Looper() == this) 1179 DispatchMessage(fLastMessage, handler); 1180 } 1181 } 1182 1183 if (fTerminating) { 1184 // we leave the looper locked when we quit 1185 return; 1186 } 1187 1188 // Unlock the looper 1189 Unlock(); 1190 1191 // Delete the current message (fLastMessage) 1192 if (fLastMessage) { 1193 delete fLastMessage; 1194 fLastMessage = NULL; 1195 } 1196 1197 // Are any messages on the port? 1198 if (port_count(fMsgPort) > 0) { 1199 // Do outer loop 1200 dispatchNextMessage = false; 1201 } 1202 } 1203 } 1204 PRINT(("BLooper::task_looper() done\n")); 1205 } 1206 1207 1208 void 1209 BLooper::_QuitRequested(BMessage* message) 1210 { 1211 bool isQuitting = QuitRequested(); 1212 int32 thread = fThread; 1213 1214 if (isQuitting) 1215 Quit(); 1216 1217 // We send a reply to the sender, when they're waiting for a reply or 1218 // if the request message contains a boolean "_shutdown_" field with value 1219 // true. In the latter case the message came from the registrar, asking 1220 // the application to shut down. 1221 bool shutdown; 1222 if (message->IsSourceWaiting() 1223 || (message->FindBool("_shutdown_", &shutdown) == B_OK && shutdown)) { 1224 BMessage replyMsg(B_REPLY); 1225 replyMsg.AddBool("result", isQuitting); 1226 replyMsg.AddInt32("thread", thread); 1227 message->SendReply(&replyMsg); 1228 } 1229 } 1230 1231 1232 bool 1233 BLooper::AssertLocked() const 1234 { 1235 if (!IsLocked()) { 1236 debugger("looper must be locked before proceeding\n"); 1237 return false; 1238 } 1239 1240 return true; 1241 } 1242 1243 1244 BHandler* 1245 BLooper::_TopLevelFilter(BMessage* message, BHandler* target) 1246 { 1247 if (message == NULL) 1248 return target; 1249 1250 // Apply the common filters first 1251 target = _ApplyFilters(CommonFilterList(), message, target); 1252 if (target) { 1253 if (target->Looper() != this) { 1254 debugger("Targeted handler does not belong to the looper."); 1255 target = NULL; 1256 } else { 1257 // Now apply handler-specific filters 1258 target = _HandlerFilter(message, target); 1259 } 1260 } 1261 1262 return target; 1263 } 1264 1265 1266 BHandler* 1267 BLooper::_HandlerFilter(BMessage* message, BHandler* target) 1268 { 1269 // Keep running filters until our handler is NULL, or until the filtering 1270 // handler returns itself as the designated handler 1271 BHandler* previousTarget = NULL; 1272 while (target != NULL && target != previousTarget) { 1273 previousTarget = target; 1274 1275 target = _ApplyFilters(target->FilterList(), message, target); 1276 if (target != NULL && target->Looper() != this) { 1277 debugger("Targeted handler does not belong to the looper."); 1278 target = NULL; 1279 } 1280 } 1281 1282 return target; 1283 } 1284 1285 1286 BHandler* 1287 BLooper::_ApplyFilters(BList* list, BMessage* message, BHandler* target) 1288 { 1289 // This is where the action is! 1290 // Check the parameters 1291 if (!list || !message) 1292 return target; 1293 1294 // For each filter in the provided list 1295 BMessageFilter* filter = NULL; 1296 for (int32 i = 0; i < list->CountItems(); ++i) { 1297 filter = (BMessageFilter*)list->ItemAt(i); 1298 1299 // Check command conditions 1300 if (filter->FiltersAnyCommand() || filter->Command() == message->what) { 1301 // Check delivery conditions 1302 message_delivery delivery = filter->MessageDelivery(); 1303 bool dropped = message->WasDropped(); 1304 if (delivery == B_ANY_DELIVERY 1305 || (delivery == B_DROPPED_DELIVERY && dropped) 1306 || (delivery == B_PROGRAMMED_DELIVERY && !dropped)) { 1307 // Check source conditions 1308 message_source source = filter->MessageSource(); 1309 bool remote = message->IsSourceRemote(); 1310 if (source == B_ANY_SOURCE 1311 || (source == B_REMOTE_SOURCE && remote) 1312 || (source == B_LOCAL_SOURCE && !remote)) { 1313 // Are we using an "external" function? 1314 filter_result result; 1315 filter_hook func = filter->FilterFunction(); 1316 if (func) 1317 result = func(message, &target, filter); 1318 else 1319 result = filter->Filter(message, &target); 1320 1321 // Is further processing allowed? 1322 if (result == B_SKIP_MESSAGE) { 1323 // No; time to bail out 1324 return NULL; 1325 } 1326 } 1327 } 1328 } 1329 } 1330 1331 return target; 1332 } 1333 1334 1335 void 1336 BLooper::check_lock() 1337 { 1338 // This is a cheap variant of AssertLocked() 1339 // It is used in situations where it's clear that the looper is valid, 1340 // ie. from handlers 1341 uint32 stack; 1342 if (((addr_t)&stack & ~(B_PAGE_SIZE - 1)) == fCachedStack 1343 || fOwner == find_thread(NULL)) 1344 return; 1345 1346 debugger("Looper must be locked."); 1347 } 1348 1349 1350 BHandler* 1351 BLooper::resolve_specifier(BHandler* target, BMessage* message) 1352 { 1353 // Check params 1354 if (!target || !message) 1355 return NULL; 1356 1357 int32 index; 1358 BMessage specifier; 1359 int32 form; 1360 const char* property; 1361 status_t err = B_OK; 1362 BHandler* newTarget = target; 1363 // Loop to deal with nested specifiers 1364 // (e.g., the 3rd button on the 4th view) 1365 do { 1366 err = message->GetCurrentSpecifier(&index, &specifier, &form, &property); 1367 if (err != B_OK) { 1368 BMessage reply(B_REPLY); 1369 reply.AddInt32("error", err); 1370 message->SendReply(&reply); 1371 return NULL; 1372 } 1373 // Current target gets what was the new target 1374 target = newTarget; 1375 newTarget = target->ResolveSpecifier(message, index, &specifier, form, 1376 property); 1377 // Check that new target is owned by looper; use IndexOf() to avoid 1378 // dereferencing newTarget (possible race condition with object 1379 // destruction by another looper) 1380 if (!newTarget || IndexOf(newTarget) < 0) 1381 return NULL; 1382 1383 // Get current specifier index (may change in ResolveSpecifier()) 1384 err = message->GetCurrentSpecifier(&index); 1385 } while (newTarget && newTarget != target && err == B_OK && index >= 0); 1386 1387 return newTarget; 1388 } 1389 1390 1391 /*! Releases all eventually nested locks. Must be called with the lock 1392 actually held. 1393 */ 1394 void 1395 BLooper::UnlockFully() 1396 { 1397 AssertLocked(); 1398 1399 // Clear the owner count 1400 fOwnerCount = 0; 1401 // Nobody owns the lock now 1402 fOwner = -1; 1403 fCachedStack = 0; 1404 #if DEBUG < 1 1405 // There is now one less thread holding a lock on this looper 1406 int32 atomicCount = atomic_add(&fAtomicCount, -1); 1407 if (atomicCount > 1) 1408 #endif 1409 release_sem(fLockSem); 1410 } 1411 1412 1413 // #pragma mark - 1414 1415 1416 port_id 1417 _get_looper_port_(const BLooper* looper) 1418 { 1419 return looper->fMsgPort; 1420 } 1421 1422