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