Advertisement

Multithreaded script execution

Started by July 20, 2013 05:53 PM
1 comment, last by justin12343 11 years, 4 months ago

I have been using AngelScript in my engine for a while and now I'm trying out its multithreading capabilities. There's not much documentation on this subject, so I'm struggling a bit. I'm trying to acheive a variable amount of threads, maybe 2 or 3, to be used to execute multiple scripts at a given time. The documentation says more than one engine can be used right? Can I only use one? Has anybody had any luck with multithreading with angelscript and does anyone have any sample code available?

I'm trying to keep it as simple as possible:

This is my execution function that my threads call:


int threadExecuteContext( void *data)
{
	asIScriptContext *ctx = (asIScriptContext*)data;

	int r = ctx->Execute();

	if (r != asEXECUTION_FINISHED)
	{
		if (r == asEXECUTION_EXCEPTION)
			std::cout << "An exception occurred while executing function on thread id " << SDL_ThreadID() << "." << std::endl;

		if (r == asEXECUTION_ERROR)
			std::cout << "An error occurred while executing function on thread id " << SDL_ThreadID() << "." << std::endl;

		return asERROR;
	}
	else
	{
		ctx->Unprepare();
	}

	return 0;	
}

This one prepares a function to be called and waits for a available thread:


int ScriptManager::ExecuteFunction( asIScriptFunction *func, asIScriptObject *obj)
{
	asIScriptContext *ctx = GetContext();

	if (ctx == NULL)
		return asERROR;

	if (ctx->Prepare( func) < 0)
	{
		std::cout << "Could not prepare script function" << std::endl;
		return asERROR;
	}

	ctx->SetObject( obj);

	SDL_Thread *th = NULL;

	while (th == NULL)
		th = GetThread( ctx);

	return asSUCCESS;
}

And lastly here is my GetThread function. It maintains the maximum thread limit by waiting for one to finish:


SDL_Thread *ScriptManager::GetThread( asIScriptContext *ctx)
{
	SDL_Thread *th = NULL;

	if (threads.size() < MAX_THREADS)
	{
		th = SDL_CreateThread( threadExecuteContext, "Script Execution Thread", ctx);
		threads.push_back( th);
	}
	else
	{
		int returned;
		thread_iterator it = threads.begin();

		SDL_WaitThread( *it, &returned);
		threads.erase( it);

		th = SDL_CreateThread( threadExecuteContext, "Script Execution Thread", ctx);
		threads.push_back( th);
	}

	return th;
}

This method has problems. The context always has an unintialized state and does not execute anything.

You can use a single engine with multiple threads. There is not much documentation, because there really isn't much to say about it.

You say the asIScriptContext is always uninitialized when the thread starts. This leads me to believe that you're reusing the same asIScriptContext for all executions. This will obviously cause problems, because the context is the internal state of an execution. It holds the call stack, program position, etc.

How does your method GetContext() look like?

Finally, a general warning about multithreading. While AngelScript's internal objects are threadsafe and shouldn't cause any problems, you will need to make sure the scripts executed in parallel are properly synchronized while accessing the same data, e.g. application registered resources, or global variables in the modules, etc. The easiest way of guaranteeing proper functioning is by making sure the scripts don't have access to the same data at all, but it may not be possible for everything in which case you'll need to add the synchronization mechanisms.

AngelCode.com - game development and more - Reference DB - game developer references
AngelScript - free scripting library - BMFont - free bitmap font generator - Tower - free puzzle game

Advertisement

asIScriptContext *ScriptManager::GetContext()
{
	asIScriptContext *ctx = NULL;
	std::vector<asIScriptContext*>::iterator it;
	
	if (!contexts.empty())
	{
		for (it = contexts.begin(); it != contexts.end(); ++it)
		{
			ctx = *it;

			if (ctx->GetState() != asEXECUTION_ACTIVE)
			{
				return ctx;
			}
		}
	}

	ctx = engine->CreateContext();

#if defined(_DEBUG) || defined(_DEBUG_RELEASE)
	peek->AddContext( ctx);
#endif

	contexts.push_back( ctx);

	return ctx;
}

I can see how this will cause problems with multiple threads attempting to access a context since it tries to reuse a existing one if it can. Maybe I should read more about threading and try to come up with a more efficient model because right now I'm just constantly creating threads when I could just create the number of execution threads needed and some how feed contexts to them.

This topic is closed to new replies.

Advertisement