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