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