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