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
26using std::max;
27using 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
65using namespace BPrivate;
66
67//! The minimal time interval for message runners (1 us).
68static const bigtime_t kMinimalTimeInterval = 1LL;
69
70
71static bigtime_t
72add_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*/
88class MessageRunnerManager::RunnerEvent : public Event {
89public:
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
113private:
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*/
122struct 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*/
214MessageRunnerManager::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*/
228MessageRunnerManager::~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*/
246void
247MessageRunnerManager::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, kMinimalTimeInterval);
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*/
336void
337MessageRunnerManager::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*/
371void
372MessageRunnerManager::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, kMinimalTimeInterval);
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*/
451void
452MessageRunnerManager::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*/
488bool
489MessageRunnerManager::Lock()
490{
491	return fLock.Lock();
492}
493
494// Unlock
495/*!	\brief Unlocks the manager.
496*/
497void
498MessageRunnerManager::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*/
511bool
512MessageRunnerManager::_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*/
526bool
527MessageRunnerManager::_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*/
541MessageRunnerManager::RunnerInfo*
542MessageRunnerManager::_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*/
557MessageRunnerManager::RunnerInfo*
558MessageRunnerManager::_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*/
576bool
577MessageRunnerManager::_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*/
597int32
598MessageRunnerManager::_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*/
613MessageRunnerManager::RunnerInfo*
614MessageRunnerManager::_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*/
628MessageRunnerManager::RunnerInfo*
629MessageRunnerManager::_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*/
644int32
645MessageRunnerManager::_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*/
661int32
662MessageRunnerManager::_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*/
681bool
682MessageRunnerManager::_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*/
734bool
735MessageRunnerManager::_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
754PRINT("runner %" B_PRId32 " (%" B_PRId64 ", %" B_PRId32 ") rescheduled: %d, "
755"time: %" B_PRId64 ", now: %" B_PRId64 "\n", info->token, info->interval,
756info->count, scheduled, info->time, system_time());
757	}
758	return scheduled;
759}
760
761// _NextToken
762/*!	\brief Returns a new unused message runner token.
763
764	\note The manager must be locked.
765
766	\return A new unused message runner token.
767*/
768int32
769MessageRunnerManager::_NextToken()
770{
771	return fNextToken++;
772}
773
774