diff options
Diffstat (limited to 'unittest')
-rw-r--r-- | unittest/Makefile.am | 5 | ||||
-rw-r--r-- | unittest/mysys/CMakeLists.txt | 34 | ||||
-rw-r--r-- | unittest/mysys/Makefile.am | 9 | ||||
-rw-r--r-- | unittest/mysys/base64-t.c | 1 | ||||
-rw-r--r-- | unittest/mysys/lf-t.c | 178 | ||||
-rw-r--r-- | unittest/mysys/my_atomic-t.c | 146 | ||||
-rw-r--r-- | unittest/mysys/thr_template.c | 92 | ||||
-rw-r--r-- | unittest/mysys/waiting_threads-t.c | 286 | ||||
-rw-r--r-- | unittest/mytap/CMakeLists.txt | 20 | ||||
-rw-r--r-- | unittest/mytap/Makefile.am | 2 | ||||
-rw-r--r-- | unittest/mytap/tap.c | 33 | ||||
-rw-r--r-- | unittest/mytap/tap.h | 47 | ||||
-rw-r--r-- | unittest/unit.pl | 18 |
13 files changed, 851 insertions, 20 deletions
diff --git a/unittest/Makefile.am b/unittest/Makefile.am index 889e029f6ef..01d27eb8cd2 100644 --- a/unittest/Makefile.am +++ b/unittest/Makefile.am @@ -18,13 +18,10 @@ SUBDIRS = mytap . mysys examples strings EXTRA_DIST = unit.pl CLEANFILES = unit -unittests = mytap mysys strings @mysql_se_unittest_dirs@ @mysql_pg_unittest_dirs@ +unittests = mytap mysys strings @mysql_se_unittest_dirs@ @mysql_pg_unittest_dirs@ ../dbug test: perl unit.pl run $(unittests) test-verbose: HARNESS_VERBOSE=1 perl unit.pl run $(unittests) - -# Don't update the files from bitkeeper -%::SCCS/s.% diff --git a/unittest/mysys/CMakeLists.txt b/unittest/mysys/CMakeLists.txt new file mode 100644 index 00000000000..e13a55d2e1e --- /dev/null +++ b/unittest/mysys/CMakeLists.txt @@ -0,0 +1,34 @@ +# Copyright (C) 2007 MySQL AB +# +# 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 St, Fifth Floor, Boston, MA 02110-1301 USA + +SET(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DSAFEMALLOC -DSAFE_MUTEX") +SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -DSAFEMALLOC -DSAFE_MUTEX") + +INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/include + ${CMAKE_SOURCE_DIR}/unittest/mytap) +ADD_EXECUTABLE(bitmap-t bitmap-t.c) +TARGET_LINK_LIBRARIES(bitmap-t mytap mysys dbug strings wsock32) + +ADD_EXECUTABLE(base64-t base64-t.c) +TARGET_LINK_LIBRARIES(base64-t mytap mysys dbug strings wsock32) + +ADD_EXECUTABLE(my_atomic-t my_atomic-t.c) +TARGET_LINK_LIBRARIES(my_atomic-t mytap mysys dbug strings wsock32) + +ADD_EXECUTABLE(lf-t lf-t.c) +TARGET_LINK_LIBRARIES(lf-t mytap mysys dbug strings wsock32) + +ADD_EXECUTABLE(waiting_threads-t waiting_threads-t.c) +TARGET_LINK_LIBRARIES(waiting_threads-t mytap mysys dbug strings wsock32) diff --git a/unittest/mysys/Makefile.am b/unittest/mysys/Makefile.am index b195017e914..ad7ae4178d1 100644 --- a/unittest/mysys/Makefile.am +++ b/unittest/mysys/Makefile.am @@ -13,15 +13,18 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -AM_CPPFLAGS = @ZLIB_INCLUDES@ -I$(top_builddir)/include -AM_CPPFLAGS += -I$(top_srcdir)/include -I$(top_srcdir)/unittest/mytap +INCLUDES = @ZLIB_INCLUDES@ -I$(top_builddir)/include \ + -I$(top_srcdir)/include -I$(top_srcdir)/unittest/mytap + +noinst_HEADERS = thr_template.c LDADD = $(top_builddir)/unittest/mytap/libmytap.a \ $(top_builddir)/mysys/libmysys.a \ $(top_builddir)/dbug/libdbug.a \ $(top_builddir)/strings/libmystrings.a -noinst_PROGRAMS = bitmap-t base64-t +EXTRA_DIST = CMakeLists.txt +noinst_PROGRAMS = bitmap-t base64-t my_atomic-t lf-t waiting_threads-t # Don't update the files from bitkeeper %::SCCS/s.% diff --git a/unittest/mysys/base64-t.c b/unittest/mysys/base64-t.c index e1a40f89ff0..cb0437f947c 100644 --- a/unittest/mysys/base64-t.c +++ b/unittest/mysys/base64-t.c @@ -66,6 +66,7 @@ main(void) ok(cmp == 0, "Comparing encode-decode result"); if (cmp != 0) { + /* FIXME: This only prints last value of the compared strings */ char buf[80]; diag(" --------- src --------- --------- dst ---------"); for (k= 0; k<src_len; k+=8) diff --git a/unittest/mysys/lf-t.c b/unittest/mysys/lf-t.c new file mode 100644 index 00000000000..61b7ae08cf5 --- /dev/null +++ b/unittest/mysys/lf-t.c @@ -0,0 +1,178 @@ +/* Copyright (C) 2008-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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +/** + @file + + Unit tests for lock-free algorithms of mysys +*/ + +#include "thr_template.c" + +#include <lf.h> + +int32 inserts= 0, N; +LF_ALLOCATOR lf_allocator; +LF_HASH lf_hash; + +/* + pin allocator - alloc and release an element in a loop +*/ +pthread_handler_t test_lf_pinbox(void *arg) +{ + int m= *(int *)arg; + int32 x= 0; + LF_PINS *pins; + + my_thread_init(); + + pins= lf_pinbox_get_pins(&lf_allocator.pinbox); + + for (x= ((int)(intptr)(&m)); m ; m--) + { + lf_pinbox_put_pins(pins); + pins= lf_pinbox_get_pins(&lf_allocator.pinbox); + } + lf_pinbox_put_pins(pins); + pthread_mutex_lock(&mutex); + if (!--running_threads) pthread_cond_signal(&cond); + pthread_mutex_unlock(&mutex); + my_thread_end(); + return 0; +} + +/* + thread local data area, allocated using lf_alloc. + union is required to enforce the minimum required element size (sizeof(ptr)) +*/ +typedef union { + int32 data; + void *not_used; +} TLA; + +pthread_handler_t test_lf_alloc(void *arg) +{ + int m= (*(int *)arg)/2; + int32 x,y= 0; + LF_PINS *pins; + + my_thread_init(); + + pins= lf_alloc_get_pins(&lf_allocator); + + for (x= ((int)(intptr)(&m)); m ; m--) + { + TLA *node1, *node2; + x= (x*m+0x87654321) & INT_MAX32; + node1= (TLA *)lf_alloc_new(pins); + node1->data= x; + y+= node1->data; + node1->data= 0; + node2= (TLA *)lf_alloc_new(pins); + node2->data= x; + y-= node2->data; + node2->data= 0; + lf_alloc_free(pins, node1); + lf_alloc_free(pins, node2); + } + lf_alloc_put_pins(pins); + pthread_mutex_lock(&mutex); + bad+= y; + + if (--N == 0) + { + diag("%d mallocs, %d pins in stack", + lf_allocator.mallocs, lf_allocator.pinbox.pins_in_array); +#ifdef MY_LF_EXTRA_DEBUG + bad|= lf_allocator.mallocs - lf_alloc_pool_count(&lf_allocator); +#endif + } + if (!--running_threads) pthread_cond_signal(&cond); + pthread_mutex_unlock(&mutex); + my_thread_end(); + return 0; +} + +#define N_TLH 1000 +pthread_handler_t test_lf_hash(void *arg) +{ + int m= (*(int *)arg)/(2*N_TLH); + int32 x,y,z,sum= 0, ins= 0; + LF_PINS *pins; + + my_thread_init(); + + pins= lf_hash_get_pins(&lf_hash); + + for (x= ((int)(intptr)(&m)); m ; m--) + { + int i; + y= x; + for (i= 0; i < N_TLH; i++) + { + x= (x*(m+i)+0x87654321) & INT_MAX32; + z= (x<0) ? -x : x; + if (lf_hash_insert(&lf_hash, pins, &z)) + { + sum+= z; + ins++; + } + } + for (i= 0; i < N_TLH; i++) + { + y= (y*(m+i)+0x87654321) & INT_MAX32; + z= (y<0) ? -y : y; + if (lf_hash_delete(&lf_hash, pins, (uchar *)&z, sizeof(z))) + sum-= z; + } + } + lf_hash_put_pins(pins); + pthread_mutex_lock(&mutex); + bad+= sum; + inserts+= ins; + + if (--N == 0) + { + diag("%d mallocs, %d pins in stack, %d hash size, %d inserts", + lf_hash.alloc.mallocs, lf_hash.alloc.pinbox.pins_in_array, + lf_hash.size, inserts); + bad|= lf_hash.count; + } + if (!--running_threads) pthread_cond_signal(&cond); + pthread_mutex_unlock(&mutex); + my_thread_end(); + return 0; +} + + +void do_tests() +{ + plan(4); + + lf_alloc_init(&lf_allocator, sizeof(TLA), offsetof(TLA, not_used)); + lf_hash_init(&lf_hash, sizeof(int), LF_HASH_UNIQUE, 0, sizeof(int), 0, + &my_charset_bin); + + bad= my_atomic_initialize(); + ok(!bad, "my_atomic_initialize() returned %d", bad); + + test_concurrently("lf_pinbox", test_lf_pinbox, N= THREADS, CYCLES); + test_concurrently("lf_alloc", test_lf_alloc, N= THREADS, CYCLES); + test_concurrently("lf_hash", test_lf_hash, N= THREADS, CYCLES/10); + + lf_hash_destroy(&lf_hash); + lf_alloc_destroy(&lf_allocator); +} + diff --git a/unittest/mysys/my_atomic-t.c b/unittest/mysys/my_atomic-t.c new file mode 100644 index 00000000000..b98f3379a4a --- /dev/null +++ b/unittest/mysys/my_atomic-t.c @@ -0,0 +1,146 @@ +/* Copyright (C) 2006-2008 MySQL AB, 2008 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#include "thr_template.c" + +/* at least gcc 3.4.5 and 3.4.6 (but not 3.2.3) on RHEL */ +#if __GNUC__ == 3 && __GNUC_MINOR__ == 4 +#define GCC_BUG_WORKAROUND volatile +#else +#define GCC_BUG_WORKAROUND +#endif + +volatile uint32 b32; +volatile int32 c32; +my_atomic_rwlock_t rwl; + +/* add and sub a random number in a loop. Must get 0 at the end */ +pthread_handler_t test_atomic_add(void *arg) +{ + int m= (*(int *)arg)/2; + GCC_BUG_WORKAROUND int32 x; + for (x= ((int)(intptr)(&m)); m ; m--) + { + x= (x*m+0x87654321) & INT_MAX32; + my_atomic_rwlock_wrlock(&rwl); + my_atomic_add32(&bad, x); + my_atomic_rwlock_wrunlock(&rwl); + + my_atomic_rwlock_wrlock(&rwl); + my_atomic_add32(&bad, -x); + my_atomic_rwlock_wrunlock(&rwl); + } + pthread_mutex_lock(&mutex); + if (!--running_threads) pthread_cond_signal(&cond); + pthread_mutex_unlock(&mutex); + return 0; +} + +/* + 1. generate thread number 0..N-1 from b32 + 2. add it to bad + 3. swap thread numbers in c32 + 4. (optionally) one more swap to avoid 0 as a result + 5. subtract result from bad + must get 0 in bad at the end +*/ +pthread_handler_t test_atomic_fas(void *arg) +{ + int m= *(int *)arg; + int32 x; + + my_atomic_rwlock_wrlock(&rwl); + x= my_atomic_add32(&b32, 1); + my_atomic_rwlock_wrunlock(&rwl); + + my_atomic_rwlock_wrlock(&rwl); + my_atomic_add32(&bad, x); + my_atomic_rwlock_wrunlock(&rwl); + + for (; m ; m--) + { + my_atomic_rwlock_wrlock(&rwl); + x= my_atomic_fas32(&c32, x); + my_atomic_rwlock_wrunlock(&rwl); + } + + if (!x) + { + my_atomic_rwlock_wrlock(&rwl); + x= my_atomic_fas32(&c32, x); + my_atomic_rwlock_wrunlock(&rwl); + } + + my_atomic_rwlock_wrlock(&rwl); + my_atomic_add32(&bad, -x); + my_atomic_rwlock_wrunlock(&rwl); + + pthread_mutex_lock(&mutex); + if (!--running_threads) pthread_cond_signal(&cond); + pthread_mutex_unlock(&mutex); + return 0; +} + +/* + same as test_atomic_add, but my_atomic_add32 is emulated with + my_atomic_cas32 - notice that the slowdown is proportional to the + number of CPUs +*/ +pthread_handler_t test_atomic_cas(void *arg) +{ + int m= (*(int *)arg)/2, ok= 0; + GCC_BUG_WORKAROUND int32 x, y; + for (x= ((int)(intptr)(&m)); m ; m--) + { + my_atomic_rwlock_wrlock(&rwl); + y= my_atomic_load32(&bad); + my_atomic_rwlock_wrunlock(&rwl); + x= (x*m+0x87654321) & INT_MAX32; + do { + my_atomic_rwlock_wrlock(&rwl); + ok= my_atomic_cas32(&bad, &y, (uint32)y+x); + my_atomic_rwlock_wrunlock(&rwl); + } while (!ok) ; + do { + my_atomic_rwlock_wrlock(&rwl); + ok= my_atomic_cas32(&bad, &y, y-x); + my_atomic_rwlock_wrunlock(&rwl); + } while (!ok) ; + } + pthread_mutex_lock(&mutex); + if (!--running_threads) pthread_cond_signal(&cond); + pthread_mutex_unlock(&mutex); + return 0; +} + +void do_tests() +{ + plan(4); + + bad= my_atomic_initialize(); + ok(!bad, "my_atomic_initialize() returned %d", bad); + + my_atomic_rwlock_init(&rwl); + + b32= c32= 0; + test_concurrently("my_atomic_add32", test_atomic_add, THREADS, CYCLES); + b32= c32= 0; + test_concurrently("my_atomic_fas32", test_atomic_fas, THREADS, CYCLES); + b32= c32= 0; + test_concurrently("my_atomic_cas32", test_atomic_cas, THREADS, CYCLES); + + my_atomic_rwlock_destroy(&rwl); +} + diff --git a/unittest/mysys/thr_template.c b/unittest/mysys/thr_template.c new file mode 100644 index 00000000000..1ac03e474fd --- /dev/null +++ b/unittest/mysys/thr_template.c @@ -0,0 +1,92 @@ +/* Copyright (C) 2006-2008 MySQL AB, 2008 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#include <my_global.h> +#include <my_sys.h> +#include <my_atomic.h> +#include <tap.h> + +volatile uint32 bad; +pthread_attr_t thr_attr; +pthread_mutex_t mutex; +pthread_cond_t cond; +uint running_threads; + +void do_tests(); + +void test_concurrently(const char *test, pthread_handler handler, int n, int m) +{ + pthread_t t; + ulonglong now= my_getsystime(); + + bad= 0; + + diag("Testing %s with %d threads, %d iterations... ", test, n, m); + for (running_threads= n ; n ; n--) + { + if (pthread_create(&t, &thr_attr, handler, &m) != 0) + { + diag("Could not create thread"); + abort(); + } + } + pthread_mutex_lock(&mutex); + while (running_threads) + pthread_cond_wait(&cond, &mutex); + pthread_mutex_unlock(&mutex); + + now= my_getsystime()-now; + ok(!bad, "tested %s in %g secs (%d)", test, ((double)now)/1e7, bad); +} + +int main(int argc __attribute__((unused)), char **argv) +{ + MY_INIT("thd_template"); + + if (argv[1] && *argv[1]) + DBUG_SET_INITIAL(argv[1]); + + pthread_mutex_init(&mutex, 0); + pthread_cond_init(&cond, 0); + pthread_attr_init(&thr_attr); + pthread_attr_setdetachstate(&thr_attr,PTHREAD_CREATE_DETACHED); + +#ifdef MY_ATOMIC_MODE_RWLOCKS +#if defined(HPUX11) || defined(__POWERPC__) /* showed to be very slow (scheduler-related) */ +#define CYCLES 300 +#else +#define CYCLES 3000 +#endif +#else +#define CYCLES 3000 +#endif +#define THREADS 30 + + diag("N CPUs: %d, atomic ops: %s", my_getncpus(), MY_ATOMIC_MODE); + + do_tests(); + + /* + workaround until we know why it crashes randomly on some machine + (BUG#22320). + */ + sleep(2); + pthread_mutex_destroy(&mutex); + pthread_cond_destroy(&cond); + pthread_attr_destroy(&thr_attr); + my_end(0); + return exit_status(); +} + diff --git a/unittest/mysys/waiting_threads-t.c b/unittest/mysys/waiting_threads-t.c new file mode 100644 index 00000000000..e32a83ef172 --- /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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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_getsystime(); + 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_getsystime(); + } + 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); + + if (!--running_threads) /* now, signal when everybody is done with deinit */ + pthread_cond_signal(&cond); + 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"); + plan(14); + compile_time_assert(THREADS >= 4); + + DBUG_PRINT("wt", ("================= initialization ===================")); + + bad= my_atomic_initialize(); + ok(!bad, "my_atomic_initialize() returned %d", bad); + + 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; +} + diff --git a/unittest/mytap/CMakeLists.txt b/unittest/mytap/CMakeLists.txt new file mode 100644 index 00000000000..2c0893226fa --- /dev/null +++ b/unittest/mytap/CMakeLists.txt @@ -0,0 +1,20 @@ +# Copyright (C) 2007 MySQL AB +# +# 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 St, Fifth Floor, Boston, MA 02110-1301 USA + +SET(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DSAFEMALLOC -DSAFE_MUTEX") +SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -DSAFEMALLOC -DSAFE_MUTEX") + +INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/include) +ADD_LIBRARY(mytap tap.c) diff --git a/unittest/mytap/Makefile.am b/unittest/mytap/Makefile.am index c02bcd3b49d..8feefb134bb 100644 --- a/unittest/mytap/Makefile.am +++ b/unittest/mytap/Makefile.am @@ -20,6 +20,8 @@ noinst_HEADERS = tap.h libmytap_a_SOURCES = tap.c +EXTRA_DIST = CMakeLists.txt + SUBDIRS = . t # Don't update the files from bitkeeper diff --git a/unittest/mytap/tap.c b/unittest/mytap/tap.c index a5831f2b71d..2f1747d7167 100644 --- a/unittest/mytap/tap.c +++ b/unittest/mytap/tap.c @@ -19,7 +19,7 @@ #include "tap.h" -#include "my_config.h" +#include "my_global.h" #include <stdlib.h> #include <stdarg.h> @@ -27,6 +27,16 @@ #include <string.h> #include <signal.h> +/* + Visual Studio 2003 does not know vsnprintf but knows _vsnprintf. + We don't put this #define in config-win.h because we prefer + my_vsnprintf everywhere instead, except when linking with libmysys + is not desirable - the case here. +*/ +#if defined(_MSC_VER) && ( _MSC_VER == 1310 ) +#define vsnprintf _vsnprintf +#endif + /** @defgroup MyTAP_Internal MyTAP Internals @@ -153,8 +163,10 @@ static signal_entry install_signal[]= { { SIGILL, handle_core_signal }, { SIGABRT, handle_core_signal }, { SIGFPE, handle_core_signal }, - { SIGSEGV, handle_core_signal }, - { SIGBUS, handle_core_signal } + { SIGSEGV, handle_core_signal } +#ifdef SIGBUS + , { SIGBUS, handle_core_signal } +#endif #ifdef SIGXCPU , { SIGXCPU, handle_core_signal } #endif @@ -169,13 +181,22 @@ static signal_entry install_signal[]= { #endif }; +int skip_big_tests= 1; + void -plan(int const count) +plan(int count) { + char *config= getenv("MYTAP_CONFIG"); + size_t i; + + if (config) + skip_big_tests= strcmp(config, "big"); + + setvbuf(tapout, 0, _IONBF, 0); /* provide output at once */ /* Install signal handler */ - size_t i; + for (i= 0; i < sizeof(install_signal)/sizeof(*install_signal); ++i) signal(install_signal[i].signo, install_signal[i].handler); @@ -208,7 +229,7 @@ skip_all(char const *reason, ...) } void -ok(int const pass, char const *fmt, ...) +ok(int pass, char const *fmt, ...) { va_list ap; va_start(ap, fmt); diff --git a/unittest/mytap/tap.h b/unittest/mytap/tap.h index 4118207ca7a..a75d0a4932b 100644 --- a/unittest/mytap/tap.h +++ b/unittest/mytap/tap.h @@ -62,6 +62,24 @@ extern "C" { #endif /** + Defines whether "big" tests should be skipped. + + This variable is set by plan() function unless MYTAP_CONFIG environment + variable is set to the string "big". It is supposed to be used as + + @code + if (skip_big_tests) { + skip(1, "Big test skipped"); + } else { + ok(life_universe_and_everything() == 42, "The answer is CORRECT"); + } + @endcode + + @see SKIP_BIG_TESTS +*/ +extern int skip_big_tests; + +/** @defgroup MyTAP_API MyTAP API MySQL support for performing unit tests according to TAP. @@ -80,10 +98,15 @@ extern "C" { that generate a core, so if you want to override these signals, do it <em>after</em> you have called the plan() function. - @param count The planned number of tests to run. + It will also set skip_big_tests variable if MYTAP_CONFIG environment + variable is defined. + + @see skip_big_tests + + @param count The planned number of tests to run. */ -void plan(int const count); +void plan(int count); /** @@ -102,7 +125,7 @@ void plan(int const count); which case nothing is printed. */ -void ok(int const pass, char const *fmt, ...) +void ok(int pass, char const *fmt, ...) __attribute__((format(printf,2,3))); @@ -160,6 +183,24 @@ void skip(int how_many, char const *reason, ...) /** + Helper macro to skip a group of "big" tests. It is used in the following + manner: + + @code + SKIP_BIG_TESTS(1) + { + ok(life_universe_and_everything() == 42, "The answer is CORRECT"); + } + @endcode + + @see skip_big_tests + */ + +#define SKIP_BIG_TESTS(COUNT) \ + if (skip_big_tests) skip((COUNT), "big test"); else + + +/** Print a diagnostics message. @param fmt Diagnostics message in printf() format. diff --git a/unittest/unit.pl b/unittest/unit.pl index 9d328985012..a1aab376fdf 100644 --- a/unittest/unit.pl +++ b/unittest/unit.pl @@ -14,8 +14,9 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -use Test::Harness qw(&runtests $verbose); +use Test::Harness; use File::Find; +use Getopt::Long; use strict; @@ -31,10 +32,19 @@ unit - Run unit tests in directory =head1 SYNOPSIS - unit run + unit [--[no]big] [--[no]verbose] run [tests to run] =cut +my $big= $ENV{'MYTAP_CONFIG'} eq 'big'; + +my $result = GetOptions ( + "big!" => \$big, + "verbose!" => \$Test::Harness::verbose, +); + +$ENV{'MYTAP_CONFIG'} = $big ? 'big' : ''; + my $cmd = shift; if (defined $cmd && exists $dispatch{$cmd}) { @@ -56,7 +66,7 @@ sub _find_test_files (@) { my @files; find sub { $File::Find::prune = 1 if /^SCCS$/; - push(@files, $File::Find::name) if -x _ && /-t\z/; + push(@files, $File::Find::name) if -x _ && (/-t\z/ || /-t\.exe\z/); }, @dirs; return @files; } @@ -92,7 +102,7 @@ sub run_cmd (@) { if (@files > 0) { # Removing the first './' from the file names foreach (@files) { s!^\./!! } - $ENV{'HARNESS_PERL_SWITCHES'} .= q" -e 'exec @ARGV'"; + $ENV{'HARNESS_PERL_SWITCHES'} .= ' -e "exec @ARGV"'; runtests @files; } } |