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