|
| 1 | +/* |
| 2 | + * Copyright 2026 The Emscripten Authors. All rights reserved. |
| 3 | + * Emscripten is available under two separate licenses, the MIT license and the |
| 4 | + * University of Illinois/NCSA Open Source License. Both these licenses can be |
| 5 | + * found in the LICENSE file. |
| 6 | + */ |
| 7 | + |
| 8 | +#include <emscripten/atomic.h> |
| 9 | + |
| 10 | +// Similar to emscripten_async_wait_callback_t but with a volatile first |
| 11 | +// argument. |
| 12 | +typedef void (*emscripten_async_wait_volatile_callback_t)(volatile void* address, uint32_t value, ATOMICS_WAIT_RESULT_T waitResult, void* userData); |
| 13 | + |
| 14 | +#define emscripten_lock_t volatile uint32_t |
| 15 | + |
| 16 | +// Use with syntax "emscripten_lock_t l = EMSCRIPTEN_LOCK_T_STATIC_INITIALIZER;" |
| 17 | +#define EMSCRIPTEN_LOCK_T_STATIC_INITIALIZER 0 |
| 18 | + |
| 19 | +void emscripten_lock_init(emscripten_lock_t * _Nonnull lock); |
| 20 | + |
| 21 | +// Attempts to acquire the specified lock. If the lock is free, then this |
| 22 | +// function acquires the lock and immediately returns true. If the lock is |
| 23 | +// not free at the time of the call, the calling thread is set to synchronously |
| 24 | +// sleep for at most maxWaitNanoseconds long, until another thread releases the |
| 25 | +// lock. If the lock is acquired within that period, the function returns |
| 26 | +// true. If the lock is not acquired within the specified period, then the |
| 27 | +// wait times out and false is returned. |
| 28 | +// NOTE: This function can be only called in a Worker, and not on the main |
| 29 | +// browser thread, because the main browser thread cannot synchronously |
| 30 | +// sleep to wait for locks. |
| 31 | + |
| 32 | +bool emscripten_lock_wait_acquire(emscripten_lock_t * _Nonnull lock, int64_t maxWaitNanoseconds); |
| 33 | + |
| 34 | +// Similar to emscripten_lock_wait_acquire(), but instead of waiting for at most |
| 35 | +// a specified timeout value, the thread will wait indefinitely long until the |
| 36 | +// lock can be acquired. |
| 37 | +// NOTE: The only way to abort this wait is to call |
| 38 | +// emscripten_terminate_wasm_worker() on the Worker. |
| 39 | +// NOTE: This function can be only called in a Worker, and not on the main |
| 40 | +// browser thread, because the main browser thread cannot synchronously |
| 41 | +// sleep to wait for locks. |
| 42 | +void emscripten_lock_waitinf_acquire(emscripten_lock_t * _Nonnull lock); |
| 43 | + |
| 44 | +// Similar to emscripten_lock_wait_acquire(), but instead of placing the calling |
| 45 | +// thread to sleep until the lock can be acquired, this function will burn CPU |
| 46 | +// cycles attempting to acquire the lock, until the given timeout is met. |
| 47 | +// This function can be called in both main thread and in Workers. |
| 48 | +// NOTE: The wait period used for this function is specified in milliseconds |
| 49 | +// instead of nanoseconds, see |
| 50 | +// https://github.com/WebAssembly/threads/issues/175 for details. |
| 51 | +// NOTE: If this function is called on the main thread, be sure to use a |
| 52 | +// reasonable max wait value, or otherwise a "slow script dialog" |
| 53 | +// notification can pop up, and can cause the browser to stop executing |
| 54 | +// the page. |
| 55 | +bool emscripten_lock_busyspin_wait_acquire(emscripten_lock_t * _Nonnull lock, double maxWaitMilliseconds); |
| 56 | + |
| 57 | +// Similar to emscripten_lock_wait_acquire(), but instead of placing the calling |
| 58 | +// thread to sleep until the lock can be acquired, this function will burn CPU |
| 59 | +// cycles indefinitely until the given lock can be acquired. |
| 60 | +// This function can be called in both main thread and in Workers. |
| 61 | +// NOTE: The only way to abort this wait is to call |
| 62 | +// emscripten_terminate_wasm_worker() on the Worker. If called on the main |
| 63 | +// thread, and the lock cannot be acquired within a reasonable time |
| 64 | +// period, this function will *HANG* the browser page content process, and |
| 65 | +// show up a "slow script dialog", and/or cause the browser to stop the |
| 66 | +// page. If you call this function on the main browser thread, be extra |
| 67 | +// careful to analyze that the given lock will be extremely fast to |
| 68 | +// acquire without contention from other threads. |
| 69 | +void emscripten_lock_busyspin_waitinf_acquire(emscripten_lock_t * _Nonnull lock); |
| 70 | + |
| 71 | +// Registers an *asynchronous* lock acquire operation. The calling thread will |
| 72 | +// asynchronously try to obtain the given lock after the calling thread yields |
| 73 | +// back to the event loop. If the attempt is successful within |
| 74 | +// maxWaitMilliseconds period, then the given callback asyncWaitFinished is |
| 75 | +// called with waitResult == ATOMICS_WAIT_OK. If the lock is not acquired within |
| 76 | +// the timeout period, then the callback asyncWaitFinished is called with |
| 77 | +// waitResult == ATOMICS_WAIT_TIMED_OUT. |
| 78 | +// NOTE: Unlike function emscripten_lock_wait_acquire() which takes in the wait |
| 79 | +// timeout parameter as int64 nanosecond units, this function takes in the wait |
| 80 | +// timeout parameter as double millisecond units. See |
| 81 | +// https://github.com/WebAssembly/threads/issues/175 for more information. |
| 82 | +// NOTE: This function can be called in both main thread and in Workers. |
| 83 | +// NOTE 2: This function will always acquire the lock asynchronously. That is, |
| 84 | +// the lock will only be attempted to acquire after current control flow |
| 85 | +// yields back to the browser, so that the Wasm call stack is empty. |
| 86 | +// This is to guarantee a uniform control flow. If you use this API in |
| 87 | +// a Worker, you cannot utilise an infinite loop programming model. |
| 88 | +void emscripten_lock_async_acquire(emscripten_lock_t * _Nonnull lock, |
| 89 | + emscripten_async_wait_volatile_callback_t _Nonnull asyncWaitFinished, |
| 90 | + void *userData, |
| 91 | + double maxWaitMilliseconds); |
| 92 | + |
| 93 | +// Attempts to acquire a lock, returning true if successful. If the lock is |
| 94 | +// already held, this function will not sleep to wait until the lock is |
| 95 | +// released, but immediately returns false. |
| 96 | +// This function can be called on both main thread and in Workers. |
| 97 | +bool emscripten_lock_try_acquire(emscripten_lock_t * _Nonnull lock); |
| 98 | + |
| 99 | +// Unlocks the specified lock for another thread to access. Note that locks are |
| 100 | +// extremely lightweight, there is no "lock owner" tracking: this function does |
| 101 | +// not actually check whether the calling thread owns the specified lock, but |
| 102 | +// any thread can call this function to release a lock on behalf of whichever |
| 103 | +// thread owns it. This function can be called on both main thread and in |
| 104 | +// Workers. |
| 105 | +void emscripten_lock_release(emscripten_lock_t * _Nonnull lock); |
| 106 | + |
| 107 | +#define emscripten_semaphore_t volatile uint32_t |
| 108 | + |
| 109 | +// Use with syntax emscripten_semaphore_t s = EMSCRIPTEN_SEMAPHORE_T_STATIC_INITIALIZER(num); |
| 110 | +#define EMSCRIPTEN_SEMAPHORE_T_STATIC_INITIALIZER(num) ((int)(num)) |
| 111 | + |
| 112 | +void emscripten_semaphore_init(emscripten_semaphore_t * _Nonnull sem, int num); |
| 113 | + |
| 114 | +// main thread, try acquire num instances, but do not sleep to wait if not |
| 115 | +// available. |
| 116 | +// Returns idx that was acquired or -1 if acquire failed. |
| 117 | +int emscripten_semaphore_try_acquire(emscripten_semaphore_t * _Nonnull sem, int num); |
| 118 | + |
| 119 | +// main thread, poll to try acquire num instances. Returns idx that was |
| 120 | +// acquired. If you use this API in Worker, you cannot run an infinite loop. |
| 121 | +void emscripten_semaphore_async_acquire(emscripten_semaphore_t * _Nonnull sem, |
| 122 | + int num, |
| 123 | + emscripten_async_wait_volatile_callback_t _Nonnull asyncWaitFinished, |
| 124 | + void *userData, |
| 125 | + double maxWaitMilliseconds); |
| 126 | + |
| 127 | +// worker, sleep to acquire num instances. Returns idx that was acquired, or -1 |
| 128 | +// if timed out unable to acquire. |
| 129 | +int emscripten_semaphore_wait_acquire(emscripten_semaphore_t * _Nonnull sem, int num, int64_t maxWaitNanoseconds); |
| 130 | + |
| 131 | +// worker, sleep infinitely long to acquire num instances. Returns idx that was |
| 132 | +// acquired. |
| 133 | +int emscripten_semaphore_waitinf_acquire(emscripten_semaphore_t * _Nonnull sem, int num); |
| 134 | + |
| 135 | +// Releases the given number of resources back to the semaphore. Note that the |
| 136 | +// ownership of resources is completely conceptual - there is no actual checking |
| 137 | +// that the calling thread had previously acquired that many resources, so |
| 138 | +// programs need to keep check of their semaphore usage consistency themselves. |
| 139 | +// Returns how many resources were available in the semaphore before the new |
| 140 | +// resources were released back to the semaphore. (i.e. the index where the |
| 141 | +// resource was put back to) |
| 142 | +// [main thread or worker] |
| 143 | +uint32_t emscripten_semaphore_release(emscripten_semaphore_t * _Nonnull sem, int num); |
| 144 | + |
| 145 | +// Condition variable is an object that can be waited on, and another thread can |
| 146 | +// signal, while coordinating an access to a related mutex. |
| 147 | +#define emscripten_condvar_t volatile uint32_t |
| 148 | + |
| 149 | +// Use with syntax emscripten_condvar_t cv = EMSCRIPTEN_CONDVAR_T_STATIC_INITIALIZER; |
| 150 | +#define EMSCRIPTEN_CONDVAR_T_STATIC_INITIALIZER ((int)(0)) |
| 151 | + |
| 152 | +// Creates a new condition variable to the given memory location. |
| 153 | +void emscripten_condvar_init(emscripten_condvar_t * _Nonnull condvar); |
| 154 | + |
| 155 | +// Atomically performs the following: |
| 156 | +// 1. releases the given lock. The lock should (but does not strictly need to) |
| 157 | +// be held by the calling thread prior to this call. |
| 158 | +// 2. sleep the calling thread to wait for the specified condition variable to |
| 159 | +// be signaled. |
| 160 | +// 3. once the sleep has finished (another thread has signaled the condition |
| 161 | +// variable), the calling thread wakes up and reacquires the lock prior to |
| 162 | +// returning from this function. |
| 163 | +void emscripten_condvar_waitinf(emscripten_condvar_t * _Nonnull condvar, emscripten_lock_t * _Nonnull lock); |
| 164 | + |
| 165 | +// Same as the above, except that an attempt to wait for the condition variable |
| 166 | +// to become true is only performed for a maximum duration. |
| 167 | +// On success (no timeout), this function will return true. If the wait times |
| 168 | +// out, this function will return false. In this case, |
| 169 | +// the calling thread will not try to reacquire the lock. |
| 170 | +bool emscripten_condvar_wait(emscripten_condvar_t * _Nonnull condvar, emscripten_lock_t * _Nonnull lock, int64_t maxWaitNanoseconds); |
| 171 | + |
| 172 | +// Asynchronously wait for the given condition variable to signal. |
| 173 | +ATOMICS_WAIT_TOKEN_T emscripten_condvar_wait_async(emscripten_condvar_t * _Nonnull condvar, |
| 174 | + emscripten_lock_t * _Nonnull lock, |
| 175 | + emscripten_async_wait_callback_t _Nonnull asyncWaitFinished, |
| 176 | + void *userData, |
| 177 | + double maxWaitMilliseconds); |
| 178 | + |
| 179 | +// Signals the given number of waiters on the specified condition variable. |
| 180 | +// Pass numWaitersToSignal == EMSCRIPTEN_NOTIFY_ALL_WAITERS to wake all waiters |
| 181 | +// ("broadcast" operation). |
| 182 | +void emscripten_condvar_signal(emscripten_condvar_t * _Nonnull condvar, int64_t numWaitersToSignal); |
0 commit comments