diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-04 18:04:16 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-04 18:04:16 +0000 |
commit | a68fb2d8219f6bccc573009600e9f23e89226a5e (patch) | |
tree | d742d35d14ae816e99293d2b01face30e9f3a46b /unittest/mysys/waiting_threads-t.c | |
parent | Initial commit. (diff) | |
download | mariadb-10.6-a68fb2d8219f6bccc573009600e9f23e89226a5e.tar.xz mariadb-10.6-a68fb2d8219f6bccc573009600e9f23e89226a5e.zip |
Adding upstream version 1:10.6.11.upstream/1%10.6.11upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'unittest/mysys/waiting_threads-t.c')
-rw-r--r-- | unittest/mysys/waiting_threads-t.c | 286 |
1 files changed, 286 insertions, 0 deletions
diff --git a/unittest/mysys/waiting_threads-t.c b/unittest/mysys/waiting_threads-t.c new file mode 100644 index 00000000..82623157 --- /dev/null +++ b/unittest/mysys/waiting_threads-t.c @@ -0,0 +1,286 @@ +/* Copyright (C) 2008 MySQL AB, 2008-2009 Sun Microsystems, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA */ + +#include "thr_template.c" +#include <waiting_threads.h> +#include <m_string.h> + +struct test_wt_thd { + WT_THD thd; + pthread_mutex_t lock; +} thds[THREADS]; + +uint i, cnt; +pthread_mutex_t lock; +pthread_cond_t thread_sync; + +ulong wt_timeout_short=100, wt_deadlock_search_depth_short=4; +ulong wt_timeout_long=10000, wt_deadlock_search_depth_long=15; + +#define reset(ARRAY) bzero(ARRAY, sizeof(ARRAY)) + +/* see explanation of the kill strategies in waiting_threads.h */ +enum { LATEST, RANDOM, YOUNGEST, LOCKS } kill_strategy; + +WT_RESOURCE_TYPE restype={ wt_resource_id_memcmp, 0}; + +#define rnd() ((uint)(my_rnd(&rand) * INT_MAX32)) + +/* + stress test: wait on a random number of random threads. + it always succeeds (unless crashes or hangs). +*/ +pthread_handler_t test_wt(void *arg) +{ + int m, n, i, id, res; + struct my_rnd_struct rand; + + my_thread_init(); + + pthread_mutex_lock(&mutex); + id= cnt++; + wt_thd_lazy_init(& thds[id].thd, + & wt_deadlock_search_depth_short, & wt_timeout_short, + & wt_deadlock_search_depth_long, & wt_timeout_long); + + /* now, wait for everybody to be ready to run */ + if (cnt >= THREADS) + pthread_cond_broadcast(&thread_sync); + else + while (cnt < THREADS) + pthread_cond_wait(&thread_sync, &mutex); + pthread_mutex_unlock(&mutex); + + my_rnd_init(&rand, (ulong)(intptr)&m, id); + if (kill_strategy == YOUNGEST) + thds[id].thd.weight= (ulong) ~ my_interval_timer(); + if (kill_strategy == LOCKS) + thds[id].thd.weight= 0; + + for (m= *(int *)arg; m ; m--) + { + WT_RESOURCE_ID resid; + int blockers[THREADS/10], j, k; + + resid.value= id; + resid.type= &restype; + + res= 0; + + /* prepare for waiting for a random number of random threads */ + for (j= n= (rnd() % THREADS)/10; !res && j >= 0; j--) + { +retry: + i= rnd() % (THREADS-1); /* pick a random thread */ + if (i >= id) i++; /* with a number from 0 to THREADS-1 excluding ours */ + + for (k=n; k >=j; k--) /* the one we didn't pick before */ + if (blockers[k] == i) + goto retry; + blockers[j]= i; + + if (kill_strategy == RANDOM) + thds[id].thd.weight= rnd(); + + pthread_mutex_lock(& thds[i].lock); + res= wt_thd_will_wait_for(& thds[id].thd, & thds[i].thd, &resid); + pthread_mutex_unlock(& thds[i].lock); + } + + if (!res) + { + pthread_mutex_lock(&lock); + res= wt_thd_cond_timedwait(& thds[id].thd, &lock); + pthread_mutex_unlock(&lock); + } + + if (res) + { + pthread_mutex_lock(& thds[id].lock); + pthread_mutex_lock(&lock); + wt_thd_release_all(& thds[id].thd); + pthread_mutex_unlock(&lock); + pthread_mutex_unlock(& thds[id].lock); + if (kill_strategy == LOCKS) + thds[id].thd.weight= 0; + if (kill_strategy == YOUNGEST) + thds[id].thd.weight= (ulong)~ my_interval_timer(); + } + else if (kill_strategy == LOCKS) + thds[id].thd.weight++; + } + + pthread_mutex_lock(&mutex); + /* wait for everybody to finish */ + if (!--cnt) + pthread_cond_broadcast(&thread_sync); + else + while (cnt) + pthread_cond_wait(&thread_sync, &mutex); + + pthread_mutex_lock(& thds[id].lock); + pthread_mutex_lock(&lock); + wt_thd_release_all(& thds[id].thd); + pthread_mutex_unlock(&lock); + pthread_mutex_unlock(& thds[id].lock); + wt_thd_destroy(& thds[id].thd); + pthread_mutex_unlock(&mutex); + + DBUG_PRINT("wt", ("exiting")); + my_thread_end(); + return 0; +} + +void do_one_test() +{ + double sum, sum0; + DBUG_ENTER("do_one_test"); + + reset(wt_cycle_stats); + reset(wt_wait_stats); + wt_success_stats=0; + cnt=0; + test_concurrently("waiting_threads", test_wt, THREADS, CYCLES); + + sum=sum0=0; + for (cnt=0; cnt < WT_CYCLE_STATS; cnt++) + sum+= wt_cycle_stats[0][cnt] + wt_cycle_stats[1][cnt]; + for (cnt=0; cnt < WT_CYCLE_STATS; cnt++) + if (wt_cycle_stats[0][cnt] + wt_cycle_stats[1][cnt] > 0) + { + sum0+=wt_cycle_stats[0][cnt] + wt_cycle_stats[1][cnt]; + diag("deadlock cycles of length %2u: %4u %4u %8.2f %%", cnt, + wt_cycle_stats[0][cnt], wt_cycle_stats[1][cnt], 1e2*sum0/sum); + } + diag("depth exceeded: %u %u", + wt_cycle_stats[0][cnt], wt_cycle_stats[1][cnt]); + for (cnt=0; cnt < WT_WAIT_STATS; cnt++) + if (wt_wait_stats[cnt]>0) + diag("deadlock waits up to %7llu us: %5u", + wt_wait_table[cnt], wt_wait_stats[cnt]); + diag("timed out: %u", wt_wait_stats[cnt]); + diag("successes: %u", wt_success_stats); + + DBUG_VOID_RETURN; +} + +void do_tests() +{ + DBUG_ENTER("do_tests"); + if (skip_big_tests) + { + skip(1, "Big test skipped"); + return; + } + plan(13); + compile_time_assert(THREADS >= 4); + + DBUG_PRINT("wt", ("================= initialization ===================")); + + pthread_cond_init(&thread_sync, 0); + pthread_mutex_init(&lock, 0); + wt_init(); + for (cnt=0; cnt < THREADS; cnt++) + pthread_mutex_init(& thds[cnt].lock, 0); + { + WT_RESOURCE_ID resid[4]; + for (i=0; i < array_elements(resid); i++) + { + wt_thd_lazy_init(& thds[i].thd, + & wt_deadlock_search_depth_short, & wt_timeout_short, + & wt_deadlock_search_depth_long, & wt_timeout_long); + resid[i].value= i+1; + resid[i].type= &restype; + } + + DBUG_PRINT("wt", ("================= manual test ===================")); + +#define ok_wait(X,Y, R) \ + ok(wt_thd_will_wait_for(& thds[X].thd, & thds[Y].thd, &resid[R]) == 0, \ + "thd[" #X "] will wait for thd[" #Y "]") +#define ok_deadlock(X,Y,R) \ + ok(wt_thd_will_wait_for(& thds[X].thd, & thds[Y].thd, &resid[R]) == WT_DEADLOCK, \ + "thd[" #X "] will wait for thd[" #Y "] - deadlock") + + ok_wait(0,1,0); + ok_wait(0,2,0); + ok_wait(0,3,0); + + pthread_mutex_lock(&lock); + bad= wt_thd_cond_timedwait(& thds[0].thd, &lock); + pthread_mutex_unlock(&lock); + ok(bad == WT_TIMEOUT, "timeout test returned %d", bad); + + ok_wait(0,1,0); + ok_wait(1,2,1); + ok_deadlock(2,0,2); + + pthread_mutex_lock(&lock); + ok(wt_thd_cond_timedwait(& thds[0].thd, &lock) == WT_TIMEOUT, "as always"); + ok(wt_thd_cond_timedwait(& thds[1].thd, &lock) == WT_TIMEOUT, "as always"); + wt_thd_release_all(& thds[0].thd); + wt_thd_release_all(& thds[1].thd); + wt_thd_release_all(& thds[2].thd); + wt_thd_release_all(& thds[3].thd); + + for (i=0; i < array_elements(resid); i++) + { + wt_thd_release_all(& thds[i].thd); + wt_thd_destroy(& thds[i].thd); + } + pthread_mutex_unlock(&lock); + } + + wt_deadlock_search_depth_short=6; + wt_timeout_short=1000; + wt_timeout_long= 100; + wt_deadlock_search_depth_long=16; + DBUG_PRINT("wt", ("================= stress test ===================")); + + diag("timeout_short=%lu us, deadlock_search_depth_short=%lu", + wt_timeout_short, wt_deadlock_search_depth_short); + diag("timeout_long=%lu us, deadlock_search_depth_long=%lu", + wt_timeout_long, wt_deadlock_search_depth_long); + +#ifndef _WIN32 +#define test_kill_strategy(X) \ + diag("kill strategy: " #X); \ + DBUG_EXECUTE("reset_file", \ + { rewind(DBUG_FILE); my_chsize(fileno(DBUG_FILE), 0, 0, MYF(MY_WME)); }); \ + DBUG_PRINT("info", ("kill strategy: " #X)); \ + kill_strategy=X; \ + do_one_test(); +#else +#define test_kill_strategy(X) \ + diag("kill strategy: " #X); \ + DBUG_PRINT("info", ("kill strategy: " #X)); \ + kill_strategy=X; \ + do_one_test(); +#endif + + test_kill_strategy(LATEST); + test_kill_strategy(RANDOM); + test_kill_strategy(YOUNGEST); + test_kill_strategy(LOCKS); + + DBUG_PRINT("wt", ("================= cleanup ===================")); + for (cnt=0; cnt < THREADS; cnt++) + pthread_mutex_destroy(& thds[cnt].lock); + wt_end(); + pthread_mutex_destroy(&lock); + pthread_cond_destroy(&thread_sync); + DBUG_VOID_RETURN; +} + |