if ((Player getVariable "SpareTime") > 0) then {...
« New to ArmA 2: elseifThe three ways to execute a script »

New to ArmA 2: Threads

Permalink 10/02/09 04:46, by jonas, Categories: ArmA

Threads in ArmA II? Yeah!

Yeah... I guess it is more an accident. It's an effect of the 3ms break. The engine has an internal FIFO stack (first in - first out) where all the scripts started with spawn, execVM and FSM are listed. When a script takes more than the 3ms, it will be suspended and put to the end of the stack. So we have a round-robin shedule.

So not good?

Not really, let's take a look at the following code by sbsmac[1]:

...

Code:


inCritSection = false;
runCritSectionTest = true;
 
testTask = {
     private ["_i","_z" ];
     while {runCritSectionTest} do {
    if (inCritSection) then {
         player sidechat format ["%1 already in critical section",time];
    };
    inCritSection = true ;
    // do something that takes long
    for "_i" from 1 to floor random 1000 do {
         _z = 1;
    } ;
    inCritSection = false;
    sleep 0.001;
     } ;
} ;
 
[] spawn {
     Player sidechat "running crit section test...";
     for "_c" from 1 to 50 do {
    [] spawn {[] call testTask;};
     };
     sleep 5;
     runCritSectionTest = false;
};

When we run this script, we'll encounter some "already in critical section" reports. This doesn't occur in Armed Assault where the scripts doesn't get suspended.

So what's happening?

The testTask will get suspended while being in the critical section. And the next script encounters the critical section and starts crying.

Bad? There are ways to handle threads.

We have critical sections, mutex and semaphores. At least in other languages... In ArmA II we have non of those. We can try to simulate them, e.g.:

Code:


if (not inCritSection) then {
  inCritSection = true;
  ...

This will work as long as the script won't get suspended after the if-check. But if it gets suspended, another script can enter the critical section, then it gets suspended too and the first script enters the critical section too...

Fun with threads :|

If we create scripts which may get suspended due to the 3ms break, we have to take care of this problem and make sure that no other script runs the same time which writes to the same global variables.

[1]: sbsmacs test-code

5 comments

Comment from: kju [Visitor]
kjuInteresting post.

Either it is me or the example script does really confuse one.
You may want to remove all code that is not relevant.

Also a more simple example, like two scripts reading and writing
on a GV, possibly at the same time, is more fitting.

You mean in your conclusion that we have no means to make
operations atomic right?
I guess that's true. Yet naming it that way makes it more clear.

Making semaphores yourself is quite easy as you show.
Threading is a common programmers tasks nowadays, so I see only
the shortcomings of the script language as the downside and
not having to take into account threading itself.

That said I am just a beginner in threading and scheduling.
So other people have far better insights.
10/02/09 @ 08:32
Comment from: Matthijs [Visitor]
MatthijsGood article.
Does this also influence the behaviour of functions? Or are functions never interrupted?

I would say it's a good practice never to assume anything regarding two scripts running separately. Neither for EXEC, EXECVM nor SPAWN.

This article may be a good start to begin a "Scripting Patterns and Best Practices" section at community.bistudio.com
10/02/09 @ 10:43
Comment from: jonas [Member] Email
jonas@kju:
This is an extract from sbsmacs example script. It is true that the code in the critical section is mostly useless, but it is required that the script has a high load. A more simple script will be more fitting, but it has to effect the 3ms break which is difficult again.
And yes, it's the shortcoming of the script language which causes the problem.

@Matthijs:
Functions can get interrupted too. You can see this in the above code since testTask is called (and therefor handled as function).
The meaning of the article is to show that such a problem can exists. It won't happen in most cases and shouldn't be a problem for average user.
10/02/09 @ 19:07
Comment from: sbsmac [Visitor]
sbsmacNice article - I thought that code as long dead and buried ;-)

What is more interesting/worrying, and something I haven't pursued is exactly what the level of atomicity is. The testcode that I wrote suggests that it is somewhere between a simple assignment statement and a multi-statement block.

If I had to hazard a guess it would be that the semi-colon ';' marks the end of an atomic operation but it would be interesting to write some more sophisticated tests. For example, is this code interruptable ?

_x = _a or _b or _c or (_d in _e) ; ?

@kju
>Making semaphores yourself is quite easy as you show.

Semaphores, or indeed any form of mutex mechanism, ultimately are expressed in terms of some kind of atomic test-and-set operation. It _looks_ like we have that in ArmA2 but I don't think anyone has probed it deeply.

Anyway, the fundamental lesson here is that one should always code with concurrent operation in mind.

@jonas
>It won't happen in most cases and shouldn't be a problem for average user.

Even in OFP, issues due to concurrency and non-reentrant code could occur. I remember finding a league C&H map where it was possible through careful timing to exploit the use of a shared guard variable to score very large numbers of points!
10/02/09 @ 19:31
Comment from: jonas [Member] Email
jonasI guess that the level of atomictiy is the ;
But I'm not sure about this.
May need some testing, but I'm not interested in testing it.
10/03/09 @ 02:58