diff options
Diffstat (limited to 'storage/maria/unittest')
-rw-r--r-- | storage/maria/unittest/Makefile.am | 2 | ||||
-rw-r--r-- | storage/maria/unittest/lockman-t.c | 246 | ||||
-rw-r--r-- | storage/maria/unittest/trnman-t.c | 177 |
3 files changed, 424 insertions, 1 deletions
diff --git a/storage/maria/unittest/Makefile.am b/storage/maria/unittest/Makefile.am index 8a5ca3d669f..e29bc7f86cb 100644 --- a/storage/maria/unittest/Makefile.am +++ b/storage/maria/unittest/Makefile.am @@ -25,5 +25,5 @@ LDADD= $(top_builddir)/unittest/mytap/libmytap.a \ $(top_builddir)/mysys/libmysys.a \ $(top_builddir)/dbug/libdbug.a \ $(top_builddir)/strings/libmystrings.a @ZLIB_LIBS@ -noinst_PROGRAMS = ma_control_file-t +noinst_PROGRAMS = ma_control_file-t trnman-t lockman-t CLEANFILES = maria_control diff --git a/storage/maria/unittest/lockman-t.c b/storage/maria/unittest/lockman-t.c new file mode 100644 index 00000000000..8b62ccfe094 --- /dev/null +++ b/storage/maria/unittest/lockman-t.c @@ -0,0 +1,246 @@ +/* Copyright (C) 2006 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; either version 2 of the License, or + (at your option) any later version. + + 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 */ + +//#define EXTRA_VERBOSE + +#include <tap.h> + +#include <my_global.h> +#include <my_sys.h> +#include <my_atomic.h> +#include <lf.h> +#include "../lockman.h" + +#define Nlos 10 +LOCK_OWNER loarray[Nlos]; +pthread_mutex_t mutexes[Nlos]; +pthread_cond_t conds[Nlos]; +LOCKMAN lockman; + +#ifndef EXTRA_VERBOSE +#define print_lockhash(X) /* no-op */ +#define DIAG(X) /* no-op */ +#else +#define DIAG(X) diag X +#endif + +LOCK_OWNER *loid2lo(uint16 loid) +{ + return loarray+loid-1; +} + +#define unlock_all(O) diag("lo" #O "> release all locks"); \ + lockman_release_locks(&lockman, loid2lo(O));print_lockhash(&lockman) +#define test_lock(O, R, L, S, RES) \ + ok(lockman_getlock(&lockman, loid2lo(O), R, L) == RES, \ + "lo" #O "> " S " lock resource " #R " with " #L "-lock"); \ + print_lockhash(&lockman) +#define lock_ok_a(O,R,L) test_lock(O,R,L,"",GOT_THE_LOCK) +#define lock_ok_i(O,R,L) test_lock(O,R,L,"",GOT_THE_LOCK_NEED_TO_LOCK_A_SUBRESOURCE) +#define lock_ok_l(O,R,L) test_lock(O,R,L,"",GOT_THE_LOCK_NEED_TO_INSTANT_LOCK_A_SUBRESOURCE) +#define lock_conflict(O,R,L) test_lock(O,R,L,"cannot ",DIDNT_GET_THE_LOCK); \ + unlock_all(O) + +void test_lockman_simple() +{ + /* simple */ + lock_ok_a(1, 1, S); + lock_ok_i(2, 2, IS); + lock_ok_i(1, 2, IX); + /* lock escalation */ + lock_ok_a(1, 1, X); + lock_ok_i(2, 2, IX); + /* failures */ + lock_conflict(2,1,X); /* this removes all locks of lo2 */ + lock_ok_a(1,2,S); + lock_ok_a(1,2,IS); + lock_ok_a(1,2,LS); + lock_ok_i(1,3,IX); + lock_ok_a(2,3,LS); + lock_ok_i(1,3,IX); + lock_ok_l(2,3,IS); + lockman_release_locks(&lockman, loid2lo(1)); + lockman_release_locks(&lockman, loid2lo(2)); + +} + +pthread_attr_t rt_attr; +pthread_mutex_t rt_mutex; +pthread_cond_t rt_cond; +int rt_num_threads; +int litmus; +void run_test(const char *test, pthread_handler handler, int n, int m) +{ + pthread_t t; + ulonglong now= my_getsystime(); + + litmus= 0; + + diag("Testing %s with %d threads, %d iterations... ", test, n, m); + for (rt_num_threads= n ; n ; n--) + pthread_create(&t, &rt_attr, handler, &m); + pthread_mutex_lock(&rt_mutex); + while (rt_num_threads) + pthread_cond_wait(&rt_cond, &rt_mutex); + pthread_mutex_unlock(&rt_mutex); + now= my_getsystime()-now; + ok(litmus == 0, "tested %s in %g secs (%d)", test, ((double)now)/1e7, litmus); +} + +int thread_number= 0, timeouts=0; +#define Nrows 1000 +#define Ntables 10 +#define TABLE_LOCK_RATIO 10 +enum lock_type lock_array[6]={S,X,LS,LX,IS,IX}; +char *lock2str[6]={"S","X","LS","LX","IS","IX"}; +char *res2str[6]={ + "DIDN'T GET THE LOCK", + "GOT THE LOCK", + "GOT THE LOCK NEED TO LOCK A SUBRESOURCE", + "GOT THE LOCK NEED TO INSTANT LOCK A SUBRESOURCE"}; +pthread_handler_t test_lockman(void *arg) +{ + int m= (*(int *)arg); + uint x, loid, row, table, res, locklevel, timeout= 0; + LOCK_OWNER *lo; + + pthread_mutex_lock(&rt_mutex); + loid= ++thread_number; + pthread_mutex_unlock(&rt_mutex); + lo= loid2lo(loid); + + for (x= ((int)(intptr)(&m)); m > 0; m--) + { + x= (x*3628273133 + 1500450271) % 9576890767; /* three prime numbers */ + row= x % Nrows + Ntables; + table= row % Ntables; + locklevel= (x/Nrows) & 3; + if ((x/Nrows/4) % TABLE_LOCK_RATIO == 0) + { /* table lock */ + res= lockman_getlock(&lockman, lo, table, lock_array[locklevel]); + DIAG(("loid=%2d, table %d lock %s, res=%s", loid, table, lock2str[locklevel], res2str[res])); + if (res == DIDNT_GET_THE_LOCK) + { + lockman_release_locks(&lockman, lo); + DIAG(("loid=%2d, release all locks", loid)); + timeout++; + continue; + } + DBUG_ASSERT(res == GOT_THE_LOCK); + } + else + { /* row lock */ + locklevel&= 1; + res= lockman_getlock(&lockman, lo, table, lock_array[locklevel + 4]); + DIAG(("loid=%2d, row %d lock %s, res=%s", loid, row, lock2str[locklevel+4], res2str[res])); + switch (res) + { + case DIDNT_GET_THE_LOCK: + lockman_release_locks(&lockman, lo); + DIAG(("loid=%2d, release all locks", loid)); + timeout++; + continue; + case GOT_THE_LOCK: + continue; + case GOT_THE_LOCK_NEED_TO_INSTANT_LOCK_A_SUBRESOURCE: + /* not implemented, so take a regular lock */ + case GOT_THE_LOCK_NEED_TO_LOCK_A_SUBRESOURCE: + res= lockman_getlock(&lockman, lo, row, lock_array[locklevel]); + DIAG(("loid=%2d, ROW %d lock %s, res=%s", loid, row, lock2str[locklevel], res2str[res])); + if (res == DIDNT_GET_THE_LOCK) + { + lockman_release_locks(&lockman, lo); + DIAG(("loid=%2d, release all locks", loid)); + timeout++; + continue; + } + DBUG_ASSERT(res == GOT_THE_LOCK); + continue; + default: + DBUG_ASSERT(0); + } + } + } + + lockman_release_locks(&lockman, lo); + + pthread_mutex_lock(&rt_mutex); + rt_num_threads--; + timeouts+= timeout; + if (!rt_num_threads) + { + pthread_cond_signal(&rt_cond); + diag("number of timeouts: %d", timeouts); + } + pthread_mutex_unlock(&rt_mutex); + + return 0; +} + +int main() +{ + int i; + + my_init(); + + plan(14); + + if (my_atomic_initialize()) + return exit_status(); + + pthread_attr_init(&rt_attr); + pthread_attr_setdetachstate(&rt_attr,PTHREAD_CREATE_DETACHED); + pthread_mutex_init(&rt_mutex, 0); + pthread_cond_init(&rt_cond, 0); + + lockman_init(&lockman, &loid2lo, 50); + + for (i= 0; i < Nlos; i++) + { + loarray[i].pins= lf_alloc_get_pins(&lockman.alloc); + loarray[i].all_locks= 0; + loarray[i].waiting_for= 0; + pthread_mutex_init(&mutexes[i], MY_MUTEX_INIT_FAST); + pthread_cond_init (&conds[i], 0); + loarray[i].mutex= &mutexes[i]; + loarray[i].cond= &conds[i]; + loarray[i].loid= i+1; + } + + test_lockman_simple(); + +#define CYCLES 100 +#define THREADS Nlos /* don't change this line */ + + run_test("lockman", test_lockman, THREADS,CYCLES); + + for (i= 0; i < Nlos; i++) + { + lockman_release_locks(&lockman, &loarray[i]); + pthread_mutex_destroy(loarray[i].mutex); + pthread_cond_destroy(loarray[i].cond); + lf_pinbox_put_pins(loarray[i].pins); + } + + lockman_destroy(&lockman); + + pthread_mutex_destroy(&rt_mutex); + pthread_cond_destroy(&rt_cond); + pthread_attr_destroy(&rt_attr); + my_end(0); + return exit_status(); +} + diff --git a/storage/maria/unittest/trnman-t.c b/storage/maria/unittest/trnman-t.c new file mode 100644 index 00000000000..6d4b48c6d3d --- /dev/null +++ b/storage/maria/unittest/trnman-t.c @@ -0,0 +1,177 @@ +/* Copyright (C) 2006 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; either version 2 of the License, or + (at your option) any later version. + + 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 <tap.h> + +#include <my_global.h> +#include <my_sys.h> +#include <my_atomic.h> +#include <lf.h> +#include "../trnman.h" + +pthread_attr_t rt_attr; +pthread_mutex_t rt_mutex; +pthread_cond_t rt_cond; +int rt_num_threads; + +int litmus; + +/* + create and end (commit or rollback) transactions randomly +*/ +#define MAX_ITER 100 +pthread_handler_t test_trnman(void *arg) +{ + int m= (*(int *)arg); + uint x, y, i, j, n; + TRN *trn[MAX_ITER]; + pthread_mutex_t mutexes[MAX_ITER]; + pthread_cond_t conds[MAX_ITER]; + + for (i=0; i < MAX_ITER; i++) + { + pthread_mutex_init(&mutexes[i], MY_MUTEX_INIT_FAST); + pthread_cond_init(&conds[i], 0); + } + + for (x= ((int)(intptr)(&m)); m > 0; ) + { + y= x= (x*3628273133 + 1500450271) % 9576890767; /* three prime numbers */ + m-= n= x % MAX_ITER; + for (i= 0; i < n; i++) + trn[i]= trnman_new_trn(&mutexes[i], &conds[i]); + for (i= 0; i < n; i++) + { + y= (y*19 + 7) % 31; + trnman_end_trn(trn[i], y & 1); + } + } + + for (i=0; i < MAX_ITER; i++) + { + pthread_mutex_destroy(&mutexes[i]); + pthread_cond_destroy(&conds[i]); + } + pthread_mutex_lock(&rt_mutex); + rt_num_threads--; + if (!rt_num_threads) + pthread_cond_signal(&rt_cond); + pthread_mutex_unlock(&rt_mutex); + + return 0; +} +#undef MAX_ITER + +void run_test(const char *test, pthread_handler handler, int n, int m) +{ + pthread_t t; + ulonglong now= my_getsystime(); + + litmus= 0; + + diag("Testing %s with %d threads, %d iterations... ", test, n, m); + for (rt_num_threads= n ; n ; n--) + pthread_create(&t, &rt_attr, handler, &m); + pthread_mutex_lock(&rt_mutex); + while (rt_num_threads) + pthread_cond_wait(&rt_cond, &rt_mutex); + pthread_mutex_unlock(&rt_mutex); + now= my_getsystime()-now; + ok(litmus == 0, "tested %s in %g secs (%d)", test, ((double)now)/1e7, litmus); +} + +#define ok_read_from(T1, T2, RES) \ + i=trnman_can_read_from(trn[T1], trid[T2]); \ + ok(i == RES, "trn" #T1 " %s read from trn" #T2, i ? "can" : "cannot") +#define start_transaction(T) \ + trn[T]= trnman_new_trn(&mutexes[T], &conds[T]); \ + trid[T]= trn[T]->trid +#define commit(T) trnman_commit_trn(trn[T]) +#define abort(T) trnman_abort_trn(trn[T]) + +#define Ntrns 4 +void test_trnman_read_from() +{ + TRN *trn[Ntrns]; + TrID trid[Ntrns]; + pthread_mutex_t mutexes[Ntrns]; + pthread_cond_t conds[Ntrns]; + int i; + + for (i=0; i < Ntrns; i++) + { + pthread_mutex_init(&mutexes[i], MY_MUTEX_INIT_FAST); + pthread_cond_init(&conds[i], 0); + } + + start_transaction(0); /* start trn1 */ + start_transaction(1); /* start trn2 */ + ok_read_from(1,0,0); + commit(0); /* commit trn1 */ + start_transaction(2); /* start trn4 */ + abort(2); /* abort trn4 */ + start_transaction(3); /* start trn5 */ + ok_read_from(3,0,1); + ok_read_from(3,1,0); + ok_read_from(3,2,0); + commit(1); /* commit trn2 */ + ok_read_from(3,1,0); + commit(3); /* commit trn5 */ + + for (i=0; i < Ntrns; i++) + { + pthread_mutex_destroy(&mutexes[i]); + pthread_cond_destroy(&conds[i]); + } +} + +int main() +{ + my_init(); + + plan(6); + + if (my_atomic_initialize()) + return exit_status(); + + pthread_attr_init(&rt_attr); + pthread_attr_setdetachstate(&rt_attr,PTHREAD_CREATE_DETACHED); + pthread_mutex_init(&rt_mutex, 0); + pthread_cond_init(&rt_cond, 0); + +#define CYCLES 10000 +#define THREADS 10 + + trnman_init(); + + test_trnman_read_from(); + run_test("trnman", test_trnman, THREADS,CYCLES); + + diag("mallocs: %d", trnman_allocated_transactions); + { + ulonglong now= my_getsystime(); + trnman_destroy(); + now= my_getsystime()-now; + diag("trnman_destroy: %g", ((double)now)/1e7); + } + + pthread_mutex_destroy(&rt_mutex); + pthread_cond_destroy(&rt_cond); + pthread_attr_destroy(&rt_attr); + my_end(0); + return exit_status(); +} + |