1 /* Copyright (C) 2006 MySQL AB
2 
3    This program is free software; you can redistribute it and/or modify
4    it under the terms of the GNU General Public License as published by
5    the Free Software Foundation; version 2 of the License.
6 
7    This program is distributed in the hope that it will be useful,
8    but WITHOUT ANY WARRANTY; without even the implied warranty of
9    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10    GNU General Public License for more details.
11 
12    You should have received a copy of the GNU General Public License
13    along with this program; if not, write to the Free Software
14    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA */
15 
16 /*
17   tablockman for row and table locks
18 */
19 
20 /* #define EXTRA_VERBOSE */
21 
22 #include <tap.h>
23 
24 #include <my_global.h>
25 #include <my_sys.h>
26 #include <my_atomic.h>
27 #include <lf.h>
28 #include "../tablockman.h"
29 
30 #define Nlos 100
31 #define Ntbls 110
32 TABLE_LOCK_OWNER loarray1[Nlos];
33 pthread_mutex_t mutexes[Nlos];
34 pthread_cond_t conds[Nlos];
35 LOCKED_TABLE ltarray[Ntbls];
36 TABLOCKMAN tablockman;
37 
38 #ifndef EXTRA_VERBOSE
39 #define print_lo1(X)       /* no-op */
40 #define DIAG(X)            /* no-op */
41 #else
42 #define DIAG(X) diag X
43 #endif
44 
loid2lo1(uint16 loid)45 TABLE_LOCK_OWNER *loid2lo1(uint16 loid)
46 {
47   return loarray1+loid-1;
48 }
49 
50 #define unlock_all(O) diag("lo" #O "> release all locks");              \
51   tablockman_release_locks(&tablockman, loid2lo1(O));
52 #define test_lock(O, R, L, S, RES)                                      \
53   ok(tablockman_getlock(&tablockman, loid2lo1(O), &ltarray[R], L) == RES,   \
54      "lo" #O "> " S "lock resource " #R " with " #L "-lock");           \
55   print_lo1(loid2lo1(O));
56 #define lock_ok_a(O, R, L)                                              \
57   test_lock(O, R, L, "", GOT_THE_LOCK)
58 #define lock_ok_i(O, R, L)                                              \
59   test_lock(O, R, L, "", GOT_THE_LOCK_NEED_TO_LOCK_A_SUBRESOURCE)
60 #define lock_ok_l(O, R, L)                                              \
61   test_lock(O, R, L, "", GOT_THE_LOCK_NEED_TO_INSTANT_LOCK_A_SUBRESOURCE)
62 #define lock_conflict(O, R, L)                                          \
63   test_lock(O, R, L, "cannot ", LOCK_TIMEOUT);
64 
test_tablockman_simple()65 void test_tablockman_simple()
66 {
67   /* simple */
68   lock_ok_a(1, 1, S);
69   lock_ok_i(2, 2, IS);
70   lock_ok_i(1, 2, IX);
71   /* lock escalation */
72   lock_ok_a(1, 1, X);
73   lock_ok_i(2, 2, IX);
74   /* failures */
75   lock_conflict(2, 1, X);
76   unlock_all(2);
77   lock_ok_a(1, 2, S);
78   lock_ok_a(1, 2, IS);
79   lock_ok_a(1, 2, LS);
80   lock_ok_i(1, 3, IX);
81   lock_ok_a(2, 3, LS);
82   lock_ok_i(1, 3, IX);
83   lock_ok_l(2, 3, IS);
84   unlock_all(1);
85   unlock_all(2);
86 
87   lock_ok_i(1, 1, IX);
88   lock_conflict(2, 1, S);
89   lock_ok_a(1, 1, LS);
90   unlock_all(1);
91   unlock_all(2);
92 
93   lock_ok_i(1, 1, IX);
94   lock_ok_a(2, 1, LS);
95   lock_ok_a(1, 1, LS);
96   lock_ok_i(1, 1, IX);
97   lock_ok_i(3, 1, IS);
98   unlock_all(1);
99   unlock_all(2);
100   unlock_all(3);
101 
102   lock_ok_i(1, 4, IS);
103   lock_ok_i(2, 4, IS);
104   lock_ok_i(3, 4, IS);
105   lock_ok_a(3, 4, LS);
106   lock_ok_i(4, 4, IS);
107   lock_conflict(4, 4, IX);
108   lock_conflict(2, 4, IX);
109   lock_ok_a(1, 4, LS);
110   unlock_all(1);
111   unlock_all(2);
112   unlock_all(3);
113   unlock_all(4);
114 
115   lock_ok_i(1, 1, IX);
116   lock_ok_i(2, 1, IX);
117   lock_conflict(1, 1, S);
118   lock_conflict(2, 1, X);
119   unlock_all(1);
120   unlock_all(2);
121 
122   lock_ok_i(1, 1, IS);
123   lock_conflict(2, 1, X);
124   lock_conflict(3, 1, IS);
125   unlock_all(1);
126   unlock_all(2);
127   unlock_all(3);
128 
129   lock_ok_a(1, 1, S);
130   lock_conflict(2, 1, IX);
131   lock_conflict(3, 1, IS);
132   unlock_all(1);
133   unlock_all(2);
134   unlock_all(3);
135 }
136 
137 int rt_num_threads;
138 int litmus;
139 int thread_number= 0, timeouts= 0;
run_test(const char * test,pthread_handler handler,int n,int m)140 void run_test(const char *test, pthread_handler handler, int n, int m)
141 {
142   pthread_t *threads;
143   ulonglong now= my_getsystime();
144   int i;
145 
146   thread_number= timeouts= 0;
147   litmus= 0;
148 
149   threads= (pthread_t *)my_malloc(sizeof(void *)*n, MYF(0));
150   if (!threads)
151   {
152     diag("Out of memory");
153     abort();
154   }
155 
156   diag("Running %s with %d threads, %d iterations... ", test, n, m);
157   rt_num_threads= n;
158   for (i= 0; i < n ; i++)
159     if (pthread_create(threads+i, 0, handler, &m))
160     {
161       diag("Could not create thread");
162       abort();
163     }
164   for (i= 0 ; i < n ; i++)
165     pthread_join(threads[i], 0);
166   now= my_getsystime()-now;
167   ok(litmus == 0, "Finished %s in %g secs (%d)", test, ((double)now)/1e7, litmus);
168   my_free((void*)threads, MYF(0));
169 }
170 
reinit_tlo(TABLOCKMAN * lm,TABLE_LOCK_OWNER * lo)171 static void reinit_tlo(TABLOCKMAN *lm, TABLE_LOCK_OWNER *lo)
172 {
173 #ifdef NOT_USED_YET
174   TABLE_LOCK_OWNER backup= *lo;
175 #endif
176 
177   tablockman_release_locks(lm, lo);
178 #ifdef NOT_USED_YET
179   pthread_mutex_destroy(lo->mutex);
180   pthread_cond_destroy(lo->cond);
181   bzero(lo, sizeof(*lo));
182 
183   lo->mutex= backup.mutex;
184   lo->cond= backup.cond;
185   lo->loid= backup.loid;
186   pthread_mutex_init(lo->mutex, MY_MUTEX_INIT_FAST);
187   pthread_cond_init(lo->cond, 0);
188 #endif
189 }
190 
191 pthread_mutex_t rt_mutex;
192 int Nrows= 100;
193 int Ntables= 10;
194 int table_lock_ratio= 10;
195 enum lockman_lock_type lock_array[6]= {S, X, LS, LX, IS, IX};
196 const char *lock2str[6]= {"S", "X", "LS", "LX", "IS", "IX"};
197 const char *res2str[]= {
198   0,
199   "OUT OF MEMORY",
200   "DEADLOCK",
201   "LOCK TIMEOUT",
202   "GOT THE LOCK",
203   "GOT THE LOCK NEED TO LOCK A SUBRESOURCE",
204   "GOT THE LOCK NEED TO INSTANT LOCK A SUBRESOURCE"};
205 
test_lockman(void * arg)206 pthread_handler_t test_lockman(void *arg)
207 {
208   int    m= (*(int *)arg);
209   uint   x, loid, row, table, res, locklevel, timeout= 0;
210   TABLE_LOCK_OWNER *lo1;
211   DBUG_ASSERT(Ntables <= Ntbls);
212   DBUG_ASSERT(Nrows + Ntables <= Ntbls);
213 
214   pthread_mutex_lock(&rt_mutex);
215   loid= ++thread_number;
216   pthread_mutex_unlock(&rt_mutex);
217   lo1= loid2lo1(loid);
218 
219   for (x= ((int)(intptr)(&m)); m > 0; m--)
220   {
221     /* three prime numbers */
222     x= (uint) ((x*LL(3628273133) + LL(1500450271)) % LL(9576890767));
223     row=  x % Nrows + Ntables;
224     table= row % Ntables;
225     locklevel= (x/Nrows) & 3;
226     if (table_lock_ratio && (x/Nrows/4) % table_lock_ratio == 0)
227     {
228       /* table lock */
229       res= tablockman_getlock(&tablockman, lo1, ltarray+table,
230                               lock_array[locklevel]);
231       DIAG(("loid %2d, table %d, lock %s, res %s", loid, table,
232             lock2str[locklevel], res2str[res]));
233       if (res < GOT_THE_LOCK)
234       {
235         reinit_tlo(&tablockman, lo1);
236         DIAG(("loid %2d, release all locks", loid));
237         timeout++;
238         continue;
239       }
240       DBUG_ASSERT(res == GOT_THE_LOCK);
241     }
242     else
243     { /* row lock */
244       locklevel&= 1;
245       res= tablockman_getlock(&tablockman, lo1, ltarray+table, lock_array[locklevel + 4]);
246       DIAG(("loid %2d, row %d, lock %s, res %s", loid, row,
247             lock2str[locklevel+4], res2str[res]));
248       switch (res)
249       {
250       case GOT_THE_LOCK:
251         continue;
252       case GOT_THE_LOCK_NEED_TO_INSTANT_LOCK_A_SUBRESOURCE:
253         /* not implemented, so take a regular lock */
254       case GOT_THE_LOCK_NEED_TO_LOCK_A_SUBRESOURCE:
255         res= tablockman_getlock(&tablockman, lo1, ltarray+row, lock_array[locklevel]);
256         DIAG(("loid %2d, ROW %d, lock %s, res %s", loid, row,
257               lock2str[locklevel], res2str[res]));
258         if (res < GOT_THE_LOCK)
259         {
260           reinit_tlo(&tablockman, lo1);
261           DIAG(("loid %2d, release all locks", loid));
262           timeout++;
263           continue;
264         }
265         DBUG_ASSERT(res == GOT_THE_LOCK);
266         continue;
267       default:
268         reinit_tlo(&tablockman, lo1);
269         DIAG(("loid %2d, release all locks", loid));
270         timeout++;
271         continue;
272       }
273     }
274   }
275 
276   reinit_tlo(&tablockman, lo1);
277 
278   pthread_mutex_lock(&rt_mutex);
279   rt_num_threads--;
280   timeouts+= timeout;
281   if (!rt_num_threads)
282     diag("number of timeouts: %d", timeouts);
283   pthread_mutex_unlock(&rt_mutex);
284 
285   return 0;
286 }
287 
main(int argc,char ** argv)288 int main(int argc __attribute__((unused)), char **argv)
289 {
290   int i;
291   MY_INIT(argv[0]);
292 
293   my_init();
294   pthread_mutex_init(&rt_mutex, 0);
295 
296   plan(40);
297 
298   tablockman_init(&tablockman, &loid2lo1, 50);
299 
300   for (i= 0; i < Nlos; i++)
301   {
302     pthread_mutex_init(&mutexes[i], MY_MUTEX_INIT_FAST);
303     pthread_cond_init (&conds[i], 0);
304 
305     loarray1[i].active_locks= 0;
306     loarray1[i].waiting_lock= 0;
307     loarray1[i].waiting_for= 0;
308     loarray1[i].mutex= &mutexes[i];
309     loarray1[i].cond= &conds[i];
310     loarray1[i].loid= i+1;
311   }
312 
313   for (i= 0; i < Ntbls; i++)
314   {
315     tablockman_init_locked_table(ltarray+i, Nlos);
316   }
317 
318   test_tablockman_simple();
319 
320 #define CYCLES 10000
321 #define THREADS Nlos /* don't change this line */
322 
323   /* mixed load, stress-test with random locks */
324   Nrows= 100;
325   Ntables= 10;
326   table_lock_ratio= 10;
327   run_test("\"random lock\" stress test", test_lockman, THREADS, CYCLES);
328 #if 0
329   /* "real-life" simulation - many rows, no table locks */
330   Nrows= 1000000;
331   Ntables= 10;
332   table_lock_ratio= 0;
333   run_test("\"real-life\" simulation test", test_lockman, THREADS, CYCLES*10);
334 #endif
335   for (i= 0; i < Nlos; i++)
336   {
337     tablockman_release_locks(&tablockman, &loarray1[i]);
338     pthread_mutex_destroy(loarray1[i].mutex);
339     pthread_cond_destroy(loarray1[i].cond);
340   }
341 
342   {
343     ulonglong now= my_getsystime();
344     for (i= 0; i < Ntbls; i++)
345     {
346       tablockman_destroy_locked_table(ltarray+i);
347     }
348     tablockman_destroy(&tablockman);
349     now= my_getsystime()-now;
350     diag("lockman_destroy: %g secs", ((double)now)/1e7);
351   }
352 
353   pthread_mutex_destroy(&rt_mutex);
354   my_end(0);
355   return exit_status();
356 }
357 
358