-
Notifications
You must be signed in to change notification settings - Fork 791
Add mutex stress test #2472
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
wenyongh
merged 1 commit into
bytecodealliance:main
from
Zzzabiyaka:makslit/mutex_stress_test
Aug 30, 2023
Merged
Add mutex stress test #2472
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
65 changes: 65 additions & 0 deletions
65
core/iwasm/libraries/lib-wasi-threads/stress-test/build.sh
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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 |
27 changes: 27 additions & 0 deletions
27
core/iwasm/libraries/lib-wasi-threads/stress-test/errorcheck_mutex_stress_test.c
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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); | ||
| } |
3 changes: 3 additions & 0 deletions
3
core/iwasm/libraries/lib-wasi-threads/stress-test/manifest.json
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,3 @@ | ||
| { | ||
| "name": "lib-wasi-threads stress tests" | ||
| } |
229 changes: 229 additions & 0 deletions
229
core/iwasm/libraries/lib-wasi-threads/stress-test/mutex_common.h
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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 |
20 changes: 20 additions & 0 deletions
20
core/iwasm/libraries/lib-wasi-threads/stress-test/normal_mutex_stress_test.c
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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); | ||
| } |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.