Mutexes in AngelScript

In the previous example we saw how to manage simple counter state using atomic operations. For more complex state we can use a mutex to safely access data across multiple threads.

import std.threading;

// Container holds a map of counters; since we want to
// update it concurrently from multiple threads, we
// add a Mutex to synchronize access.
// Note that mutexes must not be copied, so if this
// class is passed around, it should be done by
// reference.
class Container {
    private Mutex@ mu;
    private dictionary counters;

    Container() {
        @mu = Mutex();
        counters = dictionary();
    }

    // Lock the mutex before accessing counters; unlock
    // it at the end of the function.
    void inc(const string &in name) {
        mu.lock();
        if (counters.exists(name))
            int(counters[name]) += 1;
        else
            counters[name] = 1;
        mu.unlock();
    }
}

void main() {
    Container container;
    container.counters["a"] = 0;
    container.counters["b"] = 0;

    array<Thread@> threads;

    // This function increments a named counter
    // in a loop.
    void doIncrement(Container@ c, const string &in name, int n) {
        for (int i = 0; i < n; i++) {
            c.inc(name);
        }
    }

    // Run several threads concurrently; note
    // that they all access the same Container,
    // and two of them access the same counter.
    threads.insertLast(Thread(function(Container@ c) { doIncrement(c, "a", 10000); }, @container));
    threads.insertLast(Thread(function(Container@ c) { doIncrement(c, "a", 10000); }, @container));
    threads.insertLast(Thread(function(Container@ c) { doIncrement(c, "b", 10000); }, @container));

    // Wait for the threads to finish
    for (uint i = 0; i < threads.length(); i++) {
        threads[i].join();
    }

    print("Counters: " + container.counters.getKeys()[0] + "=" + int(container.counters[container.counters.getKeys()[0]]) + 
          ", " + container.counters.getKeys()[1] + "=" + int(container.counters[container.counters.getKeys()[1]]));
}

Running the program shows that the counters updated as expected.

Counters: a=20000, b=10000

Next we’ll look at implementing this same state management task using only threads and message passing.