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:
Maks Litskevich
2023-08-30 12:01:44 +01:00
committed by GitHub
parent ff151fb7ba
commit 411b903cee
12 changed files with 442 additions and 37 deletions

View File

@ -34,7 +34,10 @@ while [[ $# -gt 0 ]]; do
done
# Stress tests names
thread_start_file_exclusions=("spawn_stress_test.wasm" "linear_memory_size_update.wasm" "stress_test_threads_creation.wasm")
thread_start_file_exclusions=("linear_memory_size_update.wasm")
rm -rf *.wasm
rm -rf *.aot
for test_c in *.c; do
test_wasm="$(basename $test_c .c).wasm"

View File

@ -1,6 +0,0 @@
{
"lib-wasi-threads tests": {
"spawn_stress_test": "Stress tests are incompatible with the other part and executed differently",
"stress_test_threads_creation": "Stress tests are incompatible with the other part and executed differently"
}
}

View File

@ -1,117 +0,0 @@
/*
* 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 = 8,
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;
}

View File

@ -1,93 +0,0 @@
/*
* 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 = 8,
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)) {
__builtin_wasm_memory_atomic_wait32(&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;
}