Thread Safety

Started by
9 comments, last by WitchLord 19 years, 6 months ago
How thread safe is AngelScript currently? I've read something somewhere that says it's not thread safe at all - that, in fact, using it in multiple threads is downright suicidal. I need to be able to execute multiple contexes derived from not only the same engine, but the same module, simultaneously. Would it be possible to supply us with some information regarding which functions modify the state of the engine/modules and which only access it?
Advertisement
I wouldn't say suicidal [wink]. But yes, there are some places where you'll probably have trouble with multi threading.

The first and most problematic is the asGetActiveContext() function. This one pushes the active context on a stack so that when a system function is called it will be possible to get the context that called it. To make this safe there would have to be one stack for each thread. This one would cause a problem even if the engines are different.

Another problem is with the reference counters. They need to use semaphores so that there will not be conflicts.

Global variables are also a problem as they can be accessed from multiple places. It might be necessary to change the way they are accessed to make the bytecodes atomicals so that the use of semaphores is possible.

Those are the major problems that I can think of right now. The biggest problem with multithreading is that it is so difficult to test them so that functionality can be guaranteed.

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

Then lets hope there are some simple solutions.

Lets assume a given context will only be referenced by a single thread. That same thread will create it and destroy it. What reference counts are shared across threads in this case? Furthermore, what, exactly, is being reference counted, and why has that responsibility been given to the library and not client code?

asGetCurrentContext solves no problems that can't be handled with a different mechanism, and I can't actually think of a reason why I'd need it in what I'm planning. So it can wait.

Of course, access to all game data will be serialized. Scripts will not have direct access to it either. The game will not be accessing any globals in scripts.

As for testing - yes, it can be a pain. That's part of what makes threading so complicated.
The context holds references to the engine and the module it is accessing.

Both of these references are held so that the application can release the engine or module (discarding it) without having to release the context first.

I'll give support for multithreading priority over new features. Maybe I can get the first 1.10.0 WIP released next week with support for multithreading. I can't guarantee stability in such a short time, but at least it should be working to the best of my knowledge.

Global variables will have to wait until another WIP as well, as those would require much more work.

If you can guarantee that all contexts are released before the engine and before any modules are discarded or recompiled, you might be able to get multithreading going by disabling the context's reference counting on the engine and modules.

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

Well. Just so you know, I'm working on a MUD. There is only one engine. There is also only one context per module. Every player gets their own module, so I can easily support different commands simply by compiling them into the module. There is a module per room as well. Whereas players get compiled once, when they log in, rooms are loaded and compiled on the fly. They are entering and leaving memory constantly. All compiling work is localized to a single thread for simplicity. There are several threads in the system. One devoted to loading and compiling scripts, one to running update ticks on the world (which involves calling lots of scripts), one to push messages around between game objects, and two for each player logged in - One to read from the network (and execute commands) and another to write to the network.

It gets worse. Executing a player's command can involve calls into several contexes. Currently I just treat the command as the name of a function. First I query the player's context for the function, and if found, call it. Next I query any objects in the room, calling the function on all of them. Finally, I query the room. All of these objects have different contexes. Keeping myself from calling into the same context from different threads at the same time, and other issues, will keep me from putting commands in objects other than the player for awhile.

'Rooms' work simply by setting some global data on the player - but this data is in C++, not the script, so I can serialize it easily. A room would, for example, set the variable 'short' to the name of the room. Later, the Look command would use that variable. No need to mess with the room's context. Generally, a room's context executes code only when a player enters that room, though I plan for them to be able to register functions to be called on exit, and, of course, additional commands that work only in that room.
Seems like an interesting project. I will do what I can to make AngelScript support multithreading.

I suppose you don't want a context to be locked to a certain thread, e.g. only the thread that created the context can use it? I believe that could make my job slightly easier. A context can be used to access any module, I see no need to have one context for each module. It would be sufficient with one context per thread. Of course if you have some scripts that are long running and should be repeatedly suspended and resumed they would need to have their own contexts.

You will most likely have to protect global data by using functions to access them. These functions in turn would have to use semaphores so that multiple threads can't access the data at the same time. That should be a responsibility of the host application.



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

Well no, global resources use mutexes, not semaphores. :P

I poked things a bit and one context per player makes a great deal more sense. That same context can execute code in a room or objects module, assuming two contextes executing code in the same module concurrently is safe.
Isn't a mutex just a special form of semaphores that only lets at most one thread pass at a time? I don't remember right now, but I think that is so.

The contexts' stacks are private to the contexts so the concurrent execution of two contexts shouldn't be a problem. The only problem with execution is the global resources, including script declared variables. You can make sure none of the scripts use global script variables by call GetGlobalVarCount() after compiling the script.

The problem you might face is when calling Prepare() as it could potentially make the reference counting on the engine, and modules become incorrect.

What I will do is to add critical sections on these locations. I will do this by abstracting the Win32 functions EnterCriticalSection() and LeaveCriticalSection(). I don't know how to do it on Linux, but with the abstraction someone else can easily to it.

I will get asGetActiveContext() working by mapping the thread ID to a stack of contexts.

I think that this will not require too much work, and I hope to have the next WIP out in a couple of days with rudimentary support for multithreading.





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

In practice, a mutex is a specialization of a semaphore. But the windows API seperates them. A critical section is probably what I'll actually use. I don't think I'll be needing the extra features of the heavier objects.

I'll assert on the value of GetGlobalVarCount(). My plan was for the scripts to store any state variables through an object property mechanism, so they can be gotten at by other things and saved to disc and what not.

I need to call prepare every time I call a script function, of course. That could be a problem.
I'll concentrate on making the parts you are using threadsafe.

asGetActiveContext() // I know you don't need this function, but it is used anyway internally
asIScriptContext::Prepare() // reference counting needs to be thread safe
asIScriptEngine::Build() // reference counting needs to be thread safe

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

This topic is closed to new replies.

Advertisement