Skip to content

Commit 9d120b0

Browse files
committed
Fix a deadlock issue with emscripten_lock_async_acquire() if user attempted to synchronously acquire the lock right after asynchronously acquiring it. b25abd5#r168975511
1 parent 79bf3e0 commit 9d120b0

File tree

3 files changed

+40
-8
lines changed

3 files changed

+40
-8
lines changed

src/lib/libwasm_worker.js

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -300,24 +300,21 @@ if (ENVIRONMENT_IS_WASM_WORKER
300300

301301
emscripten_lock_async_acquire__deps: ['$polyfillWaitAsync'],
302302
emscripten_lock_async_acquire: (lock, asyncWaitFinished, userData, maxWaitMilliseconds) => {
303-
let dispatch = (val, ret) => {
304-
setTimeout(() => {
305-
{{{ makeDynCall('vpiip', 'asyncWaitFinished') }}}(lock, val, /*waitResult=*/ret, userData);
306-
}, 0);
307-
};
308303
let tryAcquireLock = () => {
309304
do {
310305
var val = Atomics.compareExchange(HEAP32, {{{ getHeapOffset('lock', 'i32') }}}, 0/*zero represents lock being free*/, 1/*one represents lock being acquired*/);
311-
if (!val) return dispatch(0, 0/*'ok'*/);
306+
if (!val) return {{{ makeDynCall('vpiip', 'asyncWaitFinished') }}}(lock, 0, 0/*'ok'*/, userData);
312307
var wait = Atomics.waitAsync(HEAP32, {{{ getHeapOffset('lock', 'i32') }}}, val, maxWaitMilliseconds);
313308
} while (wait.value === 'not-equal');
314309
#if ASSERTIONS
315310
assert(wait.async || wait.value === 'timed-out');
316311
#endif
317312
if (wait.async) wait.value.then(tryAcquireLock);
318-
else dispatch(val, 2/*'timed-out'*/);
313+
else return {{{ makeDynCall('vpiip', 'asyncWaitFinished') }}}(lock, val, 2/*'timed-out'*/, userData);
319314
};
320-
tryAcquireLock();
315+
// Asynchronously dispatch acquiring the lock so that we have uniform control flow in both
316+
// cases when the lock is acquired, and when it needs to wait.
317+
setTimeout(tryAcquireLock);
321318
},
322319

323320
emscripten_semaphore_async_acquire__deps: ['$polyfillWaitAsync'],

test/test_browser.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5329,6 +5329,12 @@ def test_wasm_worker_lock_wait2(self):
53295329
def test_wasm_worker_lock_async_acquire(self):
53305330
self.btest_exit('wasm_worker/lock_async_acquire.c', cflags=['--closure=1', '-sWASM_WORKERS'])
53315331

5332+
# Tests emscripten_lock_async_acquire() function when lock is acquired both synchronously and asynchronously.
5333+
@also_with_minimal_runtime
5334+
@flaky('https://github.com/emscripten-core/emscripten/issues/25270')
5335+
def test_wasm_worker_lock_async_and_sync_acquire(self):
5336+
self.btest('wasm_worker/lock_async_and_sync_acquire.c', expected='1', cflags=['--closure=1', '-sWASM_WORKERS'])
5337+
53325338
# Tests emscripten_lock_busyspin_wait_acquire() in Worker and main thread.
53335339
@also_with_minimal_runtime
53345340
def test_wasm_worker_lock_busyspin_wait(self):
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
#include <emscripten/wasm_worker.h>
2+
#include <emscripten/threading.h>
3+
#include <stdio.h>
4+
5+
emscripten_lock_t lock = EMSCRIPTEN_LOCK_T_STATIC_INITIALIZER;
6+
7+
int result = 0;
8+
9+
void on_acquire(volatile void* address, uint32_t value,
10+
ATOMICS_WAIT_RESULT_T waitResult, void* userData) {
11+
printf("on_acquire: releasing lock.\n");
12+
emscripten_lock_release(&lock);
13+
printf("on_acquire: released lock.\n");
14+
#ifdef REPORT_RESULT
15+
REPORT_RESULT(result);
16+
#endif
17+
}
18+
19+
int main() {
20+
printf("main: async acquiring lock.\n");
21+
emscripten_lock_async_acquire(&lock, on_acquire, 0, 100);
22+
printf("main: busy-spin acquiring lock.\n");
23+
emscripten_lock_busyspin_waitinf_acquire(&lock);
24+
printf("main: lock acquired.\n");
25+
emscripten_lock_release(&lock);
26+
printf("main: lock released.\n");
27+
result += 1;
28+
emscripten_exit_with_live_runtime();
29+
}

0 commit comments

Comments
 (0)