mirror of
				https://github.com/MariaDB/server.git
				synced 2025-10-31 10:56:12 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			356 lines
		
	
	
	
		
			9.1 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			356 lines
		
	
	
	
		
			9.1 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /* 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; 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 */
 | |
| 
 | |
| /*
 | |
|   tablockman for row and table locks
 | |
| */
 | |
| 
 | |
| /* #define EXTRA_VERBOSE */
 | |
| 
 | |
| #include <tap.h>
 | |
| 
 | |
| #include <my_global.h>
 | |
| #include <my_sys.h>
 | |
| #include <lf.h>
 | |
| #include "../tablockman.h"
 | |
| 
 | |
| #define Nlos 100
 | |
| #define Ntbls 110
 | |
| TABLE_LOCK_OWNER loarray1[Nlos];
 | |
| pthread_mutex_t mutexes[Nlos];
 | |
| pthread_cond_t conds[Nlos];
 | |
| LOCKED_TABLE ltarray[Ntbls];
 | |
| TABLOCKMAN tablockman;
 | |
| 
 | |
| #ifndef EXTRA_VERBOSE
 | |
| #define print_lo1(X)       /* no-op */
 | |
| #define DIAG(X)            /* no-op */
 | |
| #else
 | |
| #define DIAG(X) diag X
 | |
| #endif
 | |
| 
 | |
| TABLE_LOCK_OWNER *loid2lo1(uint16 loid)
 | |
| {
 | |
|   return loarray1+loid-1;
 | |
| }
 | |
| 
 | |
| #define unlock_all(O) diag("lo" #O "> release all locks");              \
 | |
|   tablockman_release_locks(&tablockman, loid2lo1(O));
 | |
| #define test_lock(O, R, L, S, RES)                                      \
 | |
|   ok(tablockman_getlock(&tablockman, loid2lo1(O), <array[R], L) == RES,   \
 | |
|      "lo" #O "> " S "lock resource " #R " with " #L "-lock");           \
 | |
|   print_lo1(loid2lo1(O));
 | |
| #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 ", LOCK_TIMEOUT);
 | |
| 
 | |
| void test_tablockman_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);
 | |
|   unlock_all(2);
 | |
|   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);
 | |
|   unlock_all(1);
 | |
|   unlock_all(2);
 | |
| 
 | |
|   lock_ok_i(1, 1, IX);
 | |
|   lock_conflict(2, 1, S);
 | |
|   lock_ok_a(1, 1, LS);
 | |
|   unlock_all(1);
 | |
|   unlock_all(2);
 | |
| 
 | |
|   lock_ok_i(1, 1, IX);
 | |
|   lock_ok_a(2, 1, LS);
 | |
|   lock_ok_a(1, 1, LS);
 | |
|   lock_ok_i(1, 1, IX);
 | |
|   lock_ok_i(3, 1, IS);
 | |
|   unlock_all(1);
 | |
|   unlock_all(2);
 | |
|   unlock_all(3);
 | |
| 
 | |
|   lock_ok_i(1, 4, IS);
 | |
|   lock_ok_i(2, 4, IS);
 | |
|   lock_ok_i(3, 4, IS);
 | |
|   lock_ok_a(3, 4, LS);
 | |
|   lock_ok_i(4, 4, IS);
 | |
|   lock_conflict(4, 4, IX);
 | |
|   lock_conflict(2, 4, IX);
 | |
|   lock_ok_a(1, 4, LS);
 | |
|   unlock_all(1);
 | |
|   unlock_all(2);
 | |
|   unlock_all(3);
 | |
|   unlock_all(4);
 | |
| 
 | |
|   lock_ok_i(1, 1, IX);
 | |
|   lock_ok_i(2, 1, IX);
 | |
|   lock_conflict(1, 1, S);
 | |
|   lock_conflict(2, 1, X);
 | |
|   unlock_all(1);
 | |
|   unlock_all(2);
 | |
| 
 | |
|   lock_ok_i(1, 1, IS);
 | |
|   lock_conflict(2, 1, X);
 | |
|   lock_conflict(3, 1, IS);
 | |
|   unlock_all(1);
 | |
|   unlock_all(2);
 | |
|   unlock_all(3);
 | |
| 
 | |
|   lock_ok_a(1, 1, S);
 | |
|   lock_conflict(2, 1, IX);
 | |
|   lock_conflict(3, 1, IS);
 | |
|   unlock_all(1);
 | |
|   unlock_all(2);
 | |
|   unlock_all(3);
 | |
| }
 | |
| 
 | |
| int rt_num_threads;
 | |
| int litmus;
 | |
| int thread_number= 0, timeouts= 0;
 | |
| void run_test(const char *test, pthread_handler handler, int n, int m)
 | |
| {
 | |
|   pthread_t *threads;
 | |
|   ulonglong now= my_getsystime();
 | |
|   int i;
 | |
| 
 | |
|   thread_number= timeouts= 0;
 | |
|   litmus= 0;
 | |
| 
 | |
|   threads= (pthread_t *)my_malloc(sizeof(void *)*n, MYF(0));
 | |
|   if (!threads)
 | |
|   {
 | |
|     diag("Out of memory");
 | |
|     abort();
 | |
|   }
 | |
| 
 | |
|   diag("Running %s with %d threads, %d iterations... ", test, n, m);
 | |
|   rt_num_threads= n;
 | |
|   for (i= 0; i < n ; i++)
 | |
|     if (pthread_create(threads+i, 0, handler, &m))
 | |
|     {
 | |
|       diag("Could not create thread");
 | |
|       abort();
 | |
|     }
 | |
|   for (i= 0 ; i < n ; i++)
 | |
|     pthread_join(threads[i], 0);
 | |
|   now= my_getsystime()-now;
 | |
|   ok(litmus == 0, "Finished %s in %g secs (%d)", test, ((double)now)/1e7, litmus);
 | |
|   my_free((void*)threads, MYF(0));
 | |
| }
 | |
| 
 | |
| static void reinit_tlo(TABLOCKMAN *lm, TABLE_LOCK_OWNER *lo)
 | |
| {
 | |
| #ifdef NOT_USED_YET
 | |
|   TABLE_LOCK_OWNER backup= *lo;
 | |
| #endif
 | |
| 
 | |
|   tablockman_release_locks(lm, lo);
 | |
| #ifdef NOT_USED_YET
 | |
|   pthread_mutex_destroy(lo->mutex);
 | |
|   pthread_cond_destroy(lo->cond);
 | |
|   bzero(lo, sizeof(*lo));
 | |
| 
 | |
|   lo->mutex= backup.mutex;
 | |
|   lo->cond= backup.cond;
 | |
|   lo->loid= backup.loid;
 | |
|   pthread_mutex_init(lo->mutex, MY_MUTEX_INIT_FAST);
 | |
|   pthread_cond_init(lo->cond, 0);
 | |
| #endif
 | |
| }
 | |
| 
 | |
| pthread_mutex_t rt_mutex;
 | |
| int Nrows= 100;
 | |
| int Ntables= 10;
 | |
| int table_lock_ratio= 10;
 | |
| enum lockman_lock_type lock_array[6]= {S, X, LS, LX, IS, IX};
 | |
| const char *lock2str[6]= {"S", "X", "LS", "LX", "IS", "IX"};
 | |
| const char *res2str[]= {
 | |
|   0,
 | |
|   "OUT OF MEMORY",
 | |
|   "DEADLOCK",
 | |
|   "LOCK TIMEOUT",
 | |
|   "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;
 | |
|   TABLE_LOCK_OWNER *lo1;
 | |
|   DBUG_ASSERT(Ntables <= Ntbls);
 | |
|   DBUG_ASSERT(Nrows + Ntables <= Ntbls);
 | |
| 
 | |
|   pthread_mutex_lock(&rt_mutex);
 | |
|   loid= ++thread_number;
 | |
|   pthread_mutex_unlock(&rt_mutex);
 | |
|   lo1= loid2lo1(loid);
 | |
| 
 | |
|   for (x= ((int)(intptr)(&m)); m > 0; m--)
 | |
|   {
 | |
|     /* three prime numbers */
 | |
|     x= (uint) ((x*LL(3628273133) + LL(1500450271)) % LL(9576890767));
 | |
|     row=  x % Nrows + Ntables;
 | |
|     table= row % Ntables;
 | |
|     locklevel= (x/Nrows) & 3;
 | |
|     if (table_lock_ratio && (x/Nrows/4) % table_lock_ratio == 0)
 | |
|     {
 | |
|       /* table lock */
 | |
|       res= tablockman_getlock(&tablockman, lo1, ltarray+table,
 | |
|                               lock_array[locklevel]);
 | |
|       DIAG(("loid %2d, table %d, lock %s, res %s", loid, table,
 | |
|             lock2str[locklevel], res2str[res]));
 | |
|       if (res < GOT_THE_LOCK)
 | |
|       {
 | |
|         reinit_tlo(&tablockman, lo1);
 | |
|         DIAG(("loid %2d, release all locks", loid));
 | |
|         timeout++;
 | |
|         continue;
 | |
|       }
 | |
|       DBUG_ASSERT(res == GOT_THE_LOCK);
 | |
|     }
 | |
|     else
 | |
|     { /* row lock */
 | |
|       locklevel&= 1;
 | |
|       res= tablockman_getlock(&tablockman, lo1, ltarray+table, lock_array[locklevel + 4]);
 | |
|       DIAG(("loid %2d, row %d, lock %s, res %s", loid, row,
 | |
|             lock2str[locklevel+4], res2str[res]));
 | |
|       switch (res)
 | |
|       {
 | |
|       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= tablockman_getlock(&tablockman, lo1, ltarray+row, lock_array[locklevel]);
 | |
|         DIAG(("loid %2d, ROW %d, lock %s, res %s", loid, row,
 | |
|               lock2str[locklevel], res2str[res]));
 | |
|         if (res < GOT_THE_LOCK)
 | |
|         {
 | |
|           reinit_tlo(&tablockman, lo1);
 | |
|           DIAG(("loid %2d, release all locks", loid));
 | |
|           timeout++;
 | |
|           continue;
 | |
|         }
 | |
|         DBUG_ASSERT(res == GOT_THE_LOCK);
 | |
|         continue;
 | |
|       default:
 | |
|         reinit_tlo(&tablockman, lo1);
 | |
|         DIAG(("loid %2d, release all locks", loid));
 | |
|         timeout++;
 | |
|         continue;
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   reinit_tlo(&tablockman, lo1);
 | |
| 
 | |
|   pthread_mutex_lock(&rt_mutex);
 | |
|   rt_num_threads--;
 | |
|   timeouts+= timeout;
 | |
|   if (!rt_num_threads)
 | |
|     diag("number of timeouts: %d", timeouts);
 | |
|   pthread_mutex_unlock(&rt_mutex);
 | |
| 
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| int main(int argc __attribute__((unused)), char **argv)
 | |
| {
 | |
|   int i;
 | |
|   MY_INIT(argv[0]);
 | |
| 
 | |
|   my_init();
 | |
|   pthread_mutex_init(&rt_mutex, 0);
 | |
| 
 | |
|   plan(40);
 | |
| 
 | |
|   tablockman_init(&tablockman, &loid2lo1, 50);
 | |
| 
 | |
|   for (i= 0; i < Nlos; i++)
 | |
|   {
 | |
|     pthread_mutex_init(&mutexes[i], MY_MUTEX_INIT_FAST);
 | |
|     pthread_cond_init (&conds[i], 0);
 | |
| 
 | |
|     loarray1[i].active_locks= 0;
 | |
|     loarray1[i].waiting_lock= 0;
 | |
|     loarray1[i].waiting_for= 0;
 | |
|     loarray1[i].mutex= &mutexes[i];
 | |
|     loarray1[i].cond= &conds[i];
 | |
|     loarray1[i].loid= i+1;
 | |
|   }
 | |
| 
 | |
|   for (i= 0; i < Ntbls; i++)
 | |
|   {
 | |
|     tablockman_init_locked_table(ltarray+i, Nlos);
 | |
|   }
 | |
| 
 | |
|   test_tablockman_simple();
 | |
| 
 | |
| #define CYCLES 10000
 | |
| #define THREADS Nlos /* don't change this line */
 | |
| 
 | |
|   /* mixed load, stress-test with random locks */
 | |
|   Nrows= 100;
 | |
|   Ntables= 10;
 | |
|   table_lock_ratio= 10;
 | |
|   run_test("\"random lock\" stress test", test_lockman, THREADS, CYCLES);
 | |
| #if 0
 | |
|   /* "real-life" simulation - many rows, no table locks */
 | |
|   Nrows= 1000000;
 | |
|   Ntables= 10;
 | |
|   table_lock_ratio= 0;
 | |
|   run_test("\"real-life\" simulation test", test_lockman, THREADS, CYCLES*10);
 | |
| #endif
 | |
|   for (i= 0; i < Nlos; i++)
 | |
|   {
 | |
|     tablockman_release_locks(&tablockman, &loarray1[i]);
 | |
|     pthread_mutex_destroy(loarray1[i].mutex);
 | |
|     pthread_cond_destroy(loarray1[i].cond);
 | |
|   }
 | |
| 
 | |
|   {
 | |
|     ulonglong now= my_getsystime();
 | |
|     for (i= 0; i < Ntbls; i++)
 | |
|     {
 | |
|       tablockman_destroy_locked_table(ltarray+i);
 | |
|     }
 | |
|     tablockman_destroy(&tablockman);
 | |
|     now= my_getsystime()-now;
 | |
|     diag("lockman_destroy: %g secs", ((double)now)/1e7);
 | |
|   }
 | |
| 
 | |
|   pthread_mutex_destroy(&rt_mutex);
 | |
|   my_end(0);
 | |
|   return exit_status();
 | |
| }
 | |
| 
 | 
