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