Mutex API
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_t create_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:
m.destroy();🔐 Exclusive Lock (Writer Lock)
Use these when only one thread should modify shared state.
Lock (blocking)
void mutex_t::lock();Blocks until exclusive ownership is acquired.
Try Lock (non-blocking)
bool mutex_t::try_lock();Returns true if the lock is acquired immediately, false otherwise.
Unlock
void mutex_t::unlock();Releases the exclusive lock.
📖 Shared Lock (Reader Lock)
Shared locks allow multiple readers, but automatically block if an exclusive writer holds the lock.
Lock Shared (blocking)
void mutex_t::lock_shared();Blocks until a shared lock is available.
Try Lock Shared (non-blocking)
bool mutex_t::try_lock_shared();Returns immediately with true on success.
Unlock Shared
void mutex_t::unlock_shared();Releases the shared lock.
🧩 Usage Example
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();
}
🗑 Automatic Cleanup
Even if a script forgets:
mutex_t m = create_mutex();
// ... never calls m.destroy()PCX will:
Free each
mtx_tsafelyAvoid leaks and invalid handles
Avoid Deadlocks!
You must avoid deadlocks
If an exception happens and the active thread had a lock, It will cause a deadlock
Use the following try & catch logic to avoid it.
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.");
}Last updated