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