Add mutex stress test (#2472)
As a part of stress-testing we want to ensure that mutex implementation is working correctly and protecting shared resource to be allocated from other threads when mutex is locked. This test covers the most common situations that happen when some program uses mutexes like locks from various threads, locks from the same thread etc.
This commit is contained in:
65
core/iwasm/libraries/lib-wasi-threads/stress-test/build.sh
Executable file
65
core/iwasm/libraries/lib-wasi-threads/stress-test/build.sh
Executable file
@ -0,0 +1,65 @@
|
||||
#!/bin/bash
|
||||
|
||||
#
|
||||
# Copyright (C) 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
#
|
||||
|
||||
set -eo pipefail
|
||||
CC=${CC:=/opt/wasi-sdk/bin/clang}
|
||||
WAMR_DIR=../../../../..
|
||||
|
||||
show_usage() {
|
||||
echo "Usage: $0 [--sysroot PATH_TO_SYSROOT]"
|
||||
echo "--sysroot PATH_TO_SYSROOT specify to build with custom sysroot for wasi-libc"
|
||||
}
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
key="$1"
|
||||
case $key in
|
||||
--sysroot)
|
||||
sysroot_path="$2"
|
||||
shift
|
||||
shift
|
||||
;;
|
||||
--help)
|
||||
show_usage
|
||||
exit
|
||||
;;
|
||||
*)
|
||||
echo "Unknown option: $1"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
rm -rf *.wasm
|
||||
rm -rf *.aot
|
||||
|
||||
for test_c in *.c; do
|
||||
test_wasm="$(basename $test_c .c).wasm"
|
||||
|
||||
if [[ -n "$sysroot_path" ]]; then
|
||||
if [ ! -d "$sysroot_path" ]; then
|
||||
echo "Directory $sysroot_path doesn't exist. Aborting"
|
||||
exit 1
|
||||
fi
|
||||
sysroot_command="--sysroot $sysroot_path"
|
||||
fi
|
||||
|
||||
echo "Compiling $test_c to $test_wasm"
|
||||
$CC \
|
||||
-target wasm32-wasi-threads \
|
||||
-O2 \
|
||||
-Wall \
|
||||
-pthread \
|
||||
-z stack-size=32768 \
|
||||
-Wl,--export=__heap_base \
|
||||
-Wl,--export=__data_end \
|
||||
-Wl,--shared-memory,--max-memory=1966080 \
|
||||
-Wl,--export=wasi_thread_start \
|
||||
-Wl,--export=malloc \
|
||||
-Wl,--export=free \
|
||||
$sysroot_command \
|
||||
$test_c -o $test_wasm
|
||||
done
|
||||
@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Amazon.com Inc. or its affiliates. All rights reserved.
|
||||
* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
*/
|
||||
|
||||
#include <pthread.h>
|
||||
#include <errno.h>
|
||||
#include "mutex_common.h"
|
||||
|
||||
int
|
||||
main()
|
||||
{
|
||||
pthread_mutex_t mutex;
|
||||
|
||||
// Set mutex type to errorcheck. This type provides some additional checks
|
||||
// (for example returns EDEADLK instead of deadlocking in some cases)
|
||||
pthread_mutexattr_t mutex_attr;
|
||||
pthread_mutexattr_init(&mutex_attr);
|
||||
pthread_mutexattr_settype(&mutex_attr, PTHREAD_MUTEX_ERRORCHECK);
|
||||
|
||||
pthread_mutex_init(&mutex, &mutex_attr);
|
||||
pthread_mutexattr_destroy(&mutex_attr);
|
||||
|
||||
run_common_tests(&mutex);
|
||||
fprintf(stderr, "Errorcheck mutex test is completed\n");
|
||||
pthread_mutex_destroy(&mutex);
|
||||
}
|
||||
@ -0,0 +1,3 @@
|
||||
{
|
||||
"name": "lib-wasi-threads stress tests"
|
||||
}
|
||||
229
core/iwasm/libraries/lib-wasi-threads/stress-test/mutex_common.h
Normal file
229
core/iwasm/libraries/lib-wasi-threads/stress-test/mutex_common.h
Normal file
@ -0,0 +1,229 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Amazon.com Inc. or its affiliates. All rights reserved.
|
||||
* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
*/
|
||||
|
||||
#ifndef MUTEX_COMMON_H
|
||||
#define MUTEX_COMMON_H
|
||||
|
||||
#include <pthread.h>
|
||||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <stdbool.h>
|
||||
#include <time.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
enum Constants {
|
||||
NUM_ITER = 250000,
|
||||
NUM_THREADS = 12,
|
||||
NUM_RETRY = 8,
|
||||
RETRY_SLEEP_TIME_US = 1000,
|
||||
};
|
||||
|
||||
// We're counting how many times each thread was called using this array
|
||||
// Main thread is also counted here so we need to make arrays bigger
|
||||
typedef struct {
|
||||
int tids[NUM_THREADS + 1];
|
||||
int calls[NUM_THREADS + 1];
|
||||
} StatCollector;
|
||||
|
||||
typedef struct {
|
||||
pthread_mutex_t *mutex;
|
||||
StatCollector stat;
|
||||
int counter;
|
||||
bool is_sleeping;
|
||||
} MutexCounter;
|
||||
|
||||
// This enum defines whether thread should sleep to increase contention
|
||||
enum SleepState {
|
||||
NON_SLEEP = 0,
|
||||
SLEEP = 1,
|
||||
};
|
||||
|
||||
void
|
||||
mutex_counter_init(MutexCounter *mutex_counter, pthread_mutex_t *mutex,
|
||||
enum SleepState is_sleeping)
|
||||
{
|
||||
memset(mutex_counter, 0, sizeof(*mutex_counter));
|
||||
mutex_counter->mutex = mutex;
|
||||
mutex_counter->is_sleeping = is_sleeping;
|
||||
}
|
||||
|
||||
// This function spawns the thread using exponential retries if it receives
|
||||
// EAGAIN
|
||||
static inline void
|
||||
spawn_thread(pthread_t *tid, void *func, void *arg)
|
||||
{
|
||||
int status_code = -1;
|
||||
int timeout_us = RETRY_SLEEP_TIME_US;
|
||||
for (int tries = 0; status_code != 0 && tries < NUM_RETRY; ++tries) {
|
||||
status_code = pthread_create(tid, NULL, (void *(*)(void *))func, arg);
|
||||
assert(status_code == 0 || status_code == EAGAIN);
|
||||
if (status_code == EAGAIN) {
|
||||
usleep(timeout_us);
|
||||
timeout_us *= 2;
|
||||
}
|
||||
}
|
||||
|
||||
assert(status_code == 0 && "Thread creation should succeed");
|
||||
}
|
||||
|
||||
// This function adds tid to our stat
|
||||
static inline void
|
||||
add_to_stat(StatCollector *stat, int tid)
|
||||
{
|
||||
int tid_num = 0;
|
||||
for (; tid_num < NUM_THREADS + 1 && stat->tids[tid_num] != 0; ++tid_num) {
|
||||
if (stat->tids[tid_num] == tid) {
|
||||
stat->calls[tid_num]++;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
assert(tid_num < NUM_THREADS + 1);
|
||||
stat->tids[tid_num] = tid;
|
||||
stat->calls[tid_num] = 1;
|
||||
}
|
||||
|
||||
// This function prints number of calls by TID
|
||||
static inline void
|
||||
print_stat(StatCollector *stat)
|
||||
{
|
||||
fprintf(stderr, "Thread calls count by TID\n");
|
||||
for (int i = 0; i < NUM_THREADS + 1; ++i) {
|
||||
if (stat->tids[i] != 0) {
|
||||
fprintf(stderr, "TID: %d; Calls: %d\n", stat->tids[i],
|
||||
stat->calls[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This function is run by the threads, it increases counter in a loop and then
|
||||
// sleeps after unlocking the mutex to provide better contention
|
||||
static inline void *
|
||||
inc_shared_variable(void *arg)
|
||||
{
|
||||
MutexCounter *mutex_counter = (MutexCounter *)(arg);
|
||||
int sleep_us = 0;
|
||||
while (!pthread_mutex_lock(mutex_counter->mutex)
|
||||
&& mutex_counter->counter < NUM_ITER) {
|
||||
mutex_counter->counter++;
|
||||
add_to_stat(&mutex_counter->stat, (int)(pthread_self()));
|
||||
if (mutex_counter->is_sleeping) {
|
||||
sleep_us = rand() % 1000;
|
||||
}
|
||||
|
||||
assert(pthread_mutex_unlock(mutex_counter->mutex) == 0
|
||||
&& "Should be able to unlock a mutex");
|
||||
if (mutex_counter->is_sleeping) {
|
||||
usleep(sleep_us);
|
||||
}
|
||||
}
|
||||
|
||||
assert(mutex_counter->counter == NUM_ITER);
|
||||
assert(pthread_mutex_unlock(mutex_counter->mutex) == 0
|
||||
&& "Should be able to unlock the mutex after test execution");
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Locking and unlocking a mutex in a single thread.
|
||||
static inline void *
|
||||
same_thread_lock_unlock_test(void *mutex)
|
||||
{
|
||||
for (int i = 0; i < NUM_ITER; ++i) {
|
||||
assert(pthread_mutex_lock(mutex) == 0
|
||||
&& "Main thread should be able to lock a mutex");
|
||||
assert(pthread_mutex_unlock(mutex) == 0
|
||||
&& "Main thread should be able to unlock a mutex");
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// This function spawns a thread that locks and unlocks a mutex `NUM_ITER` times
|
||||
// in a row
|
||||
static inline void
|
||||
same_non_main_thread_lock_unlock_test(pthread_mutex_t *mutex)
|
||||
{
|
||||
pthread_t tid = 0;
|
||||
spawn_thread(&tid, same_thread_lock_unlock_test, mutex);
|
||||
|
||||
assert(tid != 0 && "TID can't be 0 after successful thread creation");
|
||||
assert(pthread_join(tid, NULL) == 0
|
||||
&& "Thread should be joined successfully");
|
||||
}
|
||||
|
||||
// This function checks basic contention between main and non-main thread
|
||||
// increasing the shared variable
|
||||
static inline void
|
||||
two_threads_inc_test(pthread_mutex_t *mutex)
|
||||
{
|
||||
MutexCounter mutex_counter;
|
||||
mutex_counter_init(&mutex_counter, mutex, false);
|
||||
|
||||
pthread_t tid = 0;
|
||||
spawn_thread(&tid, inc_shared_variable, &mutex_counter);
|
||||
|
||||
assert(tid != 0 && "TID can't be 0 after successful thread creation");
|
||||
inc_shared_variable(&mutex_counter);
|
||||
assert(pthread_join(tid, NULL) == 0
|
||||
&& "Thread should be joined without errors");
|
||||
assert(mutex_counter.counter == NUM_ITER);
|
||||
}
|
||||
|
||||
// This function creates number of threads specified by NUM_THREADS and run
|
||||
// concurrent increasing of shared variable
|
||||
static inline void
|
||||
max_threads_inc_test(pthread_mutex_t *mutex, int threads_num,
|
||||
enum SleepState is_sleeping)
|
||||
{
|
||||
MutexCounter mutex_counter;
|
||||
mutex_counter_init(&mutex_counter, mutex, is_sleeping);
|
||||
|
||||
pthread_t tids[threads_num];
|
||||
for (int i = 0; i < threads_num; ++i) {
|
||||
spawn_thread(&tids[i], inc_shared_variable, &mutex_counter);
|
||||
}
|
||||
|
||||
inc_shared_variable(&mutex_counter);
|
||||
|
||||
for (int i = 0; i < threads_num; ++i) {
|
||||
assert(pthread_join(tids[i], NULL) == 0
|
||||
&& "Thread should be joined without errors");
|
||||
}
|
||||
|
||||
print_stat(&mutex_counter.stat);
|
||||
}
|
||||
|
||||
// This function just runs all the tests described above
|
||||
static inline void
|
||||
run_common_tests(pthread_mutex_t *mutex)
|
||||
{
|
||||
srand(time(NULL));
|
||||
|
||||
fprintf(stderr, "Starting same_thread_lock_unlock_test test\n");
|
||||
same_thread_lock_unlock_test(mutex);
|
||||
fprintf(stderr, "Finished same_thread_lock_unlock_test test\n");
|
||||
|
||||
fprintf(stderr, "Starting same_non_main_thread_lock_unlock_test test\n");
|
||||
same_non_main_thread_lock_unlock_test(mutex);
|
||||
fprintf(stderr, "Finished same_non_main_thread_lock_unlock_test test\n");
|
||||
|
||||
fprintf(stderr, "Starting two_threads_inc_test test\n");
|
||||
two_threads_inc_test(mutex);
|
||||
fprintf(stderr, "Finished two_threads_inc_test test\n");
|
||||
|
||||
fprintf(stderr, "Starting max_threads_inc_test_sleep test\n");
|
||||
max_threads_inc_test(mutex, NUM_THREADS, SLEEP);
|
||||
fprintf(stderr, "Finished concurrent_inc sleep test\n");
|
||||
|
||||
fprintf(stderr, "Starting max_threads_inc_test_non_sleep test\n");
|
||||
max_threads_inc_test(mutex, NUM_THREADS, NON_SLEEP);
|
||||
fprintf(stderr, "Finished max_threads_inc_test test\n");
|
||||
}
|
||||
|
||||
#endif // MUTEX_COMMON_H
|
||||
@ -0,0 +1,20 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Amazon.com Inc. or its affiliates. All rights reserved.
|
||||
* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
*/
|
||||
|
||||
#include <pthread.h>
|
||||
#include <errno.h>
|
||||
#include "mutex_common.h"
|
||||
|
||||
int
|
||||
main()
|
||||
{
|
||||
pthread_mutex_t mutex;
|
||||
pthread_mutex_init(&mutex, NULL);
|
||||
|
||||
run_common_tests(&mutex);
|
||||
|
||||
fprintf(stderr, "Normal mutex test is completed\n");
|
||||
pthread_mutex_destroy(&mutex);
|
||||
}
|
||||
@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Amazon.com Inc. or its affiliates. All rights reserved.
|
||||
* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
*/
|
||||
|
||||
#include <pthread.h>
|
||||
#include <errno.h>
|
||||
#include "mutex_common.h"
|
||||
|
||||
void
|
||||
multiple_same_thread_lock(void *mutex)
|
||||
{
|
||||
for (int i = 0; i < 100; ++i) {
|
||||
assert(pthread_mutex_lock(mutex) == 0
|
||||
&& "Recursive mutex should allow multiple locking");
|
||||
}
|
||||
|
||||
for (int i = 0; i < 100; ++i) {
|
||||
assert(pthread_mutex_unlock(mutex) == 0
|
||||
&& "Recursive mutex should allow multiple unlocking");
|
||||
}
|
||||
}
|
||||
|
||||
void *
|
||||
same_thread_multiple_rec_mutex_lock(void *mutex)
|
||||
{
|
||||
for (int i = 0; i < NUM_ITER; ++i) {
|
||||
multiple_same_thread_lock(mutex);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int
|
||||
main()
|
||||
{
|
||||
pthread_mutex_t mutex;
|
||||
|
||||
// Set mutex type to recursive. This type allows multiple locking and
|
||||
// unlocking within the same thread
|
||||
pthread_mutexattr_t mutex_attr;
|
||||
pthread_mutexattr_init(&mutex_attr);
|
||||
pthread_mutexattr_settype(&mutex_attr, PTHREAD_MUTEX_RECURSIVE);
|
||||
|
||||
pthread_mutex_init(&mutex, &mutex_attr);
|
||||
pthread_mutexattr_destroy(&mutex_attr);
|
||||
|
||||
run_common_tests(&mutex);
|
||||
|
||||
fprintf(stderr, "Starting same_thread_multiple_rec_mutex_lock test\n");
|
||||
same_thread_multiple_rec_mutex_lock(&mutex);
|
||||
fprintf(stderr, "Finished same_thread_multiple_rec_mutex_lock test\n");
|
||||
|
||||
fprintf(stderr, "Starting same_thread_multiple_rec_mutex_lock test in "
|
||||
"non-main thread\n");
|
||||
pthread_t tid;
|
||||
spawn_thread(&tid, same_thread_multiple_rec_mutex_lock, &mutex);
|
||||
assert(pthread_join(tid, NULL) == 0
|
||||
&& "Non-main thread should be joined successfully");
|
||||
fprintf(stderr, "Finished same_thread_multiple_rec_mutex_lock test in "
|
||||
"non-main thread\n");
|
||||
|
||||
fprintf(stderr, "Recursive mutex test is completed\n");
|
||||
pthread_mutex_destroy(&mutex);
|
||||
}
|
||||
@ -0,0 +1,117 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Amazon.com Inc. or its affiliates. All rights reserved.
|
||||
* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
*/
|
||||
|
||||
#ifndef __wasi__
|
||||
#error This example only compiles to WASM/WASI target
|
||||
#endif
|
||||
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <math.h>
|
||||
#include <pthread.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
|
||||
enum CONSTANTS {
|
||||
NUM_ITER = 100000,
|
||||
NUM_RETRY = 8,
|
||||
MAX_NUM_THREADS = 12,
|
||||
RETRY_SLEEP_TIME_US = 2000,
|
||||
};
|
||||
|
||||
unsigned prime_numbers_count = 0;
|
||||
|
||||
bool
|
||||
is_prime(unsigned int num)
|
||||
{
|
||||
for (unsigned int i = 2; i <= (unsigned int)(sqrt(num)); ++i) {
|
||||
if (num % i == 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void *
|
||||
check_if_prime(void *value)
|
||||
{
|
||||
unsigned int *num = (unsigned int *)(value);
|
||||
usleep(10000);
|
||||
if (is_prime(*num)) {
|
||||
__atomic_fetch_add(&prime_numbers_count, 1, __ATOMIC_SEQ_CST);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
unsigned int
|
||||
validate()
|
||||
{
|
||||
unsigned int counter = 0;
|
||||
for (unsigned int i = 2; i <= NUM_ITER; ++i) {
|
||||
counter += is_prime(i);
|
||||
}
|
||||
|
||||
return counter;
|
||||
}
|
||||
|
||||
void
|
||||
spawn_thread(pthread_t *thread, unsigned int *arg)
|
||||
{
|
||||
int status_code = -1;
|
||||
int timeout_us = RETRY_SLEEP_TIME_US;
|
||||
for (int tries = 0; status_code != 0 && tries < NUM_RETRY; ++tries) {
|
||||
status_code = pthread_create(thread, NULL, &check_if_prime, arg);
|
||||
assert(status_code == 0 || status_code == EAGAIN);
|
||||
if (status_code == EAGAIN) {
|
||||
usleep(timeout_us);
|
||||
timeout_us *= 2;
|
||||
}
|
||||
}
|
||||
|
||||
assert(status_code == 0 && "Thread creation should succeed");
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
pthread_t threads[MAX_NUM_THREADS];
|
||||
unsigned int args[MAX_NUM_THREADS];
|
||||
double percentage = 0.1;
|
||||
|
||||
for (unsigned int factorised_number = 2; factorised_number < NUM_ITER;
|
||||
++factorised_number) {
|
||||
if (factorised_number > NUM_ITER * percentage) {
|
||||
fprintf(stderr, "Stress test is %d%% finished\n",
|
||||
(unsigned int)(percentage * 100));
|
||||
percentage += 0.1;
|
||||
}
|
||||
|
||||
unsigned int thread_num = factorised_number % MAX_NUM_THREADS;
|
||||
if (threads[thread_num] != 0) {
|
||||
assert(pthread_join(threads[thread_num], NULL) == 0);
|
||||
}
|
||||
|
||||
args[thread_num] = factorised_number;
|
||||
|
||||
usleep(RETRY_SLEEP_TIME_US);
|
||||
spawn_thread(&threads[thread_num], &args[thread_num]);
|
||||
assert(threads[thread_num] != 0);
|
||||
}
|
||||
|
||||
for (int i = 0; i < MAX_NUM_THREADS; ++i) {
|
||||
assert(threads[i] == 0 || pthread_join(threads[i], NULL) == 0);
|
||||
}
|
||||
|
||||
// Check the test results
|
||||
assert(
|
||||
prime_numbers_count == validate()
|
||||
&& "Answer mismatch between tested code and reference implementation");
|
||||
|
||||
fprintf(stderr, "Stress test finished successfully\n");
|
||||
return 0;
|
||||
}
|
||||
@ -0,0 +1,95 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Amazon.com Inc. or its affiliates. All rights reserved.
|
||||
* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <pthread.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
|
||||
enum CONSTANTS {
|
||||
NUM_ITER = 200000,
|
||||
NUM_RETRY = 8,
|
||||
MAX_NUM_THREADS = 12,
|
||||
RETRY_SLEEP_TIME_US = 4000,
|
||||
SECOND = 1000 * 1000 * 1000
|
||||
};
|
||||
|
||||
int threads_executed = 0;
|
||||
unsigned int threads_creation_tried = 0;
|
||||
unsigned int threads_in_use = 0;
|
||||
|
||||
void *
|
||||
thread_func(void *arg)
|
||||
{
|
||||
(void)(arg);
|
||||
__atomic_fetch_add(&threads_executed, 1, __ATOMIC_RELAXED);
|
||||
__atomic_fetch_sub(&threads_in_use, 1, __ATOMIC_SEQ_CST);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void
|
||||
spawn_thread(pthread_t *thread)
|
||||
{
|
||||
int status_code = -1;
|
||||
int timeout_us = RETRY_SLEEP_TIME_US;
|
||||
for (int tries = 0; status_code != 0 && tries < NUM_RETRY; ++tries) {
|
||||
status_code = pthread_create(thread, NULL, &thread_func, NULL);
|
||||
__atomic_fetch_add(&threads_creation_tried, 1, __ATOMIC_RELAXED);
|
||||
|
||||
assert(status_code == 0 || status_code == EAGAIN);
|
||||
if (status_code == EAGAIN) {
|
||||
usleep(timeout_us);
|
||||
timeout_us *= 2;
|
||||
}
|
||||
}
|
||||
|
||||
assert(status_code == 0 && "Thread creation should succeed");
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
double percentage = 0.1;
|
||||
|
||||
for (int iter = 0; iter < NUM_ITER; ++iter) {
|
||||
if (iter > NUM_ITER * percentage) {
|
||||
fprintf(stderr, "Spawning stress test is %d%% finished\n",
|
||||
(unsigned int)(percentage * 100));
|
||||
percentage += 0.1;
|
||||
}
|
||||
while (__atomic_load_n(&threads_in_use, __ATOMIC_SEQ_CST)
|
||||
== MAX_NUM_THREADS) {
|
||||
usleep(100);
|
||||
}
|
||||
|
||||
__atomic_fetch_add(&threads_in_use, 1, __ATOMIC_SEQ_CST);
|
||||
pthread_t tmp;
|
||||
spawn_thread(&tmp);
|
||||
pthread_detach(tmp);
|
||||
}
|
||||
|
||||
while ((__atomic_load_n(&threads_in_use, __ATOMIC_SEQ_CST) != 0)) {
|
||||
// Casting to int* to supress compiler warning
|
||||
__builtin_wasm_memory_atomic_wait32((int *)(&threads_in_use), 0,
|
||||
SECOND);
|
||||
}
|
||||
|
||||
assert(__atomic_load_n(&threads_in_use, __ATOMIC_SEQ_CST) == 0);
|
||||
|
||||
// Validation
|
||||
assert(threads_creation_tried >= threads_executed
|
||||
&& "Test executed more threads than were created");
|
||||
assert((1. * threads_creation_tried) / threads_executed < 2.5
|
||||
&& "Ensuring that we're retrying thread creation less than 2.5 "
|
||||
"times on average ");
|
||||
|
||||
fprintf(stderr,
|
||||
"Spawning stress test finished successfully executed %d threads "
|
||||
"with retry ratio %f\n",
|
||||
threads_creation_tried,
|
||||
(1. * threads_creation_tried) / threads_executed);
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user