1- #include < emscripten/webaudio.h>
21#include < emscripten/threading.h>
2+ #include < emscripten/wasm_worker.h>
3+ #include < emscripten/webaudio.h>
34#include < stdio.h>
45#include < stdlib.h>
56#include < assert.h>
67
7- // Tests that
8- // - _emscripten_thread_supports_atomics_wait() returns true in a Wasm Audio Worklet.
9- // - emscripten_futex_wake() does not crash in a Wasm Audio Worklet.
10- // - emscripten_futex_wait() does not crash in a Wasm Audio Worklet.
11- // - emscripten_get_now() does not crash in a Wasm Audio Worklet.
12-
13- int futexLocation = 0 ;
14- int testSuccess = 0 ;
8+ // Tests that these audio worklet compatible functions work, details in comments below:
9+ //
10+ // - _emscripten_thread_supports_atomics_wait()
11+ // - emscripten_lock_init()
12+ // - emscripten_lock_try_acquire()
13+ // - emscripten_lock_busyspin_wait_acquire()
14+ // - emscripten_lock_busyspin_waitinf_acquire()
15+ // - emscripten_lock_release()
16+ // - emscripten_get_now()
1517
1618// Internal, found in 'system/lib/pthread/threading_internal.h'
17- extern " C" int _emscripten_thread_supports_atomics_wait ();
19+ extern " C" int _emscripten_thread_supports_atomics_wait (void );
1820
19- bool ProcessAudio (int numInputs, const AudioSampleFrame *inputs, int numOutputs, AudioSampleFrame *outputs, int numParams, const AudioParamFrame *params, void *userData) {
20- int supportsAtomicWait = _emscripten_thread_supports_atomics_wait ();
21- printf (" supportsAtomicWait: %d\n " , supportsAtomicWait);
22- assert (!supportsAtomicWait);
23- emscripten_futex_wake (&futexLocation, 1 );
24- printf (" %f\n " , emscripten_get_now ());
21+ typedef enum {
22+ // No wait support in audio worklets
23+ TEST_HAS_WAIT,
24+ // Acquired in main, fail in process
25+ TEST_TRY_ACQUIRE,
26+ // Keep acquired so time-out
27+ TEST_WAIT_ACQUIRE_FAIL,
28+ // Release in main, succeed in process
29+ TEST_WAIT_ACQUIRE,
30+ // Release in process after above
31+ TEST_RELEASE,
32+ // Released in process above, spin in main
33+ TEST_WAIT_INFINTE_1,
34+ // Release in process to stop spinning in main
35+ TEST_WAIT_INFINTE_2,
36+ // Call emscripten_get_now() in process
37+ TEST_GET_NOW,
38+ // Test finished
39+ TEST_DONE
40+ } Test;
2541
26- emscripten_futex_wait (&futexLocation, 1 , /* maxWaitMs=*/ 2 );
27- testSuccess = 1 ;
42+ // Lock used in all the tests
43+ emscripten_lock_t testLock = EMSCRIPTEN_LOCK_T_STATIC_INITIALIZER;
44+ // Which test is running (sometimes in the worklet, sometimes in the main thread)
45+ _Atomic Test whichTest = TEST_HAS_WAIT;
46+ // Time at which the test starts taken in main()
47+ double startTime = 0 ;
2848
29- return false ;
49+ bool ProcessAudio (int numInputs, const AudioSampleFrame *inputs, int numOutputs, AudioSampleFrame *outputs, int numParams, const AudioParamFrame *params, void *userData) {
50+ int result = 0 ;
51+ switch (whichTest) {
52+ case TEST_HAS_WAIT:
53+ // Should not have wait support here
54+ result = _emscripten_thread_supports_atomics_wait ();
55+ printf (" TEST_HAS_WAIT: %d (expect: 0)\n " , result);
56+ assert (!result);
57+ whichTest = TEST_TRY_ACQUIRE;
58+ break ;
59+ case TEST_TRY_ACQUIRE:
60+ // Was locked after init, should fail to acquire
61+ result = emscripten_lock_try_acquire (&testLock);
62+ printf (" TEST_TRY_ACQUIRE: %d (expect: 0)\n " , result);
63+ assert (!result);
64+ whichTest = TEST_WAIT_ACQUIRE_FAIL;
65+ break ;
66+ case TEST_WAIT_ACQUIRE_FAIL:
67+ // Still locked so we fail to acquire
68+ result = emscripten_lock_busyspin_wait_acquire (&testLock, 100 );
69+ printf (" TEST_WAIT_ACQUIRE_FAIL: %d (expect: 0)\n " , result);
70+ assert (!result);
71+ whichTest = TEST_WAIT_ACQUIRE;
72+ case TEST_WAIT_ACQUIRE:
73+ // Will get unlocked in main thread, so should quickly acquire
74+ result = emscripten_lock_busyspin_wait_acquire (&testLock, 100 );
75+ printf (" TEST_WAIT_ACQUIRE: %d (expect: 1)\n " , result);
76+ assert (result);
77+ whichTest = TEST_RELEASE;
78+ break ;
79+ case TEST_RELEASE:
80+ // Unlock, check the result
81+ emscripten_lock_release (&testLock);
82+ result = emscripten_lock_try_acquire (&testLock);
83+ printf (" TEST_RELEASE: %d (expect: 1)\n " , result);
84+ assert (result);
85+ whichTest = TEST_WAIT_INFINTE_1;
86+ break ;
87+ case TEST_WAIT_INFINTE_1:
88+ // Still locked when we enter here but move on in the main thread
89+ break ;
90+ case TEST_WAIT_INFINTE_2:
91+ emscripten_lock_release (&testLock);
92+ whichTest = TEST_GET_NOW;
93+ break ;
94+ case TEST_GET_NOW:
95+ result = (int ) (emscripten_get_now () - startTime);
96+ printf (" TEST_GET_NOW: %d (expect: > 0)\n " , result);
97+ assert (result > 0 );
98+ whichTest = TEST_DONE;
99+ case TEST_DONE:
100+ return false ;
101+ default :
102+ break ;
103+ }
104+ return true ;
30105}
31106
32107EM_JS (void , InitHtmlUi, (EMSCRIPTEN_WEBAUDIO_T audioContext), {
@@ -40,34 +115,50 @@ EM_JS(void, InitHtmlUi, (EMSCRIPTEN_WEBAUDIO_T audioContext), {
40115 };
41116});
42117
43- bool PollTestSuccess (double , void *) {
44- if (testSuccess) {
45- printf (" Test success!\n " );
46- #ifdef REPORT_RESULT
47- REPORT_RESULT (0 );
48- #endif
118+ bool MainLoop (double time, void * data) {
119+ switch (whichTest) {
120+ case TEST_WAIT_ACQUIRE:
121+ // Release here to acquire in process
122+ emscripten_lock_release (&testLock);
123+ break ;
124+ case TEST_WAIT_INFINTE_1:
125+ // Spin here until released in process (but don't change test until we know this case ran)
126+ whichTest = TEST_WAIT_INFINTE_2;
127+ emscripten_lock_busyspin_waitinf_acquire (&testLock);
128+ printf (" TEST_WAIT_INFINTE (from main)\n " );
129+ break ;
130+ case TEST_DONE:
49131 return false ;
132+ default :
133+ break ;
50134 }
51135 return true ;
52136}
53137
54138void AudioWorkletProcessorCreated (EMSCRIPTEN_WEBAUDIO_T audioContext, bool success, void *userData) {
55139 int outputChannelCounts[1 ] = { 1 };
56140 EmscriptenAudioWorkletNodeCreateOptions options = { .numberOfInputs = 0 , .numberOfOutputs = 1 , .outputChannelCounts = outputChannelCounts };
57- EMSCRIPTEN_AUDIO_WORKLET_NODE_T wasmAudioWorklet = emscripten_create_wasm_audio_worklet_node (audioContext, " noise-generator" , &options, &ProcessAudio, 0 );
141+ EMSCRIPTEN_AUDIO_WORKLET_NODE_T wasmAudioWorklet = emscripten_create_wasm_audio_worklet_node (audioContext, " noise-generator" , &options, &ProcessAudio, NULL );
58142 emscripten_audio_node_connect (wasmAudioWorklet, audioContext, 0 , 0 );
59143 InitHtmlUi (audioContext);
60144}
61145
62146void WebAudioWorkletThreadInitialized (EMSCRIPTEN_WEBAUDIO_T audioContext, bool success, void *userData) {
63147 WebAudioWorkletProcessorCreateOptions opts = { .name = " noise-generator" };
64- emscripten_create_wasm_audio_worklet_processor_async (audioContext, &opts, AudioWorkletProcessorCreated, 0 );
148+ emscripten_create_wasm_audio_worklet_processor_async (audioContext, &opts, AudioWorkletProcessorCreated, NULL );
65149}
66150
67- uint8_t wasmAudioWorkletStack[4096 ];
151+ uint8_t wasmAudioWorkletStack[2048 ];
68152
69153int main () {
70- emscripten_set_timeout_loop (PollTestSuccess, 10 , 0 );
71- EMSCRIPTEN_WEBAUDIO_T context = emscripten_create_audio_context (0 );
72- emscripten_start_wasm_audio_worklet_thread_async (context, wasmAudioWorkletStack, sizeof (wasmAudioWorkletStack), WebAudioWorkletThreadInitialized, 0 );
154+ // Main thread init and acquire (work passes to the processor)
155+ emscripten_lock_init (&testLock);
156+ int hasLock = emscripten_lock_busyspin_wait_acquire (&testLock, 0 );
157+ assert (hasLock);
158+
159+ startTime = emscripten_get_now ();
160+
161+ emscripten_set_timeout_loop (MainLoop, 10 , NULL );
162+ EMSCRIPTEN_WEBAUDIO_T context = emscripten_create_audio_context (NULL );
163+ emscripten_start_wasm_audio_worklet_thread_async (context, wasmAudioWorkletStack, sizeof (wasmAudioWorkletStack), WebAudioWorkletThreadInitialized, NULL );
73164}
0 commit comments