1 /* 2 * Copyright 2001-2006, Haiku, Inc. All Rights Reserved. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * Ingo Weinhold (bonefish@users.sf.net) 7 */ 8 9 10 #include <algorithm> 11 #include <new> 12 13 #include <Autolock.h> 14 #include <Message.h> 15 #include <MessagePrivate.h> 16 #include <Messenger.h> 17 #include <OS.h> 18 #include <RegistrarDefs.h> 19 20 #include "Debug.h" 21 #include "Event.h" 22 #include "EventQueue.h" 23 #include "MessageDeliverer.h" 24 #include "MessageRunnerManager.h" 25 26 using std::max; 27 using std::nothrow; 28 29 /*! \class MessageRunnerManager 30 \brief Manages the registrar side "shadows" of BMessageRunners. 31 32 The class features four methods to which the registrar application 33 dispatches the message runner specific request messages. 34 35 Each active message runner (i.e. one that still has messages to be sent) 36 is represented by a RunnerInfo that comprises all necessary information, 37 among these a RunnerEvent added to the event queue. When the event is 38 executed, it calls the _DoEvent() method, which in turn sends the message 39 runner message to the respective target and schedules the event for the 40 next time the message has to be sent (_ScheduleEvent()). 41 42 A couple of helper methods provide convenient access to the RunnerInfo 43 list (\a fRunnerInfos). A BLocker (\a fLock) and respective locking 44 methods are used to serialize the access to the member variables. 45 */ 46 47 /*! \var BList MessageRunnerManager::fRunnerInfos 48 \brief The list of RunnerInfos. 49 */ 50 51 /*! \var BLocker MessageRunnerManager::fLock 52 \brief A locker used to serialize the access to the object's variable 53 members. 54 */ 55 56 /*! \var EventQueue *MessageRunnerManager::fEventQueue 57 \brief Event queue used by the manager. 58 */ 59 60 /*! \var int32 MessageRunnerManager::fNextToken 61 \brief Next unused token for message runners. 62 */ 63 64 65 using namespace BPrivate; 66 67 //! The minimal time interval for message runners (1 us). 68 static const bigtime_t kMinimalTimeInterval = 1LL; 69 70 71 static bigtime_t 72 add_time(bigtime_t a, bigtime_t b) 73 { 74 // avoid a bigtime_t overflow 75 if (LONGLONG_MAX - b < a) 76 return LONGLONG_MAX; 77 else 78 return a + b; 79 } 80 81 82 // RunnerEvent 83 /*! \brief Event class used to by the message runner manager. 84 85 For each active message runner such an event is used. It invokes 86 MessageRunnerManager::_DoEvent() on execution. 87 */ 88 class MessageRunnerManager::RunnerEvent : public Event { 89 public: 90 /*! \brief Creates a new RunnerEvent. 91 \param manager The message runner manager. 92 \param info The RunnerInfo for the message runner. 93 */ 94 RunnerEvent(MessageRunnerManager *manager, RunnerInfo *info) 95 : Event(false), 96 fManager(manager), 97 fInfo(info) 98 { 99 } 100 101 /*! \brief Hook method invoked when the event is executed. 102 103 Implements Event. Calls MessageRunnerManager::_DoEvent(). 104 105 \param queue The event queue executing the event. 106 \return \c true, if the object shall be deleted, \c false otherwise. 107 */ 108 virtual bool Do(EventQueue *queue) 109 { 110 return fManager->_DoEvent(fInfo); 111 } 112 113 private: 114 MessageRunnerManager *fManager; //!< The message runner manager. 115 RunnerInfo *fInfo; //!< The message runner info. 116 }; 117 118 119 // RunnerInfo 120 /*! \brief Contains all needed information about an active message runner. 121 */ 122 struct MessageRunnerManager::RunnerInfo { 123 /*! \brief Creates a new RunnerInfo. 124 \param team The team owning the message runner. 125 \param token The unique token associated with the message runner. 126 \param target The target the message shall be sent to. 127 \param message The message to be sent to the target. 128 \param interval The message runner's time interval. 129 \param count The number of times the message shall be sent. 130 \param replyTarget The reply target for the delivered message. 131 */ 132 RunnerInfo(team_id team, int32 token, BMessenger target, BMessage *message, 133 bigtime_t interval, int32 count, BMessenger replyTarget) 134 : team(team), 135 token(token), 136 target(target), 137 message(message), 138 interval(interval), 139 count(count), 140 replyTarget(replyTarget), 141 time(0), 142 event(NULL), 143 rescheduled(false) 144 { 145 } 146 147 /*! \brief Frees all resources associated with the object. 148 149 The message and the event are delete. 150 */ 151 ~RunnerInfo() 152 { 153 delete message; 154 delete event; 155 } 156 157 /*! \brief Delivers the message to the respective target. 158 \return \c B_OK, if the message has successfully been delivered or 159 the target does still exist and its message port is full, 160 an error code otherwise. 161 */ 162 status_t DeliverMessage() 163 { 164 if (count > 0) 165 count--; 166 167 // set the reply target 168 BMessage::Private(message).SetReply(replyTarget); 169 170 // deliver the message: We use the MessageDeliverer to allow the 171 // message to be delivered, even if the target port is temporarily 172 // full. For periodic message runners, that have to deliver further 173 // messages, we restrict the delivery timeout to the message interval. 174 status_t error; 175 if (count > 0) { 176 error = MessageDeliverer::Default()->DeliverMessage(message, target, 177 interval); 178 } else { 179 error = MessageDeliverer::Default()->DeliverMessage(message, 180 target); 181 } 182 183 // B_WOULD_BLOCK is as good as B_OK. We return an error only, if 184 // there are serious problems with the target, i.e. if it doesn't 185 // exist anymore for instance. A full message port is harmless. 186 if (error == B_WOULD_BLOCK) 187 error = B_OK; 188 return error; 189 } 190 191 team_id team; //!< The team owning the message runner. 192 int32 token; /*!< The unique token associated with the 193 message runner. */ 194 BMessenger target; //!< The target the message shall be sent to. 195 BMessage *message; //!< The message to be sent to the target. 196 bigtime_t interval; //!< The message runner's time interval. 197 int32 count; /*!< The number of times the message shall be 198 sent. */ 199 BMessenger replyTarget; /*!< The reply target for the delivered 200 message. */ 201 bigtime_t time; /*!< Time at which the next message will be 202 sent. */ 203 RunnerEvent *event; //!< Runner event for the message runner. 204 bool rescheduled; /*!< Set to \c true when the event has been 205 started to be executed while it was 206 rescheduled. */ 207 }; 208 209 210 // constructor 211 /*! \brief Creates a new MessageRunnerManager. 212 \param eventQueue The EventQueue the manager shall use. 213 */ 214 MessageRunnerManager::MessageRunnerManager(EventQueue *eventQueue) 215 : fRunnerInfos(), 216 fLock(), 217 fEventQueue(eventQueue), 218 fNextToken(0) 219 { 220 } 221 222 // destructor 223 /*! \brief Frees all resources associated with the object. 224 225 The manager's event queue must already have been stopped 226 (EventQueue::Die()). 227 */ 228 MessageRunnerManager::~MessageRunnerManager() 229 { 230 // The event queue should already be stopped, but must still exist. 231 // If it is still running and an event gets executed after we've locked 232 // ourselves, then it will access an already deleted manager. 233 BAutolock _lock(fLock); 234 for (int32 i = 0; RunnerInfo *info = _InfoAt(i); i++) { 235 if (!fEventQueue->RemoveEvent(info->event)) 236 info->event = NULL; 237 delete info; 238 } 239 fRunnerInfos.MakeEmpty(); 240 } 241 242 // HandleRegisterRunner 243 /*! \brief Handles a registration request (BMessageRunner::InitData()). 244 \param request The request message. 245 */ 246 void 247 MessageRunnerManager::HandleRegisterRunner(BMessage *request) 248 { 249 FUNCTION_START(); 250 251 BAutolock _lock(fLock); 252 status_t error = B_OK; 253 // get the parameters 254 team_id team; 255 BMessenger target; 256 // TODO: This should be a "new (nothrow)", but R5's BMessage doesn't 257 // define that version. 258 BMessage *message = new BMessage; 259 bigtime_t interval; 260 int32 count; 261 BMessenger replyTarget; 262 if (error == B_OK && message == NULL) 263 error = B_NO_MEMORY; 264 if (error == B_OK && request->FindInt32("team", &team) != B_OK) 265 error = B_BAD_VALUE; 266 if (error == B_OK && request->FindMessenger("target", &target) != B_OK) 267 error = B_BAD_VALUE; 268 if (error == B_OK && request->FindMessage("message", message) != B_OK) 269 error = B_BAD_VALUE; 270 if (error == B_OK && request->FindInt64("interval", &interval) != B_OK) 271 error = B_BAD_VALUE; 272 if (error == B_OK && request->FindInt32("count", &count) != B_OK) 273 error = B_BAD_VALUE; 274 if (error == B_OK 275 && request->FindMessenger("reply_target", &replyTarget) != B_OK) { 276 error = B_BAD_VALUE; 277 } 278 279 // check the parameters 280 if (error == B_OK && count == 0) 281 error = B_BAD_VALUE; 282 283 // add a new runner info 284 RunnerInfo *info = NULL; 285 if (error == B_OK) { 286 interval = max(interval, kMinimalTimeInterval); 287 info = new(nothrow) RunnerInfo(team, _NextToken(), target, message, 288 interval, count, replyTarget); 289 if (info) { 290 info->time = system_time(); 291 if (!_AddInfo(info)) 292 error = B_NO_MEMORY; 293 } else 294 error = B_NO_MEMORY; 295 } 296 297 // create a new event 298 RunnerEvent *event = NULL; 299 if (error == B_OK) { 300 event = new(nothrow) RunnerEvent(this, info); 301 if (event) { 302 info->event = event; 303 if (!_ScheduleEvent(info)) 304 error = B_NO_MEMORY; // TODO: The only possible reason? 305 } else 306 error = B_NO_MEMORY; 307 } 308 309 // cleanup on error 310 if (error != B_OK) { 311 if (info) { 312 _RemoveInfo(info); 313 delete info; 314 } 315 delete message; 316 } 317 318 // reply to the request 319 if (error == B_OK) { 320 BMessage reply(B_REG_SUCCESS); 321 reply.AddInt32("token", info->token); 322 request->SendReply(&reply); 323 } else { 324 BMessage reply(B_REG_ERROR); 325 reply.AddInt32("error", error); 326 request->SendReply(&reply); 327 } 328 329 FUNCTION_END(); 330 } 331 332 // HandleUnregisterRunner 333 /*! \brief Handles an unregistration request (BMessageRunner destructor). 334 \param request The request message. 335 */ 336 void 337 MessageRunnerManager::HandleUnregisterRunner(BMessage *request) 338 { 339 FUNCTION_START(); 340 341 BAutolock _lock(fLock); 342 status_t error = B_OK; 343 // get the parameters 344 int32 token; 345 if (error == B_OK && request->FindInt32("token", &token) != B_OK) 346 error = B_BAD_VALUE; 347 // find and delete the runner info 348 if (error == B_OK) { 349 if (RunnerInfo *info = _InfoForToken(token)) 350 _DeleteInfo(info, false); 351 else 352 error = B_BAD_VALUE; 353 } 354 // reply to the request 355 if (error == B_OK) { 356 BMessage reply(B_REG_SUCCESS); 357 request->SendReply(&reply); 358 } else { 359 BMessage reply(B_REG_ERROR); 360 reply.AddInt32("error", error); 361 request->SendReply(&reply); 362 } 363 364 FUNCTION_END(); 365 } 366 367 // HandleSetRunnerParams 368 /*! \brief Handles an parameter change request (BMessageRunner::SetParams()). 369 \param request The request message. 370 */ 371 void 372 MessageRunnerManager::HandleSetRunnerParams(BMessage *request) 373 { 374 FUNCTION_START(); 375 376 BAutolock _lock(fLock); 377 status_t error = B_OK; 378 // get the parameters 379 int32 token; 380 bigtime_t interval; 381 int32 count; 382 bool setInterval = false; 383 bool setCount = false; 384 if (error == B_OK && request->FindInt32("token", &token) != B_OK) 385 error = B_BAD_VALUE; 386 if (error == B_OK && request->FindInt64("interval", &interval) == B_OK) 387 setInterval = true; 388 if (error == B_OK && request->FindInt32("count", &count) == B_OK) 389 setCount = true; 390 391 // find the runner info 392 RunnerInfo *info = NULL; 393 if (error == B_OK) { 394 info = _InfoForToken(token); 395 if (!info) { 396 // TODO: At this point, the runner could have been deleted already. 397 // Since setting its parameters at this point should still be 398 // valid, we'd have to recreate it. 399 // (Even though the documentation in *our* BMessageRunner 400 // implementation specifically denies the possibility of setting 401 // the runner's parameters at this point, it would still be nice 402 // to allow this.) 403 error = B_BAD_VALUE; 404 } 405 } 406 407 // set the new values 408 if (error == B_OK) { 409 bool eventRemoved = false; 410 bool deleteInfo = false; 411 // count 412 if (setCount) { 413 if (count == 0) 414 deleteInfo = true; 415 else 416 info->count = count; 417 } 418 // interval 419 if (setInterval) { 420 eventRemoved = fEventQueue->RemoveEvent(info->event); 421 if (!eventRemoved) 422 info->rescheduled = true; 423 interval = max(interval, kMinimalTimeInterval); 424 info->interval = interval; 425 info->time = system_time(); 426 if (!_ScheduleEvent(info)) 427 error = B_NO_MEMORY; // TODO: The only possible reason? 428 } 429 // remove and delete the info on error 430 if (error != B_OK || deleteInfo) 431 _DeleteInfo(info, eventRemoved); 432 } 433 434 // reply to the request 435 if (error == B_OK) { 436 BMessage reply(B_REG_SUCCESS); 437 request->SendReply(&reply); 438 } else { 439 BMessage reply(B_REG_ERROR); 440 reply.AddInt32("error", error); 441 request->SendReply(&reply); 442 } 443 444 FUNCTION_END(); 445 } 446 447 // HandleGetRunnerInfo 448 /*! \brief Handles an get info request (BMessageRunner::GetInfo()). 449 \param request The request message. 450 */ 451 void 452 MessageRunnerManager::HandleGetRunnerInfo(BMessage *request) 453 { 454 FUNCTION_START(); 455 456 BAutolock _lock(fLock); 457 status_t error = B_OK; 458 // get the parameters 459 int32 token; 460 if (error == B_OK && request->FindInt32("token", &token) != B_OK) 461 error = B_BAD_VALUE; 462 // find the runner info 463 RunnerInfo *info = NULL; 464 if (error == B_OK) { 465 info = _InfoForToken(token); 466 if (!info) 467 error = B_BAD_VALUE; 468 } 469 // reply to the request 470 if (error == B_OK) { 471 BMessage reply(B_REG_SUCCESS); 472 reply.AddInt64("interval", info->interval); 473 reply.AddInt32("count", info->count); 474 request->SendReply(&reply); 475 } else { 476 BMessage reply(B_REG_ERROR); 477 reply.AddInt32("error", error); 478 request->SendReply(&reply); 479 } 480 481 FUNCTION_END(); 482 } 483 484 // Lock 485 /*! \brief Locks the manager. 486 \return \c true, if locked successfully, \c false otherwise. 487 */ 488 bool 489 MessageRunnerManager::Lock() 490 { 491 return fLock.Lock(); 492 } 493 494 // Unlock 495 /*! \brief Unlocks the manager. 496 */ 497 void 498 MessageRunnerManager::Unlock() 499 { 500 fLock.Unlock(); 501 } 502 503 // _AddInfo 504 /*! \brief Adds a RunnerInfo to the list of RunnerInfos. 505 506 \note The manager must be locked. 507 508 \param info The RunnerInfo to be added. 509 \return \c true, if added successfully, \c false otherwise. 510 */ 511 bool 512 MessageRunnerManager::_AddInfo(RunnerInfo *info) 513 { 514 return fRunnerInfos.AddItem(info); 515 } 516 517 // _RemoveInfo 518 /*! \brief Removes a RunnerInfo from the list of RunnerInfos. 519 520 \note The manager must be locked. 521 522 \param info The RunnerInfo to be removed. 523 \return \c true, if removed successfully, \c false, if the list doesn't 524 contain the supplied info. 525 */ 526 bool 527 MessageRunnerManager::_RemoveInfo(RunnerInfo *info) 528 { 529 return fRunnerInfos.RemoveItem(info); 530 } 531 532 // _RemoveInfo 533 /*! \brief Removes a RunnerInfo at a given index from the list of RunnerInfos. 534 535 \note The manager must be locked. 536 537 \param index The index of the RunnerInfo to be removed. 538 \return \c true, if removed successfully, \c false, if the supplied index 539 is out of range. 540 */ 541 MessageRunnerManager::RunnerInfo* 542 MessageRunnerManager::_RemoveInfo(int32 index) 543 { 544 return (RunnerInfo*)fRunnerInfos.RemoveItem(index); 545 } 546 547 // _RemoveInfoWithToken 548 /*! \brief Removes a RunnerInfo with a specified token from the list of 549 RunnerInfos. 550 551 \note The manager must be locked. 552 553 \param token The token identifying the RunnerInfo to be removed. 554 \return \c true, if removed successfully, \c false, if the list doesn't 555 contain an info with the supplied token. 556 */ 557 MessageRunnerManager::RunnerInfo* 558 MessageRunnerManager::_RemoveInfoWithToken(int32 token) 559 { 560 RunnerInfo *info = NULL; 561 int32 index = _IndexOfToken(token); 562 if (index >= 0) 563 info = _RemoveInfo(index); 564 return info; 565 } 566 567 // _DeleteInfo 568 /*! \brief Removes a RunnerInfo from the list of RunnerInfos and deletes it. 569 570 \note The manager must be locked. 571 572 \param index The index of the RunnerInfo to be deleted. 573 \return \c true, if removed and deleted successfully, \c false, if the 574 list doesn't contain the supplied info. 575 */ 576 bool 577 MessageRunnerManager::_DeleteInfo(RunnerInfo *info, bool eventRemoved) 578 { 579 bool result = _RemoveInfo(info); 580 if (result) { 581 // If the event is not in the event queue and has not been removed 582 // just before, then it is in progress. It will delete itself. 583 if (!eventRemoved && !fEventQueue->RemoveEvent(info->event)) 584 info->event = NULL; 585 delete info; 586 } 587 return result; 588 } 589 590 // _CountInfos 591 /*! \brief Returns the number of RunnerInfos in the list of RunnerInfos. 592 593 \note The manager must be locked. 594 595 \return Returns the number of RunnerInfos in the list of RunnerInfos. 596 */ 597 int32 598 MessageRunnerManager::_CountInfos() const 599 { 600 return fRunnerInfos.CountItems(); 601 } 602 603 // _InfoAt 604 /*! \brief Returns the RunnerInfo at the specified index in the list of 605 RunnerInfos. 606 607 \note The manager must be locked. 608 609 \param index The index of the RunnerInfo to be returned. 610 \return The runner info at the specified index, or \c NULL, if the index 611 is out of range. 612 */ 613 MessageRunnerManager::RunnerInfo* 614 MessageRunnerManager::_InfoAt(int32 index) const 615 { 616 return (RunnerInfo*)fRunnerInfos.ItemAt(index); 617 } 618 619 // _InfoForToken 620 /*! \brief Returns the RunnerInfo with the specified index. 621 622 \note The manager must be locked. 623 624 \param token The token identifying the RunnerInfo to be returned. 625 \return The runner info at the specified index, or \c NULL, if the list 626 doesn't contain an info with the specified token. 627 */ 628 MessageRunnerManager::RunnerInfo* 629 MessageRunnerManager::_InfoForToken(int32 token) const 630 { 631 return _InfoAt(_IndexOfToken(token)); 632 } 633 634 // _IndexOf 635 /*! \brief Returns the index of the supplied RunnerInfo in the list of 636 RunnerInfos. 637 638 \note The manager must be locked. 639 640 \param info The RunnerInfo whose index shall be returned. 641 \return The index of the supplied RunnerInfo, or -1, if the list doesn't 642 contain the supplied info. 643 */ 644 int32 645 MessageRunnerManager::_IndexOf(RunnerInfo *info) const 646 { 647 return fRunnerInfos.IndexOf(info); 648 } 649 650 // _IndexOfToken 651 /*! \brief Returns the index of the RunnerInfo identified by the supplied 652 token in the list of RunnerInfos. 653 654 \note The manager must be locked. 655 656 \param token The token identifying the RunnerInfo whose index shall be 657 returned. 658 \return The index of the requested RunnerInfo, or -1, if the list doesn't 659 contain an info with the supplied token. 660 */ 661 int32 662 MessageRunnerManager::_IndexOfToken(int32 token) const 663 { 664 for (int32 i = 0; RunnerInfo *info = _InfoAt(i); i++) { 665 if (info->token == token) 666 return i; 667 } 668 return -1; 669 } 670 671 // _DoEvent 672 /*! \brief Invoked when a message runner's event is executed. 673 674 If the message runner info is still valid and the event was not just 675 rescheduled, the message is delivered to the message runner's target 676 and the event is rescheduled. 677 678 \param info The message runner's info. 679 \return \c true, if the event object shall be deleted, \c false otherwise. 680 */ 681 bool 682 MessageRunnerManager::_DoEvent(RunnerInfo *info) 683 { 684 FUNCTION_START(); 685 686 BAutolock _lock(fLock); 687 bool deleteEvent = false; 688 // first check whether the info does still exist 689 if (_lock.IsLocked() && _IndexOf(info) >= 0) { 690 // If the event has been rescheduled after being removed from the 691 // queue for execution, it needs to be ignored. This may happen, when 692 // the interval is modified. 693 if (info->rescheduled) 694 info->rescheduled = false; 695 else { 696 // send the message 697 bool success = (info->DeliverMessage() == B_OK); 698 // reschedule the event 699 if (success) 700 success = _ScheduleEvent(info); 701 702 // clean up, if the message delivery of the rescheduling failed 703 // (or the runner had already fulfilled its job) 704 if (!success) { 705 deleteEvent = true; 706 info->event = NULL; 707 _RemoveInfo(info); 708 delete info; 709 } 710 } 711 } else { 712 // The info is no more. That means it had been removed after the 713 // event was removed from the event queue, but before we could acquire 714 // the lock. Simply delete the event. 715 deleteEvent = true; 716 } 717 718 FUNCTION_END(); 719 720 return deleteEvent; 721 } 722 723 // _ScheduleEvent 724 /*! \brief Schedules the event for a message runner for the next time a 725 message has to be sent. 726 727 \note The manager must be locked. 728 729 \param info The message runner's info. 730 \return \c true, if the event successfully been rescheduled, \c false, 731 if either all messages have already been sent or the event queue 732 doesn't allow adding the event (e.g. due to insufficient memory). 733 */ 734 bool 735 MessageRunnerManager::_ScheduleEvent(RunnerInfo *info) 736 { 737 bool scheduled = false; 738 // calculate next event time 739 if (info->count != 0) { 740 info->time = add_time(info->time, info->interval); 741 742 // For runners without a count limit, we skip messages, if we're already 743 // late. 744 bigtime_t now = system_time(); 745 if (info->time < now && info->count < 0) { 746 // keep the remainder modulo interval 747 info->time = add_time(now, 748 info->interval - (now - info->time) % info->interval); 749 } 750 751 info->event->SetTime(info->time); 752 scheduled = fEventQueue->AddEvent(info->event); 753 754 PRINT("runner %" B_PRId32 " (%" B_PRId64 ", %" B_PRId32 ") rescheduled: %d, " 755 "time: %" B_PRId64 ", now: %" B_PRId64 "\n", info->token, info->interval, 756 info->count, scheduled, info->time, system_time()); 757 } 758 return scheduled; 759 } 760 761 // _NextToken 762 /*! \brief Returns a new unused message runner token. 763 764 \note The manager must be locked. 765 766 \return A new unused message runner token. 767 */ 768 int32 769 MessageRunnerManager::_NextToken() 770 { 771 return fNextToken++; 772 } 773 774