xref: /haiku/src/servers/registrar/MessageRunnerManager.cpp (revision 25a7b01d15612846f332751841da3579db313082)
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 
71 static bigtime_t
72 add_time(bigtime_t a, bigtime_t b)
73 {
74 	// avoid a bigtime_t overflow
75 	if (LONGLONG_MAX - b < a)
76 		return LONGLONG_MAX;
77 	else
78 		return a + b;
79 }
80 
81 
82 // RunnerEvent
83 /*!	\brief Event class used to by the message runner manager.
84 
85 	For each active message runner such an event is used. It invokes
86 	MessageRunnerManager::_DoEvent() on execution.
87 */
88 class MessageRunnerManager::RunnerEvent : public Event {
89 public:
90 	/*!	\brief Creates a new RunnerEvent.
91 		\param manager The message runner manager.
92 		\param info The RunnerInfo for the message runner.
93 	*/
94 	RunnerEvent(MessageRunnerManager *manager, RunnerInfo *info)
95 		: Event(false),
96 		  fManager(manager),
97 		  fInfo(info)
98 	{
99 	}
100 
101 	/*!	\brief Hook method invoked when the event is executed.
102 
103 		Implements Event. Calls MessageRunnerManager::_DoEvent().
104 
105 		\param queue The event queue executing the event.
106 		\return \c true, if the object shall be deleted, \c false otherwise.
107 	*/
108 	virtual bool Do(EventQueue *queue)
109 	{
110 		return fManager->_DoEvent(fInfo);
111 	}
112 
113 private:
114 	MessageRunnerManager	*fManager;	//!< The message runner manager.
115 	RunnerInfo				*fInfo;		//!< The message runner info.
116 };
117 
118 
119 // RunnerInfo
120 /*!	\brief Contains all needed information about an active message runner.
121 */
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.
131 	*/
132 	RunnerInfo(team_id team, int32 token, BMessenger target, BMessage *message,
133 			   bigtime_t interval, int32 count, BMessenger replyTarget)
134 		: team(team),
135 		  token(token),
136 		  target(target),
137 		  message(message),
138 		  interval(interval),
139 		  count(count),
140 		  replyTarget(replyTarget),
141 		  time(0),
142 		  event(NULL),
143 		  rescheduled(false)
144 	{
145 	}
146 
147 	/*!	\brief Frees all resources associated with the object.
148 
149 		The message and the event are delete.
150 	*/
151 	~RunnerInfo()
152 	{
153 		delete message;
154 		delete event;
155 	}
156 
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.
161 	*/
162 	status_t DeliverMessage()
163 	{
164 		if (count > 0)
165 			count--;
166 
167 		// set the reply target
168 		BMessage::Private(message).SetReply(replyTarget);
169 
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.
174 		status_t error;
175 		if (count > 0) {
176 			error = MessageDeliverer::Default()->DeliverMessage(message, target,
177 				interval);
178 		} else {
179 			error = MessageDeliverer::Default()->DeliverMessage(message,
180 				target);
181 		}
182 
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)
187 			error = B_OK;
188 		return error;
189 	}
190 
191 	team_id		team;			//!< The team owning the message runner.
192 	int32		token;			/*!< The unique token associated with the
193 									 message runner. */
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
198 									 sent. */
199 	BMessenger	replyTarget;	/*!< The reply target for the delivered
200 									 message. */
201 	bigtime_t	time;			/*!< Time at which the next message will be
202 									 sent. */
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
206 									 rescheduled. */
207 };
208 
209 
210 // constructor
211 /*!	\brief Creates a new MessageRunnerManager.
212 	\param eventQueue The EventQueue the manager shall use.
213 */
214 MessageRunnerManager::MessageRunnerManager(EventQueue *eventQueue)
215 	: fRunnerInfos(),
216 	  fLock(),
217 	  fEventQueue(eventQueue),
218 	  fNextToken(0)
219 {
220 }
221 
222 // destructor
223 /*!	\brief Frees all resources associated with the object.
224 
225 	The manager's event queue must already have been stopped
226 	(EventQueue::Die()).
227 */
228 MessageRunnerManager::~MessageRunnerManager()
229 {
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))
236 			info->event = NULL;
237 		delete info;
238 	}
239 	fRunnerInfos.MakeEmpty();
240 }
241 
242 // HandleRegisterRunner
243 /*!	\brief Handles a registration request (BMessageRunner::InitData()).
244 	\param request The request message.
245 */
246 void
247 MessageRunnerManager::HandleRegisterRunner(BMessage *request)
248 {
249 	FUNCTION_START();
250 
251 	BAutolock _lock(fLock);
252 	status_t error = B_OK;
253 	// get the parameters
254 	team_id team;
255 	BMessenger target;
256 	// TODO: This should be a "new (nothrow)", but R5's BMessage doesn't
257 	// define that version.
258 	BMessage *message = new BMessage;
259 	bigtime_t interval;
260 	int32 count;
261 	BMessenger replyTarget;
262 	if (error == B_OK && message == NULL)
263 		error = B_NO_MEMORY;
264 	if (error == B_OK && request->FindInt32("team", &team) != B_OK)
265 		error = B_BAD_VALUE;
266 	if (error == B_OK && request->FindMessenger("target", &target) != B_OK)
267 		error = B_BAD_VALUE;
268 	if (error == B_OK && request->FindMessage("message", message) != B_OK)
269 		error = B_BAD_VALUE;
270 	if (error == B_OK && request->FindInt64("interval", &interval) != B_OK)
271 		error = B_BAD_VALUE;
272 	if (error == B_OK && request->FindInt32("count", &count) != B_OK)
273 		error = B_BAD_VALUE;
274 	if (error == B_OK
275 		&& request->FindMessenger("reply_target", &replyTarget) != B_OK) {
276 		error = B_BAD_VALUE;
277 	}
278 
279 	// check the parameters
280 	if (error == B_OK && count == 0)
281 		error = B_BAD_VALUE;
282 
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 
297 	// create a new event
298 	RunnerEvent *event = NULL;
299 	if (error == B_OK) {
300 		event = new(nothrow) RunnerEvent(this, info);
301 		if (event) {
302 			info->event = event;
303 			if (!_ScheduleEvent(info))
304 				error = B_NO_MEMORY;	// TODO: The only possible reason?
305 		} else
306 			error = B_NO_MEMORY;
307 	}
308 
309 	// cleanup on error
310 	if (error != B_OK) {
311 		if (info) {
312 			_RemoveInfo(info);
313 			delete info;
314 		}
315 		delete message;
316 	}
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 
391 	// find the runner info
392 	RunnerInfo *info = NULL;
393 	if (error == B_OK) {
394 		info = _InfoForToken(token);
395 		if (!info) {
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
402 			//	to allow this.)
403 			error = B_BAD_VALUE;
404 		}
405 	}
406 
407 	// set the new values
408 	if (error == B_OK) {
409 		bool eventRemoved = false;
410 		bool deleteInfo = false;
411 		// count
412 		if (setCount) {
413 			if (count == 0)
414 				deleteInfo = true;
415 			else
416 				info->count = count;
417 		}
418 		// interval
419 		if (setInterval) {
420 			eventRemoved = fEventQueue->RemoveEvent(info->event);
421 			if (!eventRemoved)
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?
428 		}
429 		// remove and delete the info on error
430 		if (error != B_OK || deleteInfo)
431 			_DeleteInfo(info, eventRemoved);
432 	}
433 
434 	// reply to the request
435 	if (error == B_OK) {
436 		BMessage reply(B_REG_SUCCESS);
437 		request->SendReply(&reply);
438 	} else {
439 		BMessage reply(B_REG_ERROR);
440 		reply.AddInt32("error", error);
441 		request->SendReply(&reply);
442 	}
443 
444 	FUNCTION_END();
445 }
446 
447 // HandleGetRunnerInfo
448 /*!	\brief Handles an get info request (BMessageRunner::GetInfo()).
449 	\param request The request message.
450 */
451 void
452 MessageRunnerManager::HandleGetRunnerInfo(BMessage *request)
453 {
454 	FUNCTION_START();
455 
456 	BAutolock _lock(fLock);
457 	status_t error = B_OK;
458 	// get the parameters
459 	int32 token;
460 	if (error == B_OK && request->FindInt32("token", &token) != B_OK)
461 		error = B_BAD_VALUE;
462 	// find the runner info
463 	RunnerInfo *info = NULL;
464 	if (error == B_OK) {
465 		info = _InfoForToken(token);
466 		if (!info)
467 			error = B_BAD_VALUE;
468 	}
469 	// reply to the request
470 	if (error == B_OK) {
471 		BMessage reply(B_REG_SUCCESS);
472 		reply.AddInt64("interval", info->interval);
473 		reply.AddInt32("count", info->count);
474 		request->SendReply(&reply);
475 	} else {
476 		BMessage reply(B_REG_ERROR);
477 		reply.AddInt32("error", error);
478 		request->SendReply(&reply);
479 	}
480 
481 	FUNCTION_END();
482 }
483 
484 // Lock
485 /*!	\brief Locks the manager.
486 	\return \c true, if locked successfully, \c false otherwise.
487 */
488 bool
489 MessageRunnerManager::Lock()
490 {
491 	return fLock.Lock();
492 }
493 
494 // Unlock
495 /*!	\brief Unlocks the manager.
496 */
497 void
498 MessageRunnerManager::Unlock()
499 {
500 	fLock.Unlock();
501 }
502 
503 // _AddInfo
504 /*!	\brief Adds a RunnerInfo to the list of RunnerInfos.
505 
506 	\note The manager must be locked.
507 
508 	\param info The RunnerInfo to be added.
509 	\return \c true, if added successfully, \c false otherwise.
510 */
511 bool
512 MessageRunnerManager::_AddInfo(RunnerInfo *info)
513 {
514 	return fRunnerInfos.AddItem(info);
515 }
516 
517 // _RemoveInfo
518 /*!	\brief Removes a RunnerInfo from the list of RunnerInfos.
519 
520 	\note The manager must be locked.
521 
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.
525 */
526 bool
527 MessageRunnerManager::_RemoveInfo(RunnerInfo *info)
528 {
529 	return fRunnerInfos.RemoveItem(info);
530 }
531 
532 // _RemoveInfo
533 /*!	\brief Removes a RunnerInfo at a given index from the list of RunnerInfos.
534 
535 	\note The manager must be locked.
536 
537 	\param index The index of the RunnerInfo to be removed.
538 	\return \c true, if removed successfully, \c false, if the supplied index
539 			is out of range.
540 */
541 MessageRunnerManager::RunnerInfo*
542 MessageRunnerManager::_RemoveInfo(int32 index)
543 {
544 	return (RunnerInfo*)fRunnerInfos.RemoveItem(index);
545 }
546 
547 // _RemoveInfoWithToken
548 /*!	\brief Removes a RunnerInfo with a specified token from the list of
549 		   RunnerInfos.
550 
551 	\note The manager must be locked.
552 
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.
556 */
557 MessageRunnerManager::RunnerInfo*
558 MessageRunnerManager::_RemoveInfoWithToken(int32 token)
559 {
560 	RunnerInfo *info = NULL;
561 	int32 index = _IndexOfToken(token);
562 	if (index >= 0)
563 		info = _RemoveInfo(index);
564 	return info;
565 }
566 
567 // _DeleteInfo
568 /*!	\brief Removes a RunnerInfo from the list of RunnerInfos and deletes it.
569 
570 	\note The manager must be locked.
571 
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.
575 */
576 bool
577 MessageRunnerManager::_DeleteInfo(RunnerInfo *info, bool eventRemoved)
578 {
579 	bool result = _RemoveInfo(info);
580 	if (result) {
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))
584 			info->event = NULL;
585 		delete info;
586 	}
587 	return result;
588 }
589 
590 // _CountInfos
591 /*!	\brief Returns the number of RunnerInfos in the list of RunnerInfos.
592 
593 	\note The manager must be locked.
594 
595 	\return Returns the number of RunnerInfos in the list of RunnerInfos.
596 */
597 int32
598 MessageRunnerManager::_CountInfos() const
599 {
600 	return fRunnerInfos.CountItems();
601 }
602 
603 // _InfoAt
604 /*!	\brief Returns the RunnerInfo at the specified index in the list of
605 		   RunnerInfos.
606 
607 	\note The manager must be locked.
608 
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
611 			is out of range.
612 */
613 MessageRunnerManager::RunnerInfo*
614 MessageRunnerManager::_InfoAt(int32 index) const
615 {
616 	return (RunnerInfo*)fRunnerInfos.ItemAt(index);
617 }
618 
619 // _InfoForToken
620 /*!	\brief Returns the RunnerInfo with the specified index.
621 
622 	\note The manager must be locked.
623 
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.
627 */
628 MessageRunnerManager::RunnerInfo*
629 MessageRunnerManager::_InfoForToken(int32 token) const
630 {
631 	return _InfoAt(_IndexOfToken(token));
632 }
633 
634 // _IndexOf
635 /*!	\brief Returns the index of the supplied RunnerInfo in the list of
636 		   RunnerInfos.
637 
638 	\note The manager must be locked.
639 
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.
643 */
644 int32
645 MessageRunnerManager::_IndexOf(RunnerInfo *info) const
646 {
647 	return fRunnerInfos.IndexOf(info);
648 }
649 
650 // _IndexOfToken
651 /*!	\brief Returns the index of the RunnerInfo identified by the supplied
652 		   token in the list of RunnerInfos.
653 
654 	\note The manager must be locked.
655 
656 	\param token The token identifying the RunnerInfo whose index shall be
657 		   returned.
658 	\return The index of the requested RunnerInfo, or -1, if the list doesn't
659 			contain an info with the supplied token.
660 */
661 int32
662 MessageRunnerManager::_IndexOfToken(int32 token) const
663 {
664 	for (int32 i = 0; RunnerInfo *info = _InfoAt(i); i++) {
665 		if (info->token == token)
666 			return i;
667 	}
668 	return -1;
669 }
670 
671 // _DoEvent
672 /*!	\brief Invoked when a message runner's event is executed.
673 
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.
677 
678 	\param info The message runner's info.
679 	\return \c true, if the event object shall be deleted, \c false otherwise.
680 */
681 bool
682 MessageRunnerManager::_DoEvent(RunnerInfo *info)
683 {
684 	FUNCTION_START();
685 
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;
695 		else {
696 			// send the message
697 			bool success = (info->DeliverMessage() == B_OK);
698 			// reschedule the event
699 			if (success)
700 				success = _ScheduleEvent(info);
701 
702 			// clean up, if the message delivery of the rescheduling failed
703 			// (or the runner had already fulfilled its job)
704 			if (!success) {
705 				deleteEvent = true;
706 				info->event = NULL;
707 				_RemoveInfo(info);
708 				delete info;
709 			}
710 		}
711 	} else {
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.
715 		deleteEvent = true;
716 	}
717 
718 	FUNCTION_END();
719 
720 	return deleteEvent;
721 }
722 
723 // _ScheduleEvent
724 /*!	\brief Schedules the event for a message runner for the next time a
725 		   message has to be sent.
726 
727 	\note The manager must be locked.
728 
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).
733 */
734 bool
735 MessageRunnerManager::_ScheduleEvent(RunnerInfo *info)
736 {
737 	bool scheduled = false;
738 	// calculate next event time
739 	if (info->count != 0) {
740 		info->time = add_time(info->time, info->interval);
741 
742 		// For runners without a count limit, we skip messages, if we're already
743 		// late.
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);
749 		}
750 
751 		info->event->SetTime(info->time);
752 		scheduled = fEventQueue->AddEvent(info->event);
753 
754 PRINT("runner %ld (%lld, %ld) rescheduled: %d, time: %lld, now: %lld\n",
755 info->token, info->interval, info->count, scheduled, info->time, system_time());
756 	}
757 	return scheduled;
758 }
759 
760 // _NextToken
761 /*!	\brief Returns a new unused message runner token.
762 
763 	\note The manager must be locked.
764 
765 	\return A new unused message runner token.
766 */
767 int32
768 MessageRunnerManager::_NextToken()
769 {
770 	return fNextToken++;
771 }
772 
773