xref: /haiku/src/servers/registrar/MessageRunnerManager.cpp (revision f2ced752a08ff5d2618826bcd3ae3976c9f3e92e)
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