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