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