summaryrefslogtreecommitdiff
path: root/public/gcsdk/scheduledfunction.h
blob: 1522de7cee902062649f12c892852695673326bc (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
//====== Copyright (c), Valve Corporation, All rights reserved. =======
//
// Purpose: Provides a scheduled function manager that will bucket events into
//		time chunks and execute them as time elapses
//
//=============================================================================

#ifndef SCHEDULEDFUNCTION_H
#define SCHEDULEDFUNCTION_H
#ifdef _WIN32
#pragma once
#endif

namespace GCSDK
{

//interface for events that can be scheduled to run on the GC base
class IGCScheduledFunction
{
public:
	IGCScheduledFunction() : m_nAbsScheduleBucket( knInvalidBucket )	{}
	virtual ~IGCScheduledFunction();
	//called in response to our event time elapsing
	virtual void OnEvent() = 0;

	bool BIsScheduled()	const											{ return m_nAbsScheduleBucket != IGCScheduledFunction::knInvalidBucket; }

private:
	//the absolute bucket that we were scheduled in (or invalid). Used to enable deregistering
	friend class CScheduledFunctionMgr;
	uint32		m_nAbsScheduleBucket;
	uint32		m_nLLIndex;
	static const uint32 knInvalidBucket = ( uint32 )-1;
};

//utility for scheduling a global function
class CGlobalScheduledFunction :
	public IGCScheduledFunction
{
public:

	CGlobalScheduledFunction();
	
	typedef void ( *func_t )();

	void ScheduleMS( func_t pfn, uint32 nDelayMS );
	void ScheduleSecond( func_t pfn, uint32 nDelaySecond );
	void ScheduleMinute( func_t pfn, uint32 nDelayMinute );
	void Cancel();

	virtual void OnEvent() OVERRIDE;

private:
	func_t	m_pfn;
};

//the same as the above, but automatically deletes the object once the event is fired
class CGlobalScheduledFunctionAutoDelete :
	public CGlobalScheduledFunction
{
public:
	CGlobalScheduledFunctionAutoDelete()									{}

	virtual void OnEvent() OVERRIDE
	{
		CGlobalScheduledFunction::OnEvent();
		delete this;
	}
};

//utility for scheduling a member function
template< class T >
class CScheduledFunction :
	public IGCScheduledFunction
{
public:
	CScheduledFunction() : m_pObj( NULL ), m_pfn( NULL ) {}

	typedef void ( T::*func_t )();

	void ScheduleMS( T* pObj, func_t pfn, uint32 nDelayMS )
	{
		m_pObj = pObj;
		m_pfn = pfn;
		GScheduledFunctionMgr().ScheduleMS( this, nDelayMS );
	}

	void ScheduleSecond( T* pObj, func_t pfn, uint32 nDelaySecond )
	{
		m_pObj = pObj;
		m_pfn = pfn;
		GScheduledFunctionMgr().ScheduleSecond( this, nDelaySecond );
	}

	void ScheduleMinute( T* pObj, func_t pfn, uint32 nDelayMinute )
	{
		m_pObj = pObj;
		m_pfn = pfn;
		GScheduledFunctionMgr().ScheduleMinute( this, nDelayMinute );
	}


	void Cancel()
	{
		GScheduledFunctionMgr().Cancel( this );
	}

	virtual void OnEvent() OVERRIDE
	{
		( m_pObj->*m_pfn )();
	}

private:
	T*		m_pObj;
	func_t	m_pfn;
};

//similar to the above, but auto deletes once the event is fired
template< class T >
class CScheduledFunctionAutoDelete :
	public CScheduledFunction< T >
{
public:
	typedef void ( T::*func_t )();

	CScheduledFunctionAutoDelete()											{}
	CScheduledFunctionAutoDelete( T* pObj, func_t pfn, uint32 nDelayMS )	{ Schedule( pObj, pfn, nDelayMS ); }
	virtual void OnEvent() OVERRIDE
	{
		CScheduledFunction< T >::OnEvent();
		delete this;
	}
};


class CScheduledFunctionMgr
{
public:

	CScheduledFunctionMgr();

	//called to initialize the starting time for the scheduled function manager. It doesn't need to be globally absolute, just relative to the time values provided
	//to the run function
	void InitStartingTime();

	//called to register a scheduled event for a certain period in the future, with the delay specified in milliseconds. This has resolution of an individual frame, and
	//will unregister from any previously registered time slot
	void ScheduleMS( IGCScheduledFunction* pEvent, uint32 nMSDelay );
	//similar to the above, but instead of having frame resolution, this has second level resolution, and should be used for low granularity tasks
	void ScheduleSecond( IGCScheduledFunction* pEvent, uint32 nSDelay );
	//similar to the above but has minute level resolution which should be used for very low resolution tasks
	void ScheduleMinute( IGCScheduledFunction* pEvent, uint32 nMinuteDelay );

	//deregisters a previously registered event
	void Cancel( IGCScheduledFunction* pEvent );

	//called to run registered functions
	void RunFunctions();
	
private:
	
	//called internally by the other schedule functions to schedule the event at the specified resolution in our resolution array
	void InternalSchedule( uint32 nResolution, IGCScheduledFunction* pEvent, uint32 nMSDelay );

	//the list type that we store all of the entries in. We use a single list to avoid the overhead of so many lists 
	typedef CUtlLinkedList< IGCScheduledFunction*, uint32 >	TScheduleList;

	//all information tied to a specific resolution, including the time hash buckets, number of buckets, and which buckets it has executed
	class CScheduleBucket
	{
	public:
		CScheduleBucket();
		~CScheduleBucket();
		void Init( TScheduleList& MasterList, uint32 nNumBuckets, uint32 nMicroSPerBucket );		

		//maps a micro second time to a bucket time
		uint32 GetAbsScheduleBucketIndex( uint64 nMicroSTime ) const			{ return ( uint32 )( nMicroSTime / m_nMicroSPerBucket ); }

		//called to run registered functions
		void RunFunctions( TScheduleList& MasterList, uint64 nMicroSTime );

		//for each bucket, we insert a node into the master linked list, then our list runs from this node to the next empty node (or end) in the list
		uint32*		m_pBuckets;
		//the number of buckets that we have
		uint32		m_nNumBuckets;
		//how many micro seconds each bucket represents
		uint32		m_nMicroSPerBucket;
		//the last bucket that we had executed
		uint32		m_nAbsLastScheduleBucket;
	};

	//the list of all of our entries. We store bucket starts within here, and then insert the events in between them
	TScheduleList	m_ScheduleList;

	//the list of resolutions that we have
	CScheduleBucket	m_Resolutions[ 3 ];
};

//global singleton access
CScheduledFunctionMgr& GScheduledFunctionMgr();


} //namespace GCSDK

#endif