The Mutex API provides lightweight thread synchronization inside AngelScript.
Mutex objects are native engine primitives exposed as mutex_t handles.
They support:
Exclusive locks (writers)
Shared locks (readers)
Non-blocking try-lock operations
Automatic cleanup on script shutdown
Mutexes are created by create_mutex() and manually released using destroy()
π Creating Mutexes
Create
mutex_tcreate_mutex();
Creates a new mutex and registers it with the current AngelScript context.
Example:
mutex_t m =create_mutex();
Destroy
void mutex_t::destroy();
Frees the underlying native mutex.
Note: If the script forgets to call destroy(), the PCX engine frees all remaining mutexes when the script context unloads.
Example:
π Exclusive Lock (Writer Lock)
Use these when only one thread should modify shared state.
Lock (blocking)
Blocks until exclusive ownership is acquired.
Try Lock (non-blocking)
Returns true if the lock is acquired immediately, false otherwise.
Unlock
Releases the exclusive lock.
π Shared Lock (Reader Lock)
Shared locks allow multiple readers, but automatically block if an exclusive writer holds the lock.
mutex_t g_mtx = create_mutex();
int sharedValue = 0;
void writer_thread(int callback_id, int data_index)
{
g_mtx.lock();
log("[writer] acquired exclusive lock");
sharedValue += 10;
g_mtx.unlock();
log("[writer] released lock");
}
void reader_thread(int callback_id, int data_index)
{
if (g_mtx.try_lock_shared())
{
log("[reader] acquired shared lock");
int v = sharedValue; // safe read
g_mtx.unlock_shared();
log("[reader] released shared lock");
}
else
{
log("[reader] shared lock unavailable");
}
}
int main()
{
register_callback(writer_thread, 1, 0);
register_callback(reader_thread, 1, 0);
return 1;
}
void on_unload()
{
log("unloaded");
// Optional: manual cleanup
g_mtx.destroy();
}
mutex_t m = create_mutex();
// ... never calls m.destroy()
mutex_t g_example_mutex;
// A safe function that guarantees the mutex is always unlocked.
void do_work_safe(bool should_throw)
{
log("Attempting to lock the mutex...");
g_example_mutex.lock();
log("Mutex locked.");
try
{
if (should_throw)
{
log("An error occurred! Throwing an exception...");
throw("Error");
}
}
catch
{
log("Caught an exception, but the unlock call will still be reached.");
}
// This code is now guaranteed to run, preventing a deadlock.
g_example_mutex.unlock();
log("Mutex unlocked.");
}
void main()
{
g_example_mutex = create_mutex();
// 1. Call the function and make it throw an exception.
// The function will catch its own exception and unlock the mutex.
do_work_safe(true);
// 2. Call it again. This will now succeed because the mutex was released.
log("Calling the function again. This will work correctly.");
do_work_safe(false);
log("Program finished successfully.");
}