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