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