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 port_capacity) 106 : BHandler(name) 107 { 108 _InitData(name, priority, port_capacity); 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 BHandler *child; 157 while (CountHandlers()) { 158 child = HandlerAt(0); 159 if (child) 160 RemoveHandler(child); 161 } 162 163 Unlock(); 164 gLooperList.RemoveLooper(this); 165 delete_sem(fLockSem); 166 } 167 168 169 BLooper::BLooper(BMessage *data) 170 : BHandler(data) 171 { 172 int32 portCapacity; 173 if (data->FindInt32("_port_cap", &portCapacity) != B_OK 174 || portCapacity < 0) 175 portCapacity = B_LOOPER_PORT_DEFAULT_CAPACITY; 176 177 _InitData(Name(), B_NORMAL_PRIORITY, portCapacity); 178 } 179 180 181 BArchivable * 182 BLooper::Instantiate(BMessage *data) 183 { 184 if (validate_instantiation(data, "BLooper")) 185 return new BLooper(data); 186 187 return NULL; 188 } 189 190 191 status_t 192 BLooper::Archive(BMessage *data, bool deep) const 193 { 194 status_t status = BHandler::Archive(data, deep); 195 if (status < B_OK) 196 return status; 197 198 port_info info; 199 status = get_port_info(fMsgPort, &info); 200 if (status == B_OK) 201 status = data->AddInt32("_port_cap", info.capacity); 202 203 // TODO: what about the thread priority? 204 205 return status; 206 } 207 208 209 status_t 210 BLooper::PostMessage(uint32 command) 211 { 212 BMessage message(command); 213 return _PostMessage(&message, this, NULL); 214 } 215 216 217 status_t 218 BLooper::PostMessage(BMessage *message) 219 { 220 return _PostMessage(message, this, NULL); 221 } 222 223 224 status_t 225 BLooper::PostMessage(uint32 command, BHandler *handler, 226 BHandler *replyTo) 227 { 228 BMessage message(command); 229 return _PostMessage(&message, handler, replyTo); 230 } 231 232 233 status_t 234 BLooper::PostMessage(BMessage *message, BHandler *handler, 235 BHandler *replyTo) 236 { 237 return _PostMessage(message, handler, replyTo); 238 } 239 240 241 void 242 BLooper::DispatchMessage(BMessage *message, BHandler *handler) 243 { 244 PRINT(("BLooper::DispatchMessage(%.4s)\n", (char*)&message->what)); 245 246 switch (message->what) { 247 case _QUIT_: 248 // Can't call Quit() to do this, because of the slight chance 249 // another thread with have us locked between now and then. 250 fTerminating = true; 251 252 // After returning from DispatchMessage(), the looper will be 253 // deleted in _task0_() 254 break; 255 256 case B_QUIT_REQUESTED: 257 if (handler == this) { 258 _QuitRequested(message); 259 break; 260 } 261 262 // fall through 263 264 default: 265 handler->MessageReceived(message); 266 break; 267 } 268 PRINT(("BLooper::DispatchMessage() done\n")); 269 } 270 271 272 void 273 BLooper::MessageReceived(BMessage *msg) 274 { 275 // TODO: implement scripting support 276 BHandler::MessageReceived(msg); 277 } 278 279 280 BMessage* 281 BLooper::CurrentMessage() const 282 { 283 return fLastMessage; 284 } 285 286 287 BMessage* 288 BLooper::DetachCurrentMessage() 289 { 290 BMessage* msg = fLastMessage; 291 fLastMessage = NULL; 292 return msg; 293 } 294 295 296 BMessageQueue* 297 BLooper::MessageQueue() const 298 { 299 return fDirectTarget->Queue(); 300 } 301 302 303 bool 304 BLooper::IsMessageWaiting() const 305 { 306 AssertLocked(); 307 308 if (!fDirectTarget->Queue()->IsEmpty()) 309 return true; 310 311 int32 count; 312 do { 313 count = port_buffer_size_etc(fMsgPort, B_RELATIVE_TIMEOUT, 0); 314 } while (count == B_INTERRUPTED); 315 316 return count > 0; 317 } 318 319 320 void 321 BLooper::AddHandler(BHandler* handler) 322 { 323 if (handler == NULL) 324 return; 325 326 AssertLocked(); 327 328 if (handler->Looper() == NULL) { 329 fHandlers.AddItem(handler); 330 handler->SetLooper(this); 331 if (handler != this) // avoid a cycle 332 handler->SetNextHandler(this); 333 } 334 } 335 336 337 bool 338 BLooper::RemoveHandler(BHandler* handler) 339 { 340 if (handler == NULL) 341 return false; 342 343 AssertLocked(); 344 345 if (handler->Looper() == this && fHandlers.RemoveItem(handler)) { 346 if (handler == fPreferred) 347 fPreferred = NULL; 348 349 handler->SetNextHandler(NULL); 350 handler->SetLooper(NULL); 351 return true; 352 } 353 354 return false; 355 } 356 357 358 int32 359 BLooper::CountHandlers() const 360 { 361 AssertLocked(); 362 363 return fHandlers.CountItems(); 364 } 365 366 367 BHandler* 368 BLooper::HandlerAt(int32 index) const 369 { 370 AssertLocked(); 371 372 return (BHandler*)fHandlers.ItemAt(index); 373 } 374 375 376 int32 377 BLooper::IndexOf(BHandler* handler) const 378 { 379 AssertLocked(); 380 381 return fHandlers.IndexOf(handler); 382 } 383 384 385 BHandler* 386 BLooper::PreferredHandler() const 387 { 388 return fPreferred; 389 } 390 391 392 void 393 BLooper::SetPreferredHandler(BHandler* handler) 394 { 395 if (handler && handler->Looper() == this && IndexOf(handler) >= 0) { 396 fPreferred = handler; 397 } else { 398 fPreferred = NULL; 399 } 400 } 401 402 403 thread_id 404 BLooper::Run() 405 { 406 AssertLocked(); 407 408 if (fRunCalled) { 409 // Not allowed to call Run() more than once 410 debugger("can't call BLooper::Run twice!"); 411 return fThread; 412 } 413 414 fThread = spawn_thread(_task0_, Name(), fInitPriority, this); 415 if (fThread < B_OK) 416 return fThread; 417 418 if (fMsgPort < B_OK) 419 return fMsgPort; 420 421 fRunCalled = true; 422 Unlock(); 423 424 status_t err = resume_thread(fThread); 425 if (err < B_OK) 426 return err; 427 428 return fThread; 429 } 430 431 432 void 433 BLooper::Quit() 434 { 435 PRINT(("BLooper::Quit()\n")); 436 437 if (!IsLocked()) { 438 printf("ERROR - you must Lock a looper before calling Quit(), " 439 "team=%ld, looper=%s\n", Team(), Name() ? Name() : "unnamed"); 440 } 441 442 // Try to lock 443 if (!Lock()) { 444 // We're toast already 445 return; 446 } 447 448 PRINT((" is locked\n")); 449 450 if (!fRunCalled) { 451 PRINT((" Run() has not been called yet\n")); 452 fTerminating = true; 453 delete this; 454 } else if (find_thread(NULL) == fThread) { 455 PRINT((" We are the looper thread\n")); 456 fTerminating = true; 457 delete this; 458 exit_thread(0); 459 } else { 460 PRINT((" Run() has already been called and we are not the looper thread\n")); 461 462 // As with sem in _Lock(), we need to cache this here in case the looper 463 // disappears before we get to the wait_for_thread() below 464 thread_id thread = Thread(); 465 466 // We need to unlock here. Otherwise the looper thread can't 467 // dispatch the _QUIT_ message we're going to post. 468 UnlockFully(); 469 470 // As per the BeBook, if we've been called by a thread other than 471 // our own, the rest of the message queue has to get processed. So 472 // we put this in the queue, and when it shows up, we'll call Quit() 473 // from our own thread. 474 // QuitRequested() will not be called in this case. 475 PostMessage(_QUIT_); 476 477 // We have to wait until the looper is done processing any remaining messages. 478 status_t status; 479 while (wait_for_thread(thread, &status) == B_INTERRUPTED) 480 ; 481 } 482 483 PRINT(("BLooper::Quit() done\n")); 484 } 485 486 487 bool 488 BLooper::QuitRequested() 489 { 490 return true; 491 } 492 493 494 bool 495 BLooper::Lock() 496 { 497 // Defer to global _Lock(); see notes there 498 return _Lock(this, -1, B_INFINITE_TIMEOUT) == B_OK; 499 } 500 501 502 void 503 BLooper::Unlock() 504 { 505 PRINT(("BLooper::Unlock()\n")); 506 // Make sure we're locked to begin with 507 AssertLocked(); 508 509 // Decrement fOwnerCount 510 --fOwnerCount; 511 PRINT((" fOwnerCount now: %ld\n", fOwnerCount)); 512 // Check to see if the owner still wants a lock 513 if (fOwnerCount == 0) { 514 // Set fOwner to invalid thread_id (< 0) 515 fOwner = -1; 516 fCachedStack = 0; 517 518 #if DEBUG < 1 519 // Decrement requested lock count (using fAtomicCount for this) 520 int32 atomicCount = atomic_add(&fAtomicCount, -1); 521 PRINT((" fAtomicCount now: %ld\n", fAtomicCount)); 522 523 // Check if anyone is waiting for a lock 524 // and release if it's the case 525 if (atomicCount > 1) 526 #endif 527 release_sem(fLockSem); 528 } 529 PRINT(("BLooper::Unlock() done\n")); 530 } 531 532 533 bool 534 BLooper::IsLocked() const 535 { 536 if (!gLooperList.IsLooperValid(this)) { 537 // The looper is gone, so of course it's not locked 538 return false; 539 } 540 541 uint32 stack; 542 return ((uint32)&stack & ~(B_PAGE_SIZE - 1)) == fCachedStack 543 || find_thread(NULL) == fOwner; 544 } 545 546 547 status_t 548 BLooper::LockWithTimeout(bigtime_t timeout) 549 { 550 return _Lock(this, -1, timeout); 551 } 552 553 554 thread_id 555 BLooper::Thread() const 556 { 557 return fThread; 558 } 559 560 561 team_id 562 BLooper::Team() const 563 { 564 return BPrivate::current_team(); 565 } 566 567 568 BLooper* 569 BLooper::LooperForThread(thread_id thread) 570 { 571 return gLooperList.LooperForThread(thread); 572 } 573 574 575 thread_id 576 BLooper::LockingThread() const 577 { 578 return fOwner; 579 } 580 581 582 int32 583 BLooper::CountLocks() const 584 { 585 return fOwnerCount; 586 } 587 588 589 int32 590 BLooper::CountLockRequests() const 591 { 592 return fAtomicCount; 593 } 594 595 596 sem_id 597 BLooper::Sem() const 598 { 599 return fLockSem; 600 } 601 602 603 BHandler* 604 BLooper::ResolveSpecifier(BMessage* msg, int32 index, 605 BMessage* specifier, int32 form, const char* property) 606 { 607 /** 608 @note When I was first dumping the results of GetSupportedSuites() from 609 various classes, the use of the extra_data field was quite 610 mysterious to me. Then I dumped BApplication and compared the 611 result against the BeBook's docs for scripting BApplication. A 612 bunch of it isn't documented, but what is tipped me to the idea 613 that the extra_data is being used as a quick and dirty way to tell 614 what scripting "command" has been sent, e.g., for easy use in a 615 switch statement. Would certainly be a lot faster than a bunch of 616 string comparisons -- which wouldn't tell the whole story anyway, 617 because of the same name being used for multiple properties. 618 */ 619 BPropertyInfo propertyInfo(gLooperPropInfo); 620 uint32 data; 621 status_t err = B_OK; 622 const char* errMsg = ""; 623 if (propertyInfo.FindMatch(msg, index, specifier, form, property, &data) >= 0) { 624 switch (data) { 625 case BLOOPER_PROCESS_INTERNALLY: 626 return this; 627 628 case BLOOPER_HANDLER_BY_INDEX: 629 { 630 int32 index = specifier->FindInt32("index"); 631 if (form == B_REVERSE_INDEX_SPECIFIER) { 632 index = CountHandlers() - index; 633 } 634 BHandler* target = HandlerAt(index); 635 if (target) { 636 // Specifier has been fully handled 637 msg->PopSpecifier(); 638 return target; 639 } else { 640 err = B_BAD_INDEX; 641 errMsg = "handler index out of range"; 642 } 643 break; 644 } 645 646 default: 647 err = B_BAD_SCRIPT_SYNTAX; 648 errMsg = "Didn't understand the specifier(s)"; 649 break; 650 } 651 } else { 652 return BHandler::ResolveSpecifier(msg, index, specifier, form, 653 property); 654 } 655 656 BMessage reply(B_MESSAGE_NOT_UNDERSTOOD); 657 reply.AddInt32("error", err); 658 reply.AddString("message", errMsg); 659 msg->SendReply(&reply); 660 661 return NULL; 662 } 663 664 665 status_t 666 BLooper::GetSupportedSuites(BMessage* data) 667 { 668 if (data == NULL) 669 return B_BAD_VALUE; 670 671 status_t status = data->AddString("suites", "suite/vnd.Be-looper"); 672 if (status == B_OK) { 673 BPropertyInfo PropertyInfo(gLooperPropInfo); 674 status = data->AddFlat("messages", &PropertyInfo); 675 if (status == B_OK) 676 status = BHandler::GetSupportedSuites(data); 677 } 678 679 return status; 680 } 681 682 683 void 684 BLooper::AddCommonFilter(BMessageFilter* filter) 685 { 686 if (!filter) 687 return; 688 689 AssertLocked(); 690 691 if (filter->Looper()) { 692 debugger("A MessageFilter can only be used once."); 693 return; 694 } 695 696 if (!fCommonFilters) 697 fCommonFilters = new BList(FILTER_LIST_BLOCK_SIZE); 698 699 filter->SetLooper(this); 700 fCommonFilters->AddItem(filter); 701 } 702 703 704 bool 705 BLooper::RemoveCommonFilter(BMessageFilter* filter) 706 { 707 AssertLocked(); 708 709 if (!fCommonFilters) 710 return false; 711 712 bool result = fCommonFilters->RemoveItem(filter); 713 if (result) 714 filter->SetLooper(NULL); 715 716 return result; 717 } 718 719 720 void 721 BLooper::SetCommonFilterList(BList* filters) 722 { 723 // We have a somewhat serious problem here. It is entirely possible in R5 724 // to assign a given list of filters to *two* BLoopers simultaneously. This 725 // becomes problematic when the loopers are destroyed: the last looper 726 // destroyed will have a problem when it tries to delete a filter list that 727 // has already been deleted. In R5, this results in a general protection 728 // fault. We fix this by checking the filter list for ownership issues. 729 730 AssertLocked(); 731 732 BMessageFilter* filter; 733 if (filters) { 734 // Check for ownership issues 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&) 795 { 796 // Copy construction not allowed 797 } 798 799 800 BLooper& BLooper::operator=(const BLooper& ) 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, 817 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 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 AddHandler(this); 961 } 962 963 964 void 965 BLooper::AddMessage(BMessage* message) 966 { 967 _AddMessagePriv(message); 968 969 // wakeup looper when being called from other threads if necessary 970 if (find_thread(NULL) != Thread() 971 && fDirectTarget->Queue()->IsNextMessage(message) && port_count(fMsgPort) <= 0) { 972 // there is currently no message waiting, and we need to wakeup the looper 973 write_port_etc(fMsgPort, 0, NULL, 0, B_RELATIVE_TIMEOUT, 0); 974 } 975 } 976 977 978 void 979 BLooper::_AddMessagePriv(BMessage* msg) 980 { 981 // ToDo: if no target token is specified, set to preferred handler 982 // Others may want to peek into our message queue, so the preferred 983 // handler must be set correctly already if no token was given 984 985 fDirectTarget->Queue()->AddMessage(msg); 986 } 987 988 989 status_t 990 BLooper::_task0_(void *arg) 991 { 992 BLooper *looper = (BLooper *)arg; 993 994 PRINT(("LOOPER: _task0_()\n")); 995 996 if (looper->Lock()) { 997 PRINT(("LOOPER: looper locked\n")); 998 looper->task_looper(); 999 1000 delete looper; 1001 } 1002 1003 PRINT(("LOOPER: _task0_() done: thread %ld\n", find_thread(NULL))); 1004 return B_OK; 1005 } 1006 1007 1008 void * 1009 BLooper::ReadRawFromPort(int32 *msgCode, bigtime_t timeout) 1010 { 1011 PRINT(("BLooper::ReadRawFromPort()\n")); 1012 uint8 *buffer = NULL; 1013 ssize_t bufferSize; 1014 1015 do { 1016 bufferSize = port_buffer_size_etc(fMsgPort, B_RELATIVE_TIMEOUT, timeout); 1017 } while (bufferSize == B_INTERRUPTED); 1018 1019 if (bufferSize < B_OK) { 1020 PRINT(("BLooper::ReadRawFromPort(): failed: %ld\n", bufferSize)); 1021 return NULL; 1022 } 1023 1024 if (bufferSize > 0) 1025 buffer = (uint8 *)malloc(bufferSize); 1026 1027 // we don't want to wait again here, since that can only mean 1028 // that someone else has read our message and our bufferSize 1029 // is now probably wrong 1030 PRINT(("read_port()...\n")); 1031 bufferSize = read_port_etc(fMsgPort, msgCode, buffer, bufferSize, 1032 B_RELATIVE_TIMEOUT, 0); 1033 1034 if (bufferSize < B_OK) { 1035 free(buffer); 1036 return NULL; 1037 } 1038 1039 PRINT(("BLooper::ReadRawFromPort() read: %.4s, %p (%d bytes)\n", (char *)msgCode, buffer, bufferSize)); 1040 return buffer; 1041 } 1042 1043 1044 BMessage * 1045 BLooper::ReadMessageFromPort(bigtime_t timeout) 1046 { 1047 PRINT(("BLooper::ReadMessageFromPort()\n")); 1048 int32 msgCode; 1049 BMessage *message = NULL; 1050 1051 void *buffer = ReadRawFromPort(&msgCode, timeout); 1052 if (!buffer) 1053 return NULL; 1054 1055 message = ConvertToMessage(buffer, msgCode); 1056 free(buffer); 1057 1058 PRINT(("BLooper::ReadMessageFromPort() done: %p\n", message)); 1059 return message; 1060 } 1061 1062 1063 BMessage * 1064 BLooper::ConvertToMessage(void *buffer, int32 code) 1065 { 1066 PRINT(("BLooper::ConvertToMessage()\n")); 1067 if (!buffer) 1068 return NULL; 1069 1070 BMessage *message = new BMessage(); 1071 if (message->Unflatten((const char *)buffer) != B_OK) { 1072 PRINT(("BLooper::ConvertToMessage(): unflattening message failed\n")); 1073 delete message; 1074 message = NULL; 1075 } 1076 1077 PRINT(("BLooper::ConvertToMessage(): %p\n", message)); 1078 return message; 1079 } 1080 1081 1082 void 1083 BLooper::task_looper() 1084 { 1085 PRINT(("BLooper::task_looper()\n")); 1086 // Check that looper is locked (should be) 1087 AssertLocked(); 1088 // Unlock the looper 1089 Unlock(); 1090 1091 if (IsLocked()) 1092 debugger("looper must not be locked!"); 1093 1094 // loop: As long as we are not terminating. 1095 while (!fTerminating) { 1096 PRINT(("LOOPER: outer loop\n")); 1097 // TODO: timeout determination algo 1098 // Read from message port (how do we determine what the timeout is?) 1099 PRINT(("LOOPER: MessageFromPort()...\n")); 1100 BMessage *msg = MessageFromPort(); 1101 PRINT(("LOOPER: ...done\n")); 1102 1103 // Did we get a message? 1104 if (msg) 1105 _AddMessagePriv(msg); 1106 1107 // Get message count from port 1108 int32 msgCount = port_count(fMsgPort); 1109 for (int32 i = 0; i < msgCount; ++i) { 1110 // Read 'count' messages from port (so we will not block) 1111 // We use zero as our timeout since we know there is stuff there 1112 msg = MessageFromPort(0); 1113 if (msg) 1114 _AddMessagePriv(msg); 1115 } 1116 1117 // loop: As long as there are messages in the queue and the port is 1118 // empty... and we are not terminating, of course. 1119 bool dispatchNextMessage = true; 1120 while (!fTerminating && dispatchNextMessage) { 1121 PRINT(("LOOPER: inner loop\n")); 1122 // Get next message from queue (assign to fLastMessage) 1123 fLastMessage = fDirectTarget->Queue()->NextMessage(); 1124 1125 Lock(); 1126 1127 if (!fLastMessage) { 1128 // No more messages: Unlock the looper and terminate the 1129 // dispatch loop. 1130 dispatchNextMessage = false; 1131 } else { 1132 PRINT(("LOOPER: fLastMessage: 0x%lx: %.4s\n", fLastMessage->what, 1133 (char*)&fLastMessage->what)); 1134 DBG(fLastMessage->PrintToStream()); 1135 1136 // Get the target handler 1137 BHandler *handler = NULL; 1138 BMessage::Private messagePrivate(fLastMessage); 1139 bool usePreferred = messagePrivate.UsePreferredTarget(); 1140 1141 if (usePreferred) { 1142 PRINT(("LOOPER: use preferred target\n")); 1143 handler = fPreferred; 1144 if (handler == NULL) 1145 handler = this; 1146 } else { 1147 gDefaultTokens.GetToken(messagePrivate.GetTarget(), 1148 B_HANDLER_TOKEN, (void **)&handler); 1149 1150 // if this handler doesn't belong to us, we drop the message 1151 if (handler != NULL && handler->Looper() != this) 1152 handler = NULL; 1153 1154 PRINT(("LOOPER: use %ld, handler: %p, this: %p\n", 1155 messagePrivate.GetTarget(), handler, this)); 1156 } 1157 1158 // Is this a scripting message? (BMessage::HasSpecifiers()) 1159 if (handler != NULL && fLastMessage->HasSpecifiers()) { 1160 int32 index = 0; 1161 // Make sure the current specifier is kosher 1162 if (fLastMessage->GetCurrentSpecifier(&index) == B_OK) 1163 handler = resolve_specifier(handler, fLastMessage); 1164 } 1165 1166 if (handler) { 1167 // Do filtering 1168 handler = _TopLevelFilter(fLastMessage, handler); 1169 PRINT(("LOOPER: _TopLevelFilter(): %p\n", handler)); 1170 if (handler && handler->Looper() == this) 1171 DispatchMessage(fLastMessage, handler); 1172 } 1173 } 1174 1175 if (fTerminating) { 1176 // we leave the looper locked when we quit 1177 return; 1178 } 1179 1180 // Unlock the looper 1181 Unlock(); 1182 1183 // Delete the current message (fLastMessage) 1184 if (fLastMessage) { 1185 delete fLastMessage; 1186 fLastMessage = NULL; 1187 } 1188 1189 // Are any messages on the port? 1190 if (port_count(fMsgPort) > 0) { 1191 // Do outer loop 1192 dispatchNextMessage = false; 1193 } 1194 } 1195 } 1196 PRINT(("BLooper::task_looper() done\n")); 1197 } 1198 1199 1200 void 1201 BLooper::_QuitRequested(BMessage *msg) 1202 { 1203 bool isQuitting = QuitRequested(); 1204 1205 // We send a reply to the sender, when they're waiting for a reply or 1206 // if the request message contains a boolean "_shutdown_" field with value 1207 // true. In the latter case the message came from the registrar, asking 1208 // the application to shut down. 1209 bool shutdown; 1210 if (msg->IsSourceWaiting() 1211 || (msg->FindBool("_shutdown_", &shutdown) == B_OK && shutdown)) { 1212 BMessage replyMsg(B_REPLY); 1213 replyMsg.AddBool("result", isQuitting); 1214 replyMsg.AddInt32("thread", fThread); 1215 msg->SendReply(&replyMsg); 1216 } 1217 1218 if (isQuitting) 1219 Quit(); 1220 } 1221 1222 1223 bool 1224 BLooper::AssertLocked() const 1225 { 1226 if (!IsLocked()) { 1227 debugger("looper must be locked before proceeding\n"); 1228 return false; 1229 } 1230 1231 return true; 1232 } 1233 1234 1235 BHandler * 1236 BLooper::_TopLevelFilter(BMessage* msg, BHandler* target) 1237 { 1238 if (msg) { 1239 // Apply the common filters first 1240 target = _ApplyFilters(CommonFilterList(), msg, target); 1241 if (target) { 1242 if (target->Looper() != this) { 1243 debugger("Targeted handler does not belong to the looper."); 1244 target = NULL; 1245 } else { 1246 // Now apply handler-specific filters 1247 target = _HandlerFilter(msg, target); 1248 } 1249 } 1250 } 1251 1252 return target; 1253 } 1254 1255 1256 BHandler * 1257 BLooper::_HandlerFilter(BMessage* msg, BHandler* target) 1258 { 1259 // Keep running filters until our handler is NULL, or until the filtering 1260 // handler returns itself as the designated handler 1261 BHandler* previousTarget = NULL; 1262 while (target != NULL && target != previousTarget) { 1263 previousTarget = target; 1264 1265 target = _ApplyFilters(target->FilterList(), msg, target); 1266 if (target != NULL && target->Looper() != this) { 1267 debugger("Targeted handler does not belong to the looper."); 1268 target = NULL; 1269 } 1270 } 1271 1272 return target; 1273 } 1274 1275 1276 BHandler * 1277 BLooper::_ApplyFilters(BList* list, BMessage* msg, BHandler* target) 1278 { 1279 // This is where the action is! 1280 // Check the parameters 1281 if (!list || !msg) 1282 return target; 1283 1284 // For each filter in the provided list 1285 BMessageFilter* filter = NULL; 1286 for (int32 i = 0; i < list->CountItems(); ++i) { 1287 filter = (BMessageFilter*)list->ItemAt(i); 1288 1289 // Check command conditions 1290 if (filter->FiltersAnyCommand() || (filter->Command() == msg->what)) { 1291 // Check delivery conditions 1292 message_delivery delivery = filter->MessageDelivery(); 1293 bool dropped = msg->WasDropped(); 1294 if (delivery == B_ANY_DELIVERY 1295 || (delivery == B_DROPPED_DELIVERY && dropped) 1296 || (delivery == B_PROGRAMMED_DELIVERY && !dropped)) { 1297 // Check source conditions 1298 message_source source = filter->MessageSource(); 1299 bool remote = msg->IsSourceRemote(); 1300 if (source == B_ANY_SOURCE 1301 || (source == B_REMOTE_SOURCE && remote) 1302 || (source == B_LOCAL_SOURCE && !remote)) { 1303 // Are we using an "external" function? 1304 filter_result result; 1305 filter_hook func = filter->FilterFunction(); 1306 if (func) 1307 result = func(msg, &target, filter); 1308 else 1309 result = filter->Filter(msg, &target); 1310 1311 // Is further processing allowed? 1312 if (result == B_SKIP_MESSAGE) { 1313 // No; time to bail out 1314 return NULL; 1315 } 1316 } 1317 } 1318 } 1319 } 1320 1321 return target; 1322 } 1323 1324 1325 void 1326 BLooper::check_lock() 1327 { 1328 // This is a cheap variant of AssertLocked() 1329 // It is used in situations where it's clear that the looper is valid, 1330 // ie. from handlers 1331 uint32 stack; 1332 if (((uint32)&stack & ~(B_PAGE_SIZE - 1)) == fCachedStack 1333 || fOwner == find_thread(NULL)) 1334 return; 1335 1336 debugger("Looper must be locked."); 1337 } 1338 1339 1340 BHandler * 1341 BLooper::resolve_specifier(BHandler* target, BMessage* msg) 1342 { 1343 // Check params 1344 if (!target || !msg) 1345 return NULL; 1346 1347 int32 index; 1348 BMessage specifier; 1349 int32 form; 1350 const char* property; 1351 status_t err = B_OK; 1352 BHandler* newTarget = target; 1353 // Loop to deal with nested specifiers 1354 // (e.g., the 3rd button on the 4th view) 1355 do { 1356 err = msg->GetCurrentSpecifier(&index, &specifier, &form, &property); 1357 if (err) { 1358 BMessage reply(B_REPLY); 1359 reply.AddInt32("error", err); 1360 msg->SendReply(&reply); 1361 return NULL; 1362 } 1363 // Current target gets what was the new target 1364 target = newTarget; 1365 newTarget = target->ResolveSpecifier(msg, index, &specifier, form, 1366 property); 1367 // Check that new target is owned by looper; 1368 // use IndexOf() to avoid dereferencing newTarget 1369 // (possible race condition with object destruction 1370 // by another looper) 1371 if (!newTarget || IndexOf(newTarget) < 0) 1372 return NULL; 1373 1374 // Get current specifier index (may change in ResolveSpecifier()) 1375 err = msg->GetCurrentSpecifier(&index); 1376 } while (newTarget && newTarget != target && !err && index >= 0); 1377 1378 return newTarget; 1379 } 1380 1381 1382 void 1383 BLooper::UnlockFully() 1384 { 1385 AssertLocked(); 1386 1387 /** 1388 @note What we're doing here is completely undoing the current owner's lock 1389 on the looper. This is actually pretty easy, since the owner only 1390 has a single aquisition on the semaphore; every subsequent "lock" 1391 is just an increment to the owner count. The whole thing is quite 1392 similar to Unlock(), except that we clear the ownership variables, 1393 rather than merely decrementing them. 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