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