2 * Copyright 2001-2006, Haiku, Inc. All Rights Reserved.
3 * Distributed under the terms of the MIT License.
6 * Ingo Weinhold (bonefish@users.sf.net)
15 #include <MessagePrivate.h>
16 #include <Messenger.h>
18 #include <RegistrarDefs.h>
22 #include "EventQueue.h"
23 #include "MessageDeliverer.h"
24 #include "MessageRunnerManager.h"
29 /*! \class MessageRunnerManager
30 \brief Manages the registrar side "shadows" of BMessageRunners.
32 The class features four methods to which the registrar application
33 dispatches the message runner specific request messages.
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()).
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.
47 /*! \var BList MessageRunnerManager::fRunnerInfos
48 \brief The list of RunnerInfos.
51 /*! \var BLocker MessageRunnerManager::fLock
52 \brief A locker used to serialize the access to the object's variable
56 /*! \var EventQueue *MessageRunnerManager::fEventQueue
57 \brief Event queue used by the manager.
60 /*! \var int32 MessageRunnerManager::fNextToken
61 \brief Next unused token for message runners.
65 using namespace BPrivate
;
67 //! The minimal time interval for message runners (50 ms).
68 static const bigtime_t kMininalTimeInterval
= 50000LL;
72 add_time(bigtime_t a
, bigtime_t b
)
74 // avoid a bigtime_t overflow
75 if (LONGLONG_MAX
- b
< a
)
83 /*! \brief Event class used to by the message runner manager.
85 For each active message runner such an event is used. It invokes
86 MessageRunnerManager::_DoEvent() on execution.
88 class MessageRunnerManager::RunnerEvent
: public Event
{
90 /*! \brief Creates a new RunnerEvent.
91 \param manager The message runner manager.
92 \param info The RunnerInfo for the message runner.
94 RunnerEvent(MessageRunnerManager
*manager
, RunnerInfo
*info
)
101 /*! \brief Hook method invoked when the event is executed.
103 Implements Event. Calls MessageRunnerManager::_DoEvent().
105 \param queue The event queue executing the event.
106 \return \c true, if the object shall be deleted, \c false otherwise.
108 virtual bool Do(EventQueue
*queue
)
110 return fManager
->_DoEvent(fInfo
);
114 MessageRunnerManager
*fManager
; //!< The message runner manager.
115 RunnerInfo
*fInfo
; //!< The message runner info.
120 /*! \brief Contains all needed information about an active message runner.
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.
132 RunnerInfo(team_id team
, int32 token
, BMessenger target
, BMessage
*message
,
133 bigtime_t interval
, int32 count
, BMessenger replyTarget
)
140 replyTarget(replyTarget
),
147 /*! \brief Frees all resources associated with the object.
149 The message and the event are delete.
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.
162 status_t
DeliverMessage()
167 // set the reply target
168 BMessage::Private(message
).SetReply(replyTarget
);
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.
176 error
= MessageDeliverer::Default()->DeliverMessage(message
, target
,
179 error
= MessageDeliverer::Default()->DeliverMessage(message
,
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
)
191 team_id team
; //!< The team owning the message runner.
192 int32 token
; /*!< The unique token associated with the
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
199 BMessenger replyTarget
; /*!< The reply target for the delivered
201 bigtime_t time
; /*!< Time at which the next message will be
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
211 /*! \brief Creates a new MessageRunnerManager.
212 \param eventQueue The EventQueue the manager shall use.
214 MessageRunnerManager::MessageRunnerManager(EventQueue
*eventQueue
)
217 fEventQueue(eventQueue
),
223 /*! \brief Frees all resources associated with the object.
225 The manager's event queue must already have been stopped
228 MessageRunnerManager::~MessageRunnerManager()
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
))
239 fRunnerInfos
.MakeEmpty();
242 // HandleRegisterRunner
243 /*! \brief Handles a registration request (BMessageRunner::InitData()).
244 \param request The request message.
247 MessageRunnerManager::HandleRegisterRunner(BMessage
*request
)
251 BAutolock
_lock(fLock
);
252 status_t error
= B_OK
;
253 // get the parameters
256 // TODO: This should be a "new (nothrow)", but R5's BMessage doesn't
257 // define that version.
258 BMessage
*message
= new BMessage
;
261 BMessenger replyTarget
;
262 if (error
== B_OK
&& message
== NULL
)
264 if (error
== B_OK
&& request
->FindInt32("team", &team
) != B_OK
)
266 if (error
== B_OK
&& request
->FindMessenger("target", &target
) != B_OK
)
268 if (error
== B_OK
&& request
->FindMessage("message", message
) != B_OK
)
270 if (error
== B_OK
&& request
->FindInt64("interval", &interval
) != B_OK
)
272 if (error
== B_OK
&& request
->FindInt32("count", &count
) != B_OK
)
275 && request
->FindMessenger("reply_target", &replyTarget
) != B_OK
) {
279 // check the parameters
280 if (error
== B_OK
&& count
== 0)
283 // add a new runner info
284 RunnerInfo
*info
= NULL
;
286 interval
= max(interval
, kMininalTimeInterval
);
287 info
= new(nothrow
) RunnerInfo(team
, _NextToken(), target
, message
,
288 interval
, count
, replyTarget
);
290 info
->time
= system_time();
297 // create a new event
298 RunnerEvent
*event
= NULL
;
300 event
= new(nothrow
) RunnerEvent(this, info
);
303 if (!_ScheduleEvent(info
))
304 error
= B_NO_MEMORY
; // TODO: The only possible reason?
318 // reply to the request
320 BMessage
reply(B_REG_SUCCESS
);
321 reply
.AddInt32("token", info
->token
);
322 request
->SendReply(&reply
);
324 BMessage
reply(B_REG_ERROR
);
325 reply
.AddInt32("error", error
);
326 request
->SendReply(&reply
);
332 // HandleUnregisterRunner
333 /*! \brief Handles an unregistration request (BMessageRunner destructor).
334 \param request The request message.
337 MessageRunnerManager::HandleUnregisterRunner(BMessage
*request
)
341 BAutolock
_lock(fLock
);
342 status_t error
= B_OK
;
343 // get the parameters
345 if (error
== B_OK
&& request
->FindInt32("token", &token
) != B_OK
)
347 // find and delete the runner info
349 if (RunnerInfo
*info
= _InfoForToken(token
))
350 _DeleteInfo(info
, false);
354 // reply to the request
356 BMessage
reply(B_REG_SUCCESS
);
357 request
->SendReply(&reply
);
359 BMessage
reply(B_REG_ERROR
);
360 reply
.AddInt32("error", error
);
361 request
->SendReply(&reply
);
367 // HandleSetRunnerParams
368 /*! \brief Handles an parameter change request (BMessageRunner::SetParams()).
369 \param request The request message.
372 MessageRunnerManager::HandleSetRunnerParams(BMessage
*request
)
376 BAutolock
_lock(fLock
);
377 status_t error
= B_OK
;
378 // get the parameters
382 bool setInterval
= false;
383 bool setCount
= false;
384 if (error
== B_OK
&& request
->FindInt32("token", &token
) != B_OK
)
386 if (error
== B_OK
&& request
->FindInt64("interval", &interval
) == B_OK
)
388 if (error
== B_OK
&& request
->FindInt32("count", &count
) == B_OK
)
391 // find the runner info
392 RunnerInfo
*info
= NULL
;
394 info
= _InfoForToken(token
);
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
407 // set the new values
409 bool eventRemoved
= false;
410 bool deleteInfo
= false;
420 eventRemoved
= fEventQueue
->RemoveEvent(info
->event
);
422 info
->rescheduled
= true;
423 interval
= max(interval
, kMininalTimeInterval
);
424 info
->interval
= interval
;
425 info
->time
= system_time();
426 if (!_ScheduleEvent(info
))
427 error
= B_NO_MEMORY
; // TODO: The only possible reason?
429 // remove and delete the info on error
430 if (error
!= B_OK
|| deleteInfo
)
431 _DeleteInfo(info
, eventRemoved
);
434 // reply to the request
436 BMessage
reply(B_REG_SUCCESS
);
437 request
->SendReply(&reply
);
439 BMessage
reply(B_REG_ERROR
);
440 reply
.AddInt32("error", error
);
441 request
->SendReply(&reply
);
447 // HandleGetRunnerInfo
448 /*! \brief Handles an get info request (BMessageRunner::GetInfo()).
449 \param request The request message.
452 MessageRunnerManager::HandleGetRunnerInfo(BMessage
*request
)
456 BAutolock
_lock(fLock
);
457 status_t error
= B_OK
;
458 // get the parameters
460 if (error
== B_OK
&& request
->FindInt32("token", &token
) != B_OK
)
462 // find the runner info
463 RunnerInfo
*info
= NULL
;
465 info
= _InfoForToken(token
);
469 // reply to the request
471 BMessage
reply(B_REG_SUCCESS
);
472 reply
.AddInt64("interval", info
->interval
);
473 reply
.AddInt32("count", info
->count
);
474 request
->SendReply(&reply
);
476 BMessage
reply(B_REG_ERROR
);
477 reply
.AddInt32("error", error
);
478 request
->SendReply(&reply
);
485 /*! \brief Locks the manager.
486 \return \c true, if locked successfully, \c false otherwise.
489 MessageRunnerManager::Lock()
495 /*! \brief Unlocks the manager.
498 MessageRunnerManager::Unlock()
504 /*! \brief Adds a RunnerInfo to the list of RunnerInfos.
506 \note The manager must be locked.
508 \param info The RunnerInfo to be added.
509 \return \c true, if added successfully, \c false otherwise.
512 MessageRunnerManager::_AddInfo(RunnerInfo
*info
)
514 return fRunnerInfos
.AddItem(info
);
518 /*! \brief Removes a RunnerInfo from the list of RunnerInfos.
520 \note The manager must be locked.
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.
527 MessageRunnerManager::_RemoveInfo(RunnerInfo
*info
)
529 return fRunnerInfos
.RemoveItem(info
);
533 /*! \brief Removes a RunnerInfo at a given index from the list of RunnerInfos.
535 \note The manager must be locked.
537 \param index The index of the RunnerInfo to be removed.
538 \return \c true, if removed successfully, \c false, if the supplied index
541 MessageRunnerManager::RunnerInfo
*
542 MessageRunnerManager::_RemoveInfo(int32 index
)
544 return (RunnerInfo
*)fRunnerInfos
.RemoveItem(index
);
547 // _RemoveInfoWithToken
548 /*! \brief Removes a RunnerInfo with a specified token from the list of
551 \note The manager must be locked.
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.
557 MessageRunnerManager::RunnerInfo
*
558 MessageRunnerManager::_RemoveInfoWithToken(int32 token
)
560 RunnerInfo
*info
= NULL
;
561 int32 index
= _IndexOfToken(token
);
563 info
= _RemoveInfo(index
);
568 /*! \brief Removes a RunnerInfo from the list of RunnerInfos and deletes it.
570 \note The manager must be locked.
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.
577 MessageRunnerManager::_DeleteInfo(RunnerInfo
*info
, bool eventRemoved
)
579 bool result
= _RemoveInfo(info
);
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
))
591 /*! \brief Returns the number of RunnerInfos in the list of RunnerInfos.
593 \note The manager must be locked.
595 \return Returns the number of RunnerInfos in the list of RunnerInfos.
598 MessageRunnerManager::_CountInfos() const
600 return fRunnerInfos
.CountItems();
604 /*! \brief Returns the RunnerInfo at the specified index in the list of
607 \note The manager must be locked.
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
613 MessageRunnerManager::RunnerInfo
*
614 MessageRunnerManager::_InfoAt(int32 index
) const
616 return (RunnerInfo
*)fRunnerInfos
.ItemAt(index
);
620 /*! \brief Returns the RunnerInfo with the specified index.
622 \note The manager must be locked.
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.
628 MessageRunnerManager::RunnerInfo
*
629 MessageRunnerManager::_InfoForToken(int32 token
) const
631 return _InfoAt(_IndexOfToken(token
));
635 /*! \brief Returns the index of the supplied RunnerInfo in the list of
638 \note The manager must be locked.
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.
645 MessageRunnerManager::_IndexOf(RunnerInfo
*info
) const
647 return fRunnerInfos
.IndexOf(info
);
651 /*! \brief Returns the index of the RunnerInfo identified by the supplied
652 token in the list of RunnerInfos.
654 \note The manager must be locked.
656 \param token The token identifying the RunnerInfo whose index shall be
658 \return The index of the requested RunnerInfo, or -1, if the list doesn't
659 contain an info with the supplied token.
662 MessageRunnerManager::_IndexOfToken(int32 token
) const
664 for (int32 i
= 0; RunnerInfo
*info
= _InfoAt(i
); i
++) {
665 if (info
->token
== token
)
672 /*! \brief Invoked when a message runner's event is executed.
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.
678 \param info The message runner's info.
679 \return \c true, if the event object shall be deleted, \c false otherwise.
682 MessageRunnerManager::_DoEvent(RunnerInfo
*info
)
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;
697 bool success
= (info
->DeliverMessage() == B_OK
);
698 // reschedule the event
700 success
= _ScheduleEvent(info
);
702 // clean up, if the message delivery of the rescheduling failed
703 // (or the runner had already fulfilled its job)
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.
724 /*! \brief Schedules the event for a message runner for the next time a
725 message has to be sent.
727 \note The manager must be locked.
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).
735 MessageRunnerManager::_ScheduleEvent(RunnerInfo
*info
)
737 bool scheduled
= false;
738 // calculate next event time
739 if (info
->count
!= 0) {
740 info
->time
= add_time(info
->time
, info
->interval
);
742 // For runners without a count limit, we skip messages, if we're already
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
);
751 info
->event
->SetTime(info
->time
);
752 scheduled
= fEventQueue
->AddEvent(info
->event
);
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());
762 /*! \brief Returns a new unused message runner token.
764 \note The manager must be locked.
766 \return A new unused message runner token.
769 MessageRunnerManager::_NextToken()