Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 0 additions & 36 deletions src/library_pthread.js
Original file line number Diff line number Diff line change
Expand Up @@ -951,42 +951,6 @@ var LibraryPThread = {
return 0;
},

// Returns the number of threads (>= 0) woken up, or the value -EINVAL on error.
// Pass count == INT_MAX to wake up all threads.
emscripten_futex_wake: function(addr, count) {
if (addr <= 0 || addr > HEAP8.length || addr&3 != 0 || count < 0) return -{{{ cDefine('EINVAL') }}};
if (count == 0) return 0;
// Waking (at least) INT_MAX waiters is defined to mean wake all callers.
// For Atomics.notify() API Infinity is to be passed in that case.
if (count >= {{{ cDefine('INT_MAX') }}}) count = Infinity;

// See if main thread is waiting on this address? If so, wake it up by resetting its wake location to zero.
// Note that this is not a fair procedure, since we always wake main thread first before any workers, so
// this scheme does not adhere to real queue-based waiting.
var mainThreadWaitAddress = Atomics.load(HEAP32, __emscripten_main_thread_futex >> 2);
var mainThreadWoken = 0;
if (mainThreadWaitAddress == addr) {
#if ASSERTIONS
// We only use __emscripten_main_thread_futex on the main browser thread, where we
// cannot block while we wait. Therefore we should only see it set from
// other threads, and not on the main thread itself. In other words, the
// main thread must never try to wake itself up!
assert(!ENVIRONMENT_IS_WEB);
#endif
var loadedAddr = Atomics.compareExchange(HEAP32, __emscripten_main_thread_futex >> 2, mainThreadWaitAddress, 0);
if (loadedAddr == mainThreadWaitAddress) {
--count;
mainThreadWoken = 1;
if (count <= 0) return 1;
}
}

// Wake any workers waiting on this address.
var ret = Atomics.notify(HEAP32, addr >> 2, count);
if (ret >= 0) return ret + mainThreadWoken;
throw 'Atomics.notify returned an unexpected value ' + ret;
},

__atomic_is_lock_free: function(size, ptr) {
return size <= 4 && (size & (size-1)) == 0 && (ptr&(size-1)) == 0;
},
Expand Down
52 changes: 52 additions & 0 deletions system/lib/pthread/emscripten_futex_wake.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/*
* Copyright 2021 The Emscripten Authors. All rights reserved.
* Emscripten is available under two separate licenses, the MIT license and the
* University of Illinois/NCSA Open Source License. Both these licenses can be
* found in the LICENSE file.
*/

#include <assert.h>
#include <errno.h>
#include <limits.h>
#include <atomic.h>
#include <emscripten/threading.h>

int _emscripten_thread_supports_atomics_wait(void);

// Stores the memory address that the main thread is waiting on, if any. If
// the main thread is waiting, we wake it up before waking up any workers.
void* _emscripten_main_thread_futex;

// Returns the number of threads (>= 0) woken up, or the value -EINVAL on error.
// Pass count == INT_MAX to wake up all threads.
int emscripten_futex_wake(volatile void *addr, int count) {
if (!addr || (((intptr_t)addr) & 3) != 0 || count < 0) {
return -EINVAL;
}
if (count == 0) {
return 0;
}

// See if main thread is waiting on this address? If so, wake it up by resetting its wake location to zero.
// Note that this is not a fair procedure, since we always wake main thread first before any workers, so
// this scheme does not adhere to real queue-based waiting.
int main_thread_woken = 0;
if (a_cas_p(&_emscripten_main_thread_futex, (void*)addr, 0) == addr) {
// We only use __emscripten_main_thread_futex on the main browser thread,
// where we cannot block while we wait. Therefore we should only see it set
// from other threads (that should always support waiting), and not on the
// main thread itself. In other words, the main thread must never try to
// wake itself up!
assert(_emscripten_thread_supports_atomics_wait());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

At some point we might want to audit our code to try to make it work correctly from audio worklets, which also can't block.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah.. this mechanism assumes only one (busy-looping) main thread I think, so it would need to be revisited.

Copy link
Contributor

@Jonathhhan Jonathhhan Oct 6, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess I came across the audioworklet issue:

SimpleSequencer.js:1 Uncaught RuntimeError: Aborted(Assertion failed: _emscripten_thread_supports_atomics_wait(), at: 
/home/jonathan/emsdk/upstream/emscripten/system/lib/pthread/emscripten_futex_wake.c,40,emscripten_futex_wake)
    at abort (SimpleSequencer.js:1:23555)
    at ___assert_fail (SimpleSequencer.js:1:92185)
    at emscripten_futex_wake (SimpleSequencer.wasm:0x5fcc66)
    at __wake (SimpleSequencer.wasm:0x5fc80b)
    at __pthread_mutex_unlock (SimpleSequencer.wasm:0x6041a7)
    at sys_unlock (SimpleSequencer.wasm:0x4b4743)
    at ofxPd::audioOut(float*, int, int) (SimpleSequencer.wasm:0x43abb3)
    at ofxOfelia::audioOut(ofSoundBuffer&) (SimpleSequencer.wasm:0x691b3)
    at std::__2::__function::__func<std::__2::__bind<void (ofBaseSoundInput::*)(ofSoundBuffer&), ofBaseSoundInput*&, std::__2::placeholders::__ph<1> const&>, std::__2::allocator<std::__2::__bind<void (ofBaseSoundInput::*)(ofSoundBuffer&), ofBaseSoundInput*&, std::__2::placeholders::__ph<1> const&>>, void (ofSoundBuffer&)>::operator()(ofSoundBuffer&) (SimpleSequencer.wasm:0x63b62)
    at ofxEmscriptenSoundStream::audio_cb(int, int, int, void*) (SimpleSequencer.wasm:0x637e0)

It would be absolutely great, if this gets fixed. Because the app I am using with Emscripten seems to rely on that. And I am a bit lost with fixing it by myself.

--count;
main_thread_woken = 1;
if (count <= 0) {
return 1;
}
}

// Wake any workers waiting on this address.
int ret = __builtin_wasm_memory_atomic_notify((int*)addr, count);
assert(ret >= 0);
return ret + main_thread_woken;
}
4 changes: 0 additions & 4 deletions system/lib/pthread/library_pthread.c
Original file line number Diff line number Diff line change
Expand Up @@ -831,10 +831,6 @@ int emscripten_dispatch_to_thread_async_(pthread_t target_thread,
return ret;
}

// Stores the memory address that the main thread is waiting on, if any. If
// the main thread is waiting, we wake it up before waking up any workers.
EMSCRIPTEN_KEEPALIVE void* _emscripten_main_thread_futex;

void __emscripten_init_main_thread_js(void* tb);

static void *dummy_tsd[1] = { 0 };
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ G
H
I
J
K
p
q
r
s
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ $emscripten_async_run_in_main_thread
$emscripten_current_thread_process_queued_calls
$emscripten_dispatch_to_thread_
$emscripten_futex_wait
$emscripten_futex_wake
$emscripten_main_thread_process_queued_calls
$emscripten_proxy_main
$emscripten_run_in_main_runtime_thread_js
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,3 @@ a.l
a.m
a.n
a.o
a.p
Original file line number Diff line number Diff line change
@@ -1 +1 @@
49636
48861
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,3 @@ l
m
n
o
p
Original file line number Diff line number Diff line change
@@ -1 +1 @@
16846
16911
1 change: 1 addition & 0 deletions tools/system_libs.py
Original file line number Diff line number Diff line change
Expand Up @@ -828,6 +828,7 @@ def get_files(self):
'emscripten_proxy_main.c',
'emscripten_thread_state.S',
'emscripten_futex_wait.c',
'emscripten_futex_wake.c',
])
else:
ignore += ['thread']
Expand Down