Skip to content

Threading

IllidanS4 edited this page Mar 18, 2018 · 4 revisions

A thread is an object that allows parallel execution of a specified piece of code. In standard programming languages, a thread is constructed by specifying the code which should be run, and provides methods to control or observe the exceution.

However, since Pawn and AMX weren't designed with the support for multi-threaded programs in mind, the support for parallel execution is still limited in this plugin. First of all, you cannot create new threads directly, and only one piece of code can be running at a time on every AMX machine.

Running threaded code

Beause of the limitations, instead of specifying the function containing the code that should be run concurrently, the execution of the code is "transferred" to a new thread, which will then run it in parallel with the main server's thread.

print("begin");
threaded(false)
{
    for(new i = 0; i < 10000; i++) printf("%d", i);
}
print("end");

The threaded pseudo-statement is a convenient syntactic shortcut to enclosing a block in a pair of calls to thread_detach/thread_attach. The first function "detaches" the code from the main thread to a separate thread, and the second one does the opposite.

Synchronisation

Notice the argument in threaded(false). In a threaded block, you may want to call native functions, but doing so could introduce race conditions (effects when the result of a process can be affected by the order of concurrent operations). For functions like GetPlayerPos, strcmp, or gettime, which have no side effects, it doesn't matter, but changing e.g. a server rule when somebody wants to read it may produce inconsistent behaviour.

To address this reason, using thread(true) will run the thread in the auto-sync mode, which will synchronize all native calls with the process tick of the server. The main code will run in parallel, but any native call will pause the execution and wait until the next process tick happens, then executes the call and resumes the thread.

Use the auto-sync mode whenever you only have a small amount of non-CPU-extensive native calls, and the work is done only by your script. If the work is done by a native function instead, use threaded(false), because otherwise the function would be run synchronously.

Callbacks

At the moment, due to the shared heap-stack memory block, at most one thread is allowed to execute code in a single AMX machine, including the main thread. This means that if a callback is to be executed, it cannot do so until there is no thread running a code in the AMX. Currently, the plugin will wait with the execution until the thread is paused, since pausing the thread itself is problematic.

The thread is paused whenever a native call in the auto-sync mode happens, when the threaded block ends, or when thread_sync is called. This native function pauses the thread until the next tick happens, doing effectively nothing in itself in the auto-sync mode (since the synchronisation happens there anyway).

Consider the following code:

stock delay(ticks)
{
    new end = GetTickCount()+ticks;
    while(GetTickCount() < end) {}
}

forward Work();
public Work()
{
    printf("A");
    threaded(false)
    {
        printf("B");
        delay(500);
        printf("C");
        delay(500);
        printf("D");
        delay(500);
        printf("E");
    }
    printf("F");
}

forward Timer();
public Timer()
{
    printf("X");
}

public OnFilterScriptInit()
{
    CallLocalFunction(#Work, "");
    SetTimer(#Timer, 1000, false);
}

Work does no synchronisation, so Timer cannot be executed until the block ends. The output is ABCDEXF.

However, when thread_sync(); is put before printf("E");, it pauses the execution and executes any pending calls, resulting in the output ABCDXEF.

Tasks

At the moment, threads and tasks are incompatible. Using await or doing any calls that suspend the execution of the code is not supported in a threaded block, and other task functions like task_set_result may be only called in the auto-sync mode.

Strings

Standard string modifying and comparison operations are thread-safe (when not done on the same string concurrently), but any function that accesses the string pool is not thread-safe.

Clone this wiki locally