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