PageRenderTime 43ms CodeModel.GetById 5ms app.highlight 34ms RepoModel.GetById 1ms app.codeStats 1ms

/xbmc/visualizations/Vortex/angelscript/add_on/contextmgr/contextmgr.cpp

http://github.com/xbmc/xbmc
C++ | 308 lines | 208 code | 55 blank | 45 comment | 40 complexity | b8298446beb883892d17efe74ff50b2b MD5 | raw file
  1#include <assert.h>
  2#include <string>
  3
  4#include "contextmgr.h"
  5
  6using namespace std;
  7
  8// TODO: Should have a pool of free asIScriptContext so that new contexts
  9//       won't be allocated every time. The application must not keep 
 10//       its own references, instead it must tell the context manager
 11//       that it is using the context. Otherwise the context manager may
 12//       think it can reuse the context too early.
 13
 14// TODO: Need to have a callback for when scripts finishes, so that the 
 15//       application can receive return values.
 16
 17BEGIN_AS_NAMESPACE
 18
 19struct SContextInfo
 20{
 21    asUINT sleepUntil;
 22	vector<asIScriptContext*> coRoutines;
 23	asUINT currentCoRoutine;
 24};
 25
 26static CContextMgr *g_ctxMgr = 0;
 27static void ScriptSleep(asUINT milliSeconds)
 28{
 29	// Get a pointer to the context that is currently being executed
 30	asIScriptContext *ctx = asGetActiveContext();
 31	if( ctx && g_ctxMgr )
 32	{
 33		// Suspend its execution. The VM will continue until the current 
 34		// statement is finished and then return from the Execute() method
 35		ctx->Suspend();
 36
 37		// Tell the context manager when the context is to continue execution
 38		g_ctxMgr->SetSleeping(ctx, milliSeconds);
 39	}
 40}
 41
 42static void ScriptYield()
 43{
 44	// Get a pointer to the context that is currently being executed
 45	asIScriptContext *ctx = asGetActiveContext();
 46	if( ctx && g_ctxMgr )
 47	{
 48		// Let the context manager know that it should run the next co-routine
 49		g_ctxMgr->NextCoRoutine();
 50
 51		// The current context must be suspended so that VM will return from 
 52		// the Execute() method where the context manager will continue.
 53		ctx->Suspend();
 54	}
 55}
 56
 57void ScriptCreateCoRoutine(string &func, CScriptAny *arg)
 58{
 59	asIScriptContext *ctx = asGetActiveContext();
 60	if( ctx && g_ctxMgr )
 61	{
 62		asIScriptEngine *engine = ctx->GetEngine();
 63		string mod = engine->GetFunctionDescriptorById(ctx->GetCurrentFunction())->GetModuleName();
 64
 65		// We need to find the function that will be created as the co-routine
 66		string decl = "void " + func + "(any @)"; 
 67		int funcId = engine->GetModule(mod.c_str())->GetFunctionIdByDecl(decl.c_str());
 68		if( funcId < 0 )
 69		{
 70			// No function could be found, raise an exception
 71			ctx->SetException(("Function '" + decl + "' doesn't exist").c_str());
 72			return;
 73		}
 74
 75		// Create a new context for the co-routine
 76		asIScriptContext *coctx = g_ctxMgr->AddContextForCoRoutine(ctx, funcId);
 77
 78		// Pass the argument to the context
 79		coctx->SetArgObject(0, arg);
 80	}
 81}
 82
 83CContextMgr::CContextMgr()
 84{
 85    getTimeFunc   = 0;
 86	currentThread = 0;
 87}
 88
 89CContextMgr::~CContextMgr()
 90{
 91	asUINT n;
 92
 93	// Free the memory
 94	for( n = 0; n < threads.size(); n++ )
 95	{
 96		if( threads[n] )
 97		{
 98			for( asUINT c = 0; c < threads[n]->coRoutines.size(); c++ )
 99			{
100				if( threads[n]->coRoutines[c] )
101					threads[n]->coRoutines[c]->Release();
102			}
103
104			delete threads[n];
105		}
106	}
107
108	for( n = 0; n < freeThreads.size(); n++ )
109	{
110		if( freeThreads[n] )
111		{
112			assert( freeThreads[n]->coRoutines.size() == 0 );
113
114			delete freeThreads[n];
115		}
116	}
117}
118
119void CContextMgr::ExecuteScripts()
120{
121	g_ctxMgr = this; 
122
123	// TODO: Should have a time out. And if not all scripts executed before the 
124	//       time out, the next time the function is called the loop should continue
125	//       where it left off.
126
127	// TODO: We should have a per thread time out as well. When this is used, the yield
128	//       call should resume the next coroutine immediately if there is still time
129
130	// Check if the system time is higher than the time set for the contexts
131	asUINT time = getTimeFunc ? getTimeFunc() : asUINT(-1);
132	for( currentThread = 0; currentThread < threads.size(); currentThread++ )
133	{
134		if( threads[currentThread]->sleepUntil < time )
135		{
136			// TODO: Only allow each thread to execute for a while
137
138			int currentCoRoutine = threads[currentThread]->currentCoRoutine;
139			int r = threads[currentThread]->coRoutines[currentCoRoutine]->Execute();
140			if( r != asEXECUTION_SUSPENDED )
141			{
142				// The context has terminated execution (for one reason or other)
143				threads[currentThread]->coRoutines[currentCoRoutine]->Release();
144				threads[currentThread]->coRoutines[currentCoRoutine] = 0;
145
146				threads[currentThread]->coRoutines.erase(threads[currentThread]->coRoutines.begin() + threads[currentThread]->currentCoRoutine);
147				if( threads[currentThread]->currentCoRoutine >= threads[currentThread]->coRoutines.size() )
148					threads[currentThread]->currentCoRoutine = 0;
149
150				// If this was the last co-routine terminate the thread
151				if( threads[currentThread]->coRoutines.size() == 0 )
152				{
153					freeThreads.push_back(threads[currentThread]);
154					threads.erase(threads.begin() + currentThread);
155					currentThread--;
156				}
157			}
158		}
159	}
160
161	g_ctxMgr = 0;
162}
163
164void CContextMgr::NextCoRoutine()
165{
166	threads[currentThread]->currentCoRoutine++;
167	if( threads[currentThread]->currentCoRoutine >= threads[currentThread]->coRoutines.size() )
168		threads[currentThread]->currentCoRoutine = 0;
169}
170
171void CContextMgr::AbortAll()
172{
173	// Abort all contexts and release them. The script engine will make 
174	// sure that all resources held by the scripts are properly released.
175
176	for( asUINT n = 0; n < threads.size(); n++ )
177	{
178		for( asUINT c = 0; c < threads[n]->coRoutines.size(); c++ )
179		{
180			if( threads[n]->coRoutines[c] )
181			{
182				threads[n]->coRoutines[c]->Abort();
183				threads[n]->coRoutines[c]->Release();
184				threads[n]->coRoutines[c] = 0;
185			}
186		}
187		threads[n]->coRoutines.resize(0);
188
189		freeThreads.push_back(threads[n]);
190	}
191
192	threads.resize(0);
193
194	currentThread = 0;
195}
196
197asIScriptContext *CContextMgr::AddContext(asIScriptEngine *engine, int funcId)
198{
199	// Create the new context
200	asIScriptContext *ctx = engine->CreateContext();
201	if( ctx == 0 )
202		return 0;
203
204	// Prepare it to execute the function
205	int r = ctx->Prepare(funcId);
206	if( r < 0 )
207	{
208		ctx->Release();
209		return 0;
210	}
211
212	// Add the context to the list for execution
213	SContextInfo *info = 0;
214	if( freeThreads.size() > 0 )
215	{
216		info = *freeThreads.rbegin();
217		freeThreads.pop_back();
218	}
219	else
220	{
221		info = new SContextInfo;
222	}
223
224    info->coRoutines.push_back(ctx);
225	info->currentCoRoutine = 0;
226    info->sleepUntil = 0;
227	threads.push_back(info);
228
229	return ctx;
230}
231
232asIScriptContext *CContextMgr::AddContextForCoRoutine(asIScriptContext *currCtx, int funcId)
233{
234	asIScriptEngine *engine = currCtx->GetEngine();
235	asIScriptContext *coctx = engine->CreateContext();
236	if( coctx == 0 )
237	{
238		return 0;
239	}
240
241	// Prepare the context
242	int r = coctx->Prepare(funcId);
243	if( r < 0 )
244	{
245		// Couldn't prepare the context
246		coctx->Release();
247		return 0;
248	}
249
250	// Find the current context thread info
251	// TODO: Start with the current thread so that we can find the group faster
252	for( asUINT n = 0; n < threads.size(); n++ )
253	{
254		if( threads[n]->coRoutines[threads[n]->currentCoRoutine] == currCtx )
255		{
256			// Add the coRoutine to the list
257			threads[n]->coRoutines.push_back(coctx);
258		}
259	}
260
261	return coctx; 
262}
263
264void CContextMgr::SetSleeping(asIScriptContext *ctx, asUINT milliSeconds)
265{
266    assert( getTimeFunc != 0 );
267    
268	// Find the context and update the timeStamp  
269	// for when the context is to be continued
270
271	// TODO: Start with the current thread
272
273	for( asUINT n = 0; n < threads.size(); n++ )
274	{
275		if( threads[n]->coRoutines[threads[n]->currentCoRoutine] == ctx )
276		{
277			threads[n]->sleepUntil = (getTimeFunc ? getTimeFunc() : 0) + milliSeconds;
278		}
279	}
280}
281
282void CContextMgr::RegisterThreadSupport(asIScriptEngine *engine)
283{
284	int r;
285
286    // Must set the get time callback function for this to work
287    assert( getTimeFunc != 0 );
288    
289    // Register the sleep function
290    r = engine->RegisterGlobalFunction("void sleep(uint)", asFUNCTION(ScriptSleep), asCALL_CDECL); assert( r >= 0 );
291
292	// TODO: Add support for spawning new threads, waiting for signals, etc
293}
294
295void CContextMgr::RegisterCoRoutineSupport(asIScriptEngine *engine)
296{
297	int r;
298
299	r = engine->RegisterGlobalFunction("void yield()", asFUNCTION(ScriptYield), asCALL_CDECL); assert( r >= 0 );
300	r = engine->RegisterGlobalFunction("void createCoRoutine(const string &in, any @+)", asFUNCTION(ScriptCreateCoRoutine), asCALL_CDECL); assert( r >= 0 );
301}
302
303void CContextMgr::SetGetTimeCallback(TIMEFUNC_t func)
304{
305	getTimeFunc = func;
306}
307
308END_AS_NAMESPACE