From fdc2ea7e9054c4a08e8ee88a3faafc4642114e83 Mon Sep 17 00:00:00 2001 From: Sam Clegg Date: Wed, 7 Feb 2024 17:39:42 -0800 Subject: [PATCH] Cleanup some pthread tests. NFC (#21293) --- test/pthread/test_pthread_64bit_atomics.c | 155 +++++++++++++++++ test/pthread/test_pthread_64bit_atomics.cpp | 162 ------------------ ...hread_malloc.cpp => test_pthread_malloc.c} | 20 +-- test/pthread/test_pthread_malloc_free.cpp | 21 +-- ...t_pthread_once.cpp => test_pthread_once.c} | 32 ++-- .../pthread/test_pthread_run_on_main_thread.c | 145 ++++++++++++++++ .../test_pthread_run_on_main_thread.cpp | 159 ----------------- .../test_pthread_run_on_main_thread_flood.c | 89 ++++++++++ .../test_pthread_run_on_main_thread_flood.cpp | 100 ----------- ...d_volatile.cpp => test_pthread_volatile.c} | 9 +- test/test_browser.py | 19 +- 11 files changed, 435 insertions(+), 476 deletions(-) create mode 100644 test/pthread/test_pthread_64bit_atomics.c delete mode 100644 test/pthread/test_pthread_64bit_atomics.cpp rename test/pthread/{test_pthread_malloc.cpp => test_pthread_malloc.c} (82%) rename test/pthread/{test_pthread_once.cpp => test_pthread_once.c} (50%) create mode 100644 test/pthread/test_pthread_run_on_main_thread.c delete mode 100644 test/pthread/test_pthread_run_on_main_thread.cpp create mode 100644 test/pthread/test_pthread_run_on_main_thread_flood.c delete mode 100644 test/pthread/test_pthread_run_on_main_thread_flood.cpp rename test/pthread/{test_pthread_volatile.cpp => test_pthread_volatile.c} (87%) diff --git a/test/pthread/test_pthread_64bit_atomics.c b/test/pthread/test_pthread_64bit_atomics.c new file mode 100644 index 000000000000..d1bd28dd99f1 --- /dev/null +++ b/test/pthread/test_pthread_64bit_atomics.c @@ -0,0 +1,155 @@ +// Copyright 2015 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 +#include +#include +#include +#include +#include +#include +#include + +#define NUM_THREADS 8 +#define DATA_COUNT 10 + +volatile double globalDouble = 0.0; +volatile uint64_t globalU64 = 0; + +uint64_t sharedData[DATA_COUNT] = {}; + +struct Test { + int op; + int threadId; +}; + +uint64_t threadCasAccumulatedWrittenData[NUM_THREADS] = {}; +uint64_t threadCasAccumulatedReadData[NUM_THREADS] = {}; + +int64_t rand_60() { + return (int64_t)(emscripten_random() * (float)0x3FFFFFFF) | ((int64_t)(emscripten_random() * (float)0x3FFFFFFF) << 30); +} + +void *ThreadMain(void *arg) { + assert(pthread_self() != 0); + assert(globalDouble == 5.0); + assert(globalU64 == 5); + struct Test *t = (struct Test*)arg; + emscripten_outf("Thread %d for test %d: starting computation", t->threadId, t->op); + + for (int i = 0; i < 99999; ++i) { + for (int j = 0; j < DATA_COUNT; ++j) { + switch (t->op) { + case 0: emscripten_atomic_add_u64(&sharedData[j], 1); break; + case 1: emscripten_atomic_sub_u64(&sharedData[j], 1); break; + case 2: emscripten_atomic_and_u64(&sharedData[j], ~(1UL << t->threadId)); break; + case 3: emscripten_atomic_or_u64(&sharedData[j], 1UL << t->threadId); break; + case 4: emscripten_atomic_xor_u64(&sharedData[j], 1UL << t->threadId); break; + case 5: { + // Atomically load and store data, and test that each individual u8 is the same. + uint64_t data = emscripten_atomic_load_u64(&sharedData[j]); + uint8_t dataU8[8]; + memcpy(dataU8, &data, 8); + assert(dataU8[0] >= 10 && dataU8[0] < 10+NUM_THREADS); + assert(dataU8[0] == dataU8[1] && dataU8[0] == dataU8[2] && dataU8[0] == dataU8[3]); + assert(dataU8[0] == dataU8[4] && dataU8[0] == dataU8[5] && dataU8[0] == dataU8[6] && dataU8[0] == dataU8[7]); + dataU8[0] = dataU8[1] = dataU8[2] = dataU8[3] = dataU8[4] = dataU8[5] = dataU8[6] = dataU8[7] = 10 + t->threadId; + memcpy(&data, dataU8, 8); + emscripten_atomic_store_u64(&sharedData[j], data); + } + break; + case 6: { + uint64_t newData = rand_60(); + uint64_t data; + uint64_t prevData; + do { + data = emscripten_atomic_load_u64(&sharedData[j]); + prevData = emscripten_atomic_cas_u64(&sharedData[j], data, newData); + } while(prevData != data); + threadCasAccumulatedReadData[t->threadId] += data; + threadCasAccumulatedWrittenData[t->threadId] += newData; + } + break; + } + } + } + emscripten_outf("Thread %d for test %d: finished, exit()ing", t->threadId, t->op); + pthread_exit(0); +} + +struct Test t[NUM_THREADS] = {}; +pthread_t thread[NUM_THREADS]; + +void RunTest(int test) { + pthread_attr_t attr; + pthread_attr_init(&attr); + pthread_attr_setstacksize(&attr, 4*1024); + + emscripten_outf("Main thread has thread ID %p\n", pthread_self()); + assert(pthread_self() != 0); + + switch(test) { + case 2: memset(sharedData, 0xFF, sizeof(sharedData)); break; + case 5: memset(sharedData, 0x10, sizeof(sharedData)); break; + default: memset(sharedData, 0, sizeof(sharedData)); break; + } + + emscripten_outf("Main: Starting test %d", test); + + for (int i = 0; i < NUM_THREADS; ++i) { + t[i].op = test; + t[i].threadId = i; + int rc = pthread_create(&thread[i], &attr, ThreadMain, &t[i]); + assert(rc == 0); + } + + pthread_attr_destroy(&attr); + + for (int i = 0; i < NUM_THREADS; ++i) { + int status = 1; + int rc = pthread_join(thread[i], (void**)&status); + assert(rc == 0); + assert(status == 0); + } + + int val = sharedData[0]; + emscripten_outf("Main: Test %d finished. Result: %d", test, val); + if (test != 6) { + for (int i = 1; i < DATA_COUNT; ++i) { + assert(sharedData[i] == sharedData[0]); + } + } +} + +int main() { + globalDouble = 5.0; + globalU64 = 4; + + uint64_t prevU64 = emscripten_atomic_add_u64((void*)&globalU64, 1); + assert(prevU64 == 4); + + for (int i = 0; i < 7; ++i) { + RunTest(i); + } + + uint64_t totalRead = 0; + uint64_t totalWritten = 0; + for (int i = 0; i < NUM_THREADS; ++i) { + totalRead += threadCasAccumulatedReadData[i]; + totalWritten += threadCasAccumulatedWrittenData[i]; + } + for (int i = 0; i < DATA_COUNT; ++i) { + totalRead += sharedData[i]; + } + + if (totalRead == totalWritten) { + emscripten_outf("totalRead: %llu, totalWritten: %llu\n", totalRead, totalWritten); + } else { + emscripten_outf("64-bit CAS test failed! totalRead != totalWritten (%llu != %llu)\n", totalRead, totalWritten); + } + assert(totalRead == totalWritten); + emscripten_outf("Main: Test successfully finished"); + return 0; +} diff --git a/test/pthread/test_pthread_64bit_atomics.cpp b/test/pthread/test_pthread_64bit_atomics.cpp deleted file mode 100644 index 207a2383a853..000000000000 --- a/test/pthread/test_pthread_64bit_atomics.cpp +++ /dev/null @@ -1,162 +0,0 @@ -// Copyright 2015 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 -#include -#include -#include -#include -#include -#include -#include - -#define NUM_THREADS 8 - -volatile double globalDouble = 0.0; -volatile uint64_t globalU64 = 0; - -const int N = 10; -uint64_t sharedData[N] = {}; - -struct Test -{ - int op; - int threadId; -}; - -uint64_t threadCasAccumulatedWrittenData[NUM_THREADS] = {}; -uint64_t threadCasAccumulatedReadData[NUM_THREADS] = {}; - -int64_t rand_60() -{ - return (int64_t)(emscripten_random() * float(0x3FFFFFFF)) | ((int64_t)(emscripten_random() * float(0x3FFFFFFF)) << 30); -} - -void *ThreadMain(void *arg) -{ - assert(pthread_self() != 0); - assert(globalDouble == 5.0); - assert(globalU64 == 5); - struct Test *t = (struct Test*)arg; - emscripten_outf("Thread %d for test %d: starting computation", t->threadId, t->op); - - for(int i = 0; i < 99999; ++i) - for(int j = 0; j < N; ++j) - { - switch(t->op) - { - case 0: emscripten_atomic_add_u64(&sharedData[j], 1); break; - case 1: emscripten_atomic_sub_u64(&sharedData[j], 1); break; - case 2: emscripten_atomic_and_u64(&sharedData[j], ~(1UL << t->threadId)); break; - case 3: emscripten_atomic_or_u64(&sharedData[j], 1UL << t->threadId); break; - case 4: emscripten_atomic_xor_u64(&sharedData[j], 1UL << t->threadId); break; - case 5: - { - // Atomically load and store data, and test that each individual u8 is the same. - uint64_t data = emscripten_atomic_load_u64(&sharedData[j]); - uint8_t dataU8[8]; - memcpy(dataU8, &data, 8); - assert(dataU8[0] >= 10 && dataU8[0] < 10+NUM_THREADS); - assert(dataU8[0] == dataU8[1] && dataU8[0] == dataU8[2] && dataU8[0] == dataU8[3]); - assert(dataU8[0] == dataU8[4] && dataU8[0] == dataU8[5] && dataU8[0] == dataU8[6] && dataU8[0] == dataU8[7]); - dataU8[0] = dataU8[1] = dataU8[2] = dataU8[3] = dataU8[4] = dataU8[5] = dataU8[6] = dataU8[7] = 10 + t->threadId; - memcpy(&data, dataU8, 8); - emscripten_atomic_store_u64(&sharedData[j], data); - } - break; - case 6: - { - uint64_t newData = rand_60(); - uint64_t data; - uint64_t prevData; - do { - data = emscripten_atomic_load_u64(&sharedData[j]); - prevData = emscripten_atomic_cas_u64(&sharedData[j], data, newData); - } while(prevData != data); - threadCasAccumulatedReadData[t->threadId] += data; - threadCasAccumulatedWrittenData[t->threadId] += newData; - } - break; - } - } - emscripten_outf("Thread %d for test %d: finished, exit()ing", t->threadId, t->op); - pthread_exit(0); -} - -struct Test t[NUM_THREADS] = {}; -pthread_t thread[NUM_THREADS]; - -void RunTest(int test) -{ - pthread_attr_t attr; - pthread_attr_init(&attr); - pthread_attr_setstacksize(&attr, 4*1024); - - emscripten_outf("Main thread has thread ID %ld\n", pthread_self()); - assert(pthread_self() != 0); - - switch(test) - { - case 2: memset(sharedData, 0xFF, sizeof(sharedData)); break; - case 5: memset(sharedData, 0x10, sizeof(sharedData)); break; - default: memset(sharedData, 0, sizeof(sharedData)); break; - } - - emscripten_outf("Main: Starting test %d", test); - - for(int i = 0; i < NUM_THREADS; ++i) - { - t[i].op = test; - t[i].threadId = i; - int rc = pthread_create(&thread[i], &attr, ThreadMain, &t[i]); - assert(rc == 0); - } - - pthread_attr_destroy(&attr); - - for(int i = 0; i < NUM_THREADS; ++i) - { - int status = 1; - int rc = pthread_join(thread[i], (void**)&status); - assert(rc == 0); - assert(status == 0); - } - - int val = sharedData[0]; - emscripten_outf("Main: Test %d finished. Result: %d", test, val); - if (test != 6) - { - for(int i = 1; i < N; ++i) - assert(sharedData[i] == sharedData[0]); - } -} - -int main() -{ - globalDouble = 5.0; - globalU64 = 4; - - uint64_t prevU64 = emscripten_atomic_add_u64((void*)&globalU64, 1); assert(prevU64 == 4); - - for(int i = 0; i < 7; ++i) - RunTest(i); - - uint64_t totalRead = 0; - uint64_t totalWritten = 0; - for(int i = 0; i < NUM_THREADS; ++i) - { - totalRead += threadCasAccumulatedReadData[i]; - totalWritten += threadCasAccumulatedWrittenData[i]; - } - for(int i = 0; i < N; ++i) - totalRead += sharedData[i]; - if (totalRead == totalWritten) - emscripten_outf("totalRead: %llu, totalWritten: %llu\n", totalRead, totalWritten); - else - emscripten_outf("64-bit CAS test failed! totalRead != totalWritten (%llu != %llu)\n", totalRead, totalWritten); - assert(totalRead == totalWritten); - emscripten_outf("Main: Test successfully finished"); - return 0; -} diff --git a/test/pthread/test_pthread_malloc.cpp b/test/pthread/test_pthread_malloc.c similarity index 82% rename from test/pthread/test_pthread_malloc.cpp rename to test/pthread/test_pthread_malloc.c index a355793fe778..58a845bad0e8 100644 --- a/test/pthread/test_pthread_malloc.cpp +++ b/test/pthread/test_pthread_malloc.c @@ -12,20 +12,16 @@ #define NUM_THREADS 8 #define N 6 -static void *thread_start(void *arg) -{ +static void *thread_start(void *arg) { long n = (long)arg; long *mem[N] = {}; - for(long i = 0; i < N; ++i) - { + for (long i = 0; i < N; ++i) { mem[i] = (long*)malloc(4); *mem[i] = n+i; } - for(long i = 0; i < N; ++i) - { + for (long i = 0; i < N; ++i) { long k = *mem[i]; - if (k != n+i) - { + if (k != n+i) { emscripten_errf("Memory corrupted! mem[i]: %ld, i: %ld, n: %ld", k, i, n); pthread_exit((void*)1); } @@ -37,13 +33,13 @@ static void *thread_start(void *arg) pthread_exit(0); } -int main() -{ +int main() { pthread_t thr[NUM_THREADS]; - for(intptr_t i = 0; i < NUM_THREADS; ++i) + for (intptr_t i = 0; i < NUM_THREADS; ++i) { pthread_create(&thr[i], NULL, thread_start, (void*)(i*N)); + } int result = 0; - for(int i = 0; i < NUM_THREADS; ++i) { + for (int i = 0; i < NUM_THREADS; ++i) { int res = 0; pthread_join(thr[i], (void**)&res); result += res; diff --git a/test/pthread/test_pthread_malloc_free.cpp b/test/pthread/test_pthread_malloc_free.cpp index b5f004464efb..2ca84eee0943 100644 --- a/test/pthread/test_pthread_malloc_free.cpp +++ b/test/pthread/test_pthread_malloc_free.cpp @@ -17,10 +17,8 @@ pthread_mutex_t vectorMutex = PTHREAD_MUTEX_INITIALIZER; std::vector allocatedMemory; -static void *thread_start(void *arg) -{ - for(int i = 0; i < NUM_BLOCKS_TO_ALLOC; ++i) - { +static void *thread_start(void *arg) { + for (int i = 0; i < NUM_BLOCKS_TO_ALLOC; ++i) { void *mem = malloc(4); pthread_mutex_lock(&vectorMutex); allocatedMemory.push_back(mem); @@ -29,26 +27,23 @@ static void *thread_start(void *arg) pthread_exit(0); } -int main() -{ +int main() { pthread_t thr[NUM_THREADS]; - for(int i = 0; i < NUM_THREADS; ++i) - { + for (int i = 0; i < NUM_THREADS; ++i) { int rc = pthread_create(&thr[i], NULL, thread_start, 0); assert(rc == 0); } unsigned long numBlocksToFree = NUM_BLOCKS_TO_ALLOC * NUM_THREADS; - while(numBlocksToFree > 0) - { + while (numBlocksToFree > 0) { pthread_mutex_lock(&vectorMutex); - for(size_t i = 0; i < allocatedMemory.size(); ++i) + for (size_t i = 0; i < allocatedMemory.size(); ++i) { free(allocatedMemory[i]); + } numBlocksToFree -= allocatedMemory.size(); allocatedMemory.clear(); pthread_mutex_unlock(&vectorMutex); } - for(int i = 0; i < NUM_THREADS; ++i) - { + for (int i = 0; i < NUM_THREADS; ++i) { int res = 0; int rc = pthread_join(thr[i], (void**)&res); assert(rc == 0); diff --git a/test/pthread/test_pthread_once.cpp b/test/pthread/test_pthread_once.c similarity index 50% rename from test/pthread/test_pthread_once.cpp rename to test/pthread/test_pthread_once.c index 98b2bdce65a5..2a8b9b41b383 100644 --- a/test/pthread/test_pthread_once.cpp +++ b/test/pthread/test_pthread_once.c @@ -11,32 +11,32 @@ _Atomic int numInitialized = 0; -void once_init() -{ - numInitialized++; +void once_init() { + numInitialized++; } #define NUM_THREADS 8 void *thread_main(void *arg) { - static pthread_once_t control = PTHREAD_ONCE_INIT; - pthread_once(&control, &once_init); - assert(numInitialized == 1); - pthread_exit(0); + static pthread_once_t control = PTHREAD_ONCE_INIT; + pthread_once(&control, &once_init); + assert(numInitialized == 1); + pthread_exit(0); } pthread_t thread[NUM_THREADS]; -int main() -{ - assert(numInitialized == 0); - for(int i = 0; i < NUM_THREADS; ++i) pthread_create(&thread[i], NULL, thread_main, 0); +int main() { + assert(numInitialized == 0); + for (int i = 0; i < NUM_THREADS; ++i) { + pthread_create(&thread[i], NULL, thread_main, 0); + } - if (emscripten_has_threading_support()) { - for(int i = 0; i < NUM_THREADS; ++i) pthread_join(thread[i], NULL); - assert(numInitialized == 1); - } + if (emscripten_has_threading_support()) { + for(int i = 0; i < NUM_THREADS; ++i) pthread_join(thread[i], NULL); + assert(numInitialized == 1); + } - return 0; + return 0; } diff --git a/test/pthread/test_pthread_run_on_main_thread.c b/test/pthread/test_pthread_run_on_main_thread.c new file mode 100644 index 000000000000..3657b10c9057 --- /dev/null +++ b/test/pthread/test_pthread_run_on_main_thread.c @@ -0,0 +1,145 @@ +// Copyright 2016 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 +#include +#include +#include + +int v_called = 0; +void v() { + assert(emscripten_is_main_runtime_thread()); + printf("Hello!\n"); + v_called = 1; +} + +int vi_called = 0; +void vi(int param0) { + assert(emscripten_is_main_runtime_thread()); + printf("Hello %d!\n", param0); + vi_called = 1; +} + +int vii_called = 0; +void vii(int param0, int param1) { + assert(emscripten_is_main_runtime_thread()); + printf("Hello %d %d!\n", param0, param1); + vii_called = 1; +} + +int viii_called = 0; +void viii(int param0, int param1, int param2) { + assert(emscripten_is_main_runtime_thread()); + printf("Hello %d %d %d!\n", param0, param1, param2); + viii_called = 1; +} + +int i_called = 0; +int i() { + assert(emscripten_is_main_runtime_thread()); + printf("Hello i!\n"); + i_called = 1; + return 84; +} + +int ii_called = 0; +int ii(int param0) { + assert(emscripten_is_main_runtime_thread()); + printf("Hello ii %d!\n", param0); + ii_called = 1; + return 85; +} + +int iii_called = 0; +int iii(int param0, int param1) { + assert(emscripten_is_main_runtime_thread()); + printf("Hello iii %d %d!\n", param0, param1); + iii_called = 1; + return 86; +} + +int iiii_called = 0; +int iiii(int param0, int param1, int param2) { + assert(emscripten_is_main_runtime_thread()); + printf("Hello iiii %d %d %d!\n", param0, param1, param2); + iiii_called = 1; + return 87; +} + +void test_sync() { + printf("Testing sync proxied runs:\n"); + int ret; + v_called = 0; emscripten_sync_run_in_main_runtime_thread(EM_FUNC_SIG_V, v); assert(v_called == 1); + vi_called = 0; emscripten_sync_run_in_main_runtime_thread(EM_FUNC_SIG_VI, vi, 42); assert(vi_called == 1); + vii_called = 0; emscripten_sync_run_in_main_runtime_thread(EM_FUNC_SIG_VII, vii, 42, 43); assert(vii_called == 1); + viii_called = 0; emscripten_sync_run_in_main_runtime_thread(EM_FUNC_SIG_VIII, viii, 42, 43, 44); assert(viii_called == 1); + i_called = 0; ret = emscripten_sync_run_in_main_runtime_thread(EM_FUNC_SIG_I, i); assert(i_called == 1); assert(ret == 84); + ii_called = 0; ret = emscripten_sync_run_in_main_runtime_thread(EM_FUNC_SIG_II, ii, 42); assert(ii_called == 1); assert(ret == 85); + iii_called = 0; ret = emscripten_sync_run_in_main_runtime_thread(EM_FUNC_SIG_III, iii, 42, 43); assert(iii_called == 1); assert(ret == 86); + iiii_called = 0; ret = emscripten_sync_run_in_main_runtime_thread(EM_FUNC_SIG_IIII, iiii, 42, 43, 44); assert(iiii_called == 1); assert(ret == 87); +} + +void test_async() { + printf("Testing async proxied runs:\n"); + emscripten_async_run_in_main_runtime_thread(EM_FUNC_SIG_V, v); + emscripten_async_run_in_main_runtime_thread(EM_FUNC_SIG_VI, vi, 42); + emscripten_async_run_in_main_runtime_thread(EM_FUNC_SIG_VII, vii, 42, 43); + emscripten_async_run_in_main_runtime_thread(EM_FUNC_SIG_VIII, viii, 42, 43, 44); + emscripten_async_run_in_main_runtime_thread(EM_FUNC_SIG_I, i); + emscripten_async_run_in_main_runtime_thread(EM_FUNC_SIG_II, ii, 42); + emscripten_async_run_in_main_runtime_thread(EM_FUNC_SIG_III, iii, 42, 43); + emscripten_async_run_in_main_runtime_thread(EM_FUNC_SIG_IIII, iiii, 42, 43, 44); +} + +void test_async_waitable() { + printf("Testing waitable async proxied runs:\n"); + em_queued_call *c1 = emscripten_async_waitable_run_in_main_runtime_thread(EM_FUNC_SIG_V, v); + em_queued_call *c2 = emscripten_async_waitable_run_in_main_runtime_thread(EM_FUNC_SIG_VI, vi, 42); + em_queued_call *c3 = emscripten_async_waitable_run_in_main_runtime_thread(EM_FUNC_SIG_VII, vii, 42, 43); + em_queued_call *c4 = emscripten_async_waitable_run_in_main_runtime_thread(EM_FUNC_SIG_VIII, viii, 42, 43, 44); + em_queued_call *c5 = emscripten_async_waitable_run_in_main_runtime_thread(EM_FUNC_SIG_I, i); + em_queued_call *c6 = emscripten_async_waitable_run_in_main_runtime_thread(EM_FUNC_SIG_II, ii, 42); + em_queued_call *c7 = emscripten_async_waitable_run_in_main_runtime_thread(EM_FUNC_SIG_III, iii, 42, 43); + em_queued_call *c8 = emscripten_async_waitable_run_in_main_runtime_thread(EM_FUNC_SIG_IIII, iiii, 42, 43, 44); + EMSCRIPTEN_RESULT r = emscripten_wait_for_call_v(c1, INFINITY); assert(r == EMSCRIPTEN_RESULT_SUCCESS); + r = emscripten_wait_for_call_v(c2, INFINITY); assert(r == EMSCRIPTEN_RESULT_SUCCESS); + r = emscripten_wait_for_call_v(c3, INFINITY); assert(r == EMSCRIPTEN_RESULT_SUCCESS); + r = emscripten_wait_for_call_v(c4, INFINITY); assert(r == EMSCRIPTEN_RESULT_SUCCESS); + int r1; r = emscripten_wait_for_call_i(c5, INFINITY, &r1); assert(r1 == 84); assert(r == EMSCRIPTEN_RESULT_SUCCESS); + int r2; r = emscripten_wait_for_call_i(c6, INFINITY, &r2); assert(r2 == 85); assert(r == EMSCRIPTEN_RESULT_SUCCESS); + int r3; r = emscripten_wait_for_call_i(c7, INFINITY, &r3); assert(r3 == 86); assert(r == EMSCRIPTEN_RESULT_SUCCESS); + int r4; r = emscripten_wait_for_call_i(c8, INFINITY, &r4); assert(r4 == 87); assert(r == EMSCRIPTEN_RESULT_SUCCESS); + emscripten_async_waitable_close(c1); + emscripten_async_waitable_close(c2); + emscripten_async_waitable_close(c3); + emscripten_async_waitable_close(c4); + emscripten_async_waitable_close(c5); + emscripten_async_waitable_close(c6); + emscripten_async_waitable_close(c7); + emscripten_async_waitable_close(c8); +} + +void *thread_main(void* arg) { + test_sync(); + test_async(); + test_async_waitable(); + pthread_exit(0); +} + +int main() { + if (emscripten_has_threading_support()) { + test_sync(); + test_async_waitable(); + + pthread_t thread; + int rc = pthread_create(&thread, NULL, thread_main, NULL); + assert(rc == 0); + rc = pthread_join(thread, 0); + assert(rc == 0); + } + + test_async(); + return 0; +} diff --git a/test/pthread/test_pthread_run_on_main_thread.cpp b/test/pthread/test_pthread_run_on_main_thread.cpp deleted file mode 100644 index 1e12ddd1912a..000000000000 --- a/test/pthread/test_pthread_run_on_main_thread.cpp +++ /dev/null @@ -1,159 +0,0 @@ -// Copyright 2016 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 -#include -#include -#include - -int v_called = 0; -void v() -{ - assert(emscripten_is_main_runtime_thread()); - printf("Hello!\n"); - v_called = 1; -} - -int vi_called = 0; -void vi(int param0) -{ - assert(emscripten_is_main_runtime_thread()); - printf("Hello %d!\n", param0); - vi_called = 1; -} - -int vii_called = 0; -void vii(int param0, int param1) -{ - assert(emscripten_is_main_runtime_thread()); - printf("Hello %d %d!\n", param0, param1); - vii_called = 1; -} - -int viii_called = 0; -void viii(int param0, int param1, int param2) -{ - assert(emscripten_is_main_runtime_thread()); - printf("Hello %d %d %d!\n", param0, param1, param2); - viii_called = 1; -} - -int i_called = 0; -int i() -{ - assert(emscripten_is_main_runtime_thread()); - printf("Hello i!\n"); - i_called = 1; - return 84; -} - -int ii_called = 0; -int ii(int param0) -{ - assert(emscripten_is_main_runtime_thread()); - printf("Hello ii %d!\n", param0); - ii_called = 1; - return 85; -} - -int iii_called = 0; -int iii(int param0, int param1) -{ - assert(emscripten_is_main_runtime_thread()); - printf("Hello iii %d %d!\n", param0, param1); - iii_called = 1; - return 86; -} - -int iiii_called = 0; -int iiii(int param0, int param1, int param2) -{ - assert(emscripten_is_main_runtime_thread()); - printf("Hello iiii %d %d %d!\n", param0, param1, param2); - iiii_called = 1; - return 87; -} - -void test_sync() -{ - printf("Testing sync proxied runs:\n"); - int ret; - v_called = 0; emscripten_sync_run_in_main_runtime_thread(EM_FUNC_SIG_V, v); assert(v_called == 1); - vi_called = 0; emscripten_sync_run_in_main_runtime_thread(EM_FUNC_SIG_VI, vi, 42); assert(vi_called == 1); - vii_called = 0; emscripten_sync_run_in_main_runtime_thread(EM_FUNC_SIG_VII, vii, 42, 43); assert(vii_called == 1); - viii_called = 0; emscripten_sync_run_in_main_runtime_thread(EM_FUNC_SIG_VIII, viii, 42, 43, 44); assert(viii_called == 1); - i_called = 0; ret = emscripten_sync_run_in_main_runtime_thread(EM_FUNC_SIG_I, i); assert(i_called == 1); assert(ret == 84); - ii_called = 0; ret = emscripten_sync_run_in_main_runtime_thread(EM_FUNC_SIG_II, ii, 42); assert(ii_called == 1); assert(ret == 85); - iii_called = 0; ret = emscripten_sync_run_in_main_runtime_thread(EM_FUNC_SIG_III, iii, 42, 43); assert(iii_called == 1); assert(ret == 86); - iiii_called = 0; ret = emscripten_sync_run_in_main_runtime_thread(EM_FUNC_SIG_IIII, iiii, 42, 43, 44); assert(iiii_called == 1); assert(ret == 87); -} - -void test_async() -{ - printf("Testing async proxied runs:\n"); - emscripten_async_run_in_main_runtime_thread(EM_FUNC_SIG_V, v); - emscripten_async_run_in_main_runtime_thread(EM_FUNC_SIG_VI, vi, 42); - emscripten_async_run_in_main_runtime_thread(EM_FUNC_SIG_VII, vii, 42, 43); - emscripten_async_run_in_main_runtime_thread(EM_FUNC_SIG_VIII, viii, 42, 43, 44); - emscripten_async_run_in_main_runtime_thread(EM_FUNC_SIG_I, i); - emscripten_async_run_in_main_runtime_thread(EM_FUNC_SIG_II, ii, 42); - emscripten_async_run_in_main_runtime_thread(EM_FUNC_SIG_III, iii, 42, 43); - emscripten_async_run_in_main_runtime_thread(EM_FUNC_SIG_IIII, iiii, 42, 43, 44); -} - -void test_async_waitable() -{ - printf("Testing waitable async proxied runs:\n"); - em_queued_call *c1 = emscripten_async_waitable_run_in_main_runtime_thread(EM_FUNC_SIG_V, v); - em_queued_call *c2 = emscripten_async_waitable_run_in_main_runtime_thread(EM_FUNC_SIG_VI, vi, 42); - em_queued_call *c3 = emscripten_async_waitable_run_in_main_runtime_thread(EM_FUNC_SIG_VII, vii, 42, 43); - em_queued_call *c4 = emscripten_async_waitable_run_in_main_runtime_thread(EM_FUNC_SIG_VIII, viii, 42, 43, 44); - em_queued_call *c5 = emscripten_async_waitable_run_in_main_runtime_thread(EM_FUNC_SIG_I, i); - em_queued_call *c6 = emscripten_async_waitable_run_in_main_runtime_thread(EM_FUNC_SIG_II, ii, 42); - em_queued_call *c7 = emscripten_async_waitable_run_in_main_runtime_thread(EM_FUNC_SIG_III, iii, 42, 43); - em_queued_call *c8 = emscripten_async_waitable_run_in_main_runtime_thread(EM_FUNC_SIG_IIII, iiii, 42, 43, 44); - EMSCRIPTEN_RESULT r = emscripten_wait_for_call_v(c1, INFINITY); assert(r == EMSCRIPTEN_RESULT_SUCCESS); - r = emscripten_wait_for_call_v(c2, INFINITY); assert(r == EMSCRIPTEN_RESULT_SUCCESS); - r = emscripten_wait_for_call_v(c3, INFINITY); assert(r == EMSCRIPTEN_RESULT_SUCCESS); - r = emscripten_wait_for_call_v(c4, INFINITY); assert(r == EMSCRIPTEN_RESULT_SUCCESS); - int r1; r = emscripten_wait_for_call_i(c5, INFINITY, &r1); assert(r1 == 84); assert(r == EMSCRIPTEN_RESULT_SUCCESS); - int r2; r = emscripten_wait_for_call_i(c6, INFINITY, &r2); assert(r2 == 85); assert(r == EMSCRIPTEN_RESULT_SUCCESS); - int r3; r = emscripten_wait_for_call_i(c7, INFINITY, &r3); assert(r3 == 86); assert(r == EMSCRIPTEN_RESULT_SUCCESS); - int r4; r = emscripten_wait_for_call_i(c8, INFINITY, &r4); assert(r4 == 87); assert(r == EMSCRIPTEN_RESULT_SUCCESS); - emscripten_async_waitable_close(c1); - emscripten_async_waitable_close(c2); - emscripten_async_waitable_close(c3); - emscripten_async_waitable_close(c4); - emscripten_async_waitable_close(c5); - emscripten_async_waitable_close(c6); - emscripten_async_waitable_close(c7); - emscripten_async_waitable_close(c8); -} - -void *thread_main(void*) -{ - test_sync(); - test_async(); - test_async_waitable(); - pthread_exit(0); -} - -int main() -{ - if (emscripten_has_threading_support()) - { - test_sync(); - test_async_waitable(); - - pthread_t thread; - int rc = pthread_create(&thread, NULL, thread_main, NULL); - assert(rc == 0); - rc = pthread_join(thread, 0); - assert(rc == 0); - } - - test_async(); - return 0; -} diff --git a/test/pthread/test_pthread_run_on_main_thread_flood.c b/test/pthread/test_pthread_run_on_main_thread_flood.c new file mode 100644 index 000000000000..e06baae61c83 --- /dev/null +++ b/test/pthread/test_pthread_run_on_main_thread_flood.c @@ -0,0 +1,89 @@ +// Copyright 2016 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 +#include +#include +#include + +_Atomic int func_called = 0; + +void v() { + func_called++; +} + +void test_sync() { + printf("Testing sync proxied runs:\n"); + func_called = 0; + for (int i = 0; i < 1000; ++i) { + emscripten_sync_run_in_main_runtime_thread(EM_FUNC_SIG_V, v); + assert(func_called == i+1); + } +} + +void test_async() { + printf("Testing async proxied runs:\n"); + func_called = 0; + for (int i = 0; i < 1000; ++i) { + emscripten_async_run_in_main_runtime_thread(EM_FUNC_SIG_V, v); + } + + while (func_called != 1000) {} +} + +void test_async_waitable() { + printf("Testing waitable async proxied runs:\n"); + func_called = 0; + em_queued_call* handles[1000]; + + for (int i = 0; i < 1000; ++i) { + handles[i] = emscripten_async_waitable_run_in_main_runtime_thread(EM_FUNC_SIG_V, v); + } + + EMSCRIPTEN_RESULT r = emscripten_wait_for_call_v(handles[999], INFINITY); + assert(r == EMSCRIPTEN_RESULT_SUCCESS); + + // Since ordering is guaranteed we know that all the other tasks must + // also have already been completed. + int final_count = func_called; + printf("final_count: %d\n", final_count); + assert(final_count == 1000); + + // Cleanup/free all the handles. Waiting on the 1000th a second time is + // allowed by the API. + for (int i = 0; i < 1000; ++i) { + EMSCRIPTEN_RESULT r = emscripten_wait_for_call_v(handles[i], INFINITY); + assert(r == EMSCRIPTEN_RESULT_SUCCESS); + emscripten_async_waitable_close(handles[i]); + } +} + +void *thread_main(void* arg) { + test_sync(); + test_async(); + test_async_waitable(); + printf("thread_main done\n"); + pthread_exit(NULL); +} + +int main() { + if (emscripten_has_threading_support()) { + test_sync(); + test_async_waitable(); + + pthread_t thread; + int rc = pthread_create(&thread, NULL, thread_main, NULL); + assert(rc == 0); + void* retval; + rc = pthread_join(thread, &retval); + assert(rc == 0); + printf("pthread_join done: %ld\n", (intptr_t)retval); + assert(retval == NULL); + } + + test_async(); + + return 0; +} diff --git a/test/pthread/test_pthread_run_on_main_thread_flood.cpp b/test/pthread/test_pthread_run_on_main_thread_flood.cpp deleted file mode 100644 index d41dc59a4738..000000000000 --- a/test/pthread/test_pthread_run_on_main_thread_flood.cpp +++ /dev/null @@ -1,100 +0,0 @@ -// Copyright 2016 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 -#include -#include -#include - -_Atomic int func_called = 0; - -void v() -{ - func_called++; -} - -void test_sync() -{ - printf("Testing sync proxied runs:\n"); - func_called = 0; - for(int i = 0; i < 1000; ++i) - { - emscripten_sync_run_in_main_runtime_thread(EM_FUNC_SIG_V, v); - assert(func_called == i+1); - } -} - -void test_async() -{ - printf("Testing async proxied runs:\n"); - func_called = 0; - for(int i = 0; i < 1000; ++i) - { - emscripten_async_run_in_main_runtime_thread(EM_FUNC_SIG_V, v); - } - - while(func_called != 1000) {} -} - -void test_async_waitable() -{ - printf("Testing waitable async proxied runs:\n"); - func_called = 0; - em_queued_call* handles[1000]; - - for(int i = 0; i < 1000; ++i) - { - handles[i] = emscripten_async_waitable_run_in_main_runtime_thread(EM_FUNC_SIG_V, v); - } - - EMSCRIPTEN_RESULT r = emscripten_wait_for_call_v(handles[999], INFINITY); - assert(r == EMSCRIPTEN_RESULT_SUCCESS); - - // Since ordering is guaranteed we know that all the other tasks must - // also have already been completed. - int final_count = func_called; - printf("final_count: %d\n", final_count); - assert(final_count == 1000); - - // Cleanup/free all the handles. Waiting on the 1000th a second time is - // allowed by the API. - for(int i = 0; i < 1000; ++i) - { - EMSCRIPTEN_RESULT r = emscripten_wait_for_call_v(handles[i], INFINITY); - assert(r == EMSCRIPTEN_RESULT_SUCCESS); - emscripten_async_waitable_close(handles[i]); - } -} - -void *thread_main(void*) -{ - test_sync(); - test_async(); - test_async_waitable(); - printf("thread_main done\n"); - pthread_exit(NULL); -} - -int main() -{ - if (emscripten_has_threading_support()) - { - test_sync(); - test_async_waitable(); - - pthread_t thread; - int rc = pthread_create(&thread, NULL, thread_main, NULL); - assert(rc == 0); - void* retval; - rc = pthread_join(thread, &retval); - assert(rc == 0); - printf("pthread_join done: %ld\n", (intptr_t)retval); - assert(retval == NULL); - } - - test_async(); - - return 0; -} diff --git a/test/pthread/test_pthread_volatile.cpp b/test/pthread/test_pthread_volatile.c similarity index 87% rename from test/pthread/test_pthread_volatile.cpp rename to test/pthread/test_pthread_volatile.c index 61f013bfd804..70e1a6b96fa4 100644 --- a/test/pthread/test_pthread_volatile.cpp +++ b/test/pthread/test_pthread_volatile.c @@ -20,18 +20,15 @@ _Atomic #endif int sharedVar = 0; -static void *thread_start(void *arg) // thread: just flip the shared flag and quit. -{ +static void *thread_start(void *arg) { // thread: just flip the shared flag and quit. sharedVar = 1; pthread_exit(0); } -int main() -{ +int main() { pthread_t thr; int rc = pthread_create(&thr, NULL, thread_start, (void*)0); - if (rc != 0) - { + if (rc != 0) { return 1; } diff --git a/test/test_browser.py b/test/test_browser.py index cf766bda1985..f05433615523 100644 --- a/test/test_browser.py +++ b/test/test_browser.py @@ -3912,7 +3912,7 @@ def test_pthread_atomics(self, args): # Test 64-bit atomics. @requires_threads def test_pthread_64bit_atomics(self): - self.btest_exit('pthread/test_pthread_64bit_atomics.cpp', args=['-sINITIAL_MEMORY=64MB', '-O3', '-pthread', '-sPTHREAD_POOL_SIZE=8']) + self.btest_exit('pthread/test_pthread_64bit_atomics.c', args=['-sINITIAL_MEMORY=64MB', '-O3', '-pthread', '-sPTHREAD_POOL_SIZE=8']) # Test 64-bit C++11 atomics. @parameterized({ @@ -4091,7 +4091,7 @@ def test_pthread_attr_getstack(self): # Test that memory allocation is thread-safe. @requires_threads def test_pthread_malloc(self): - self.btest_exit('pthread/test_pthread_malloc.cpp', args=['-sINITIAL_MEMORY=64MB', '-O3', '-pthread', '-sPTHREAD_POOL_SIZE=8']) + self.btest_exit('pthread/test_pthread_malloc.c', args=['-sINITIAL_MEMORY=64MB', '-O3', '-pthread', '-sPTHREAD_POOL_SIZE=8']) # Stress test pthreads allocating memory that will call to sbrk(), and main thread has to free up the data. @requires_threads @@ -4106,7 +4106,7 @@ def test_pthread_barrier(self): # Test the pthread_once() function. @requires_threads def test_pthread_once(self): - self.btest_exit('pthread/test_pthread_once.cpp', args=['-sINITIAL_MEMORY=64MB', '-O3', '-pthread', '-sPTHREAD_POOL_SIZE=8']) + self.btest_exit('pthread/test_pthread_once.c', args=['-sINITIAL_MEMORY=64MB', '-O3', '-pthread', '-sPTHREAD_POOL_SIZE=8']) # Test against a certain thread exit time handling bug by spawning tons of threads. @requires_threads @@ -4116,9 +4116,12 @@ def test_pthread_spawns(self): # It is common for code to flip volatile global vars for thread control. This is a bit lax, but nevertheless, test whether that # kind of scheme will work with Emscripten as well. @requires_threads - def test_pthread_volatile(self): - for arg in [[], ['-DUSE_C_VOLATILE']]: - self.btest_exit('pthread/test_pthread_volatile.cpp', args=['-sINITIAL_MEMORY=64MB', '-O3', '-pthread', '-sPTHREAD_POOL_SIZE=8'] + arg) + @parameterized({ + '': (['-DUSE_C_VOLATILE'],), + 'atomic': ([],), + }) + def test_pthread_volatile(self, args): + self.btest_exit('pthread/test_pthread_volatile.c', args=['-sINITIAL_MEMORY=64MB', '-O3', '-pthread', '-sPTHREAD_POOL_SIZE=8'] + args) # Test thread-specific data (TLS). @requires_threads @@ -4249,12 +4252,12 @@ def test_pthread_gauge_available_memory(self, args): @disabled('https://github.com/emscripten-core/emscripten/issues/18210') @requires_threads def test_pthread_run_on_main_thread(self): - self.btest_exit('pthread/test_pthread_run_on_main_thread.cpp', args=['-O3', '-pthread', '-sPTHREAD_POOL_SIZE']) + self.btest_exit('pthread/test_pthread_run_on_main_thread.c', args=['-O3', '-pthread', '-sPTHREAD_POOL_SIZE']) # Test how a lot of back-to-back called proxying operations behave. @requires_threads def test_pthread_run_on_main_thread_flood(self): - self.btest_exit('pthread/test_pthread_run_on_main_thread_flood.cpp', args=['-O3', '-pthread', '-sPTHREAD_POOL_SIZE']) + self.btest_exit('pthread/test_pthread_run_on_main_thread_flood.c', args=['-O3', '-pthread', '-sPTHREAD_POOL_SIZE']) # Test that it is possible to asynchronously call a JavaScript function on the # main thread.