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