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