1/* Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved.
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 St, Fifth Floor, Boston, MA 02110-1301 USA */
15
16/* Test av locking */
17
18#ifndef _WIN32 /*no fork() in Windows*/
19
20#include <my_global.h>
21#include "myisam.h"
22#include <sys/types.h>
23#ifdef HAVE_SYS_WAIT_H
24# include <sys/wait.h>
25#endif
26#ifndef WEXITSTATUS
27# define WEXITSTATUS(stat_val) ((unsigned)(stat_val) >> 8)
28#endif
29#ifndef WIFEXITED
30# define WIFEXITED(stat_val) (((stat_val) & 255) == 0)
31#endif
32
33
34#if defined(HAVE_LRAND48)
35#define rnd(X) (lrand48() % X)
36#define rnd_init(X) srand48(X)
37#else
38#define rnd(X) (random() % X)
39#define rnd_init(X) srandom(X)
40#endif
41
42
43const char *filename= "test3";
44uint tests=10,forks=10,key_cacheing=0,use_log=0;
45
46static void get_options(int argc, char *argv[]);
47void start_test(int id);
48int test_read(MI_INFO *,int),test_write(MI_INFO *,int,int),
49 test_update(MI_INFO *,int,int),test_rrnd(MI_INFO *,int);
50
51struct record {
52 uchar id[8];
53 uchar nr[4];
54 uchar text[10];
55} record;
56
57
58int main(int argc,char **argv)
59{
60 int status,wait_ret;
61 uint i=0;
62 MI_KEYDEF keyinfo[10];
63 MI_COLUMNDEF recinfo[10];
64 HA_KEYSEG keyseg[10][2];
65 MY_INIT(argv[0]);
66 get_options(argc,argv);
67
68 bzero((char*) keyinfo,sizeof(keyinfo));
69 bzero((char*) recinfo,sizeof(recinfo));
70 bzero((char*) keyseg,sizeof(keyseg));
71 keyinfo[0].seg= &keyseg[0][0];
72 keyinfo[0].seg[0].start=0;
73 keyinfo[0].seg[0].length=8;
74 keyinfo[0].seg[0].type=HA_KEYTYPE_TEXT;
75 keyinfo[0].seg[0].flag=HA_SPACE_PACK;
76 keyinfo[0].key_alg=HA_KEY_ALG_BTREE;
77 keyinfo[0].keysegs=1;
78 keyinfo[0].flag = (uint8) HA_PACK_KEY;
79 keyinfo[0].block_length= 0; /* Default block length */
80 keyinfo[1].seg= &keyseg[1][0];
81 keyinfo[1].seg[0].start=8;
82 keyinfo[1].seg[0].length=4; /* Long is always 4 in myisam */
83 keyinfo[1].seg[0].type=HA_KEYTYPE_LONG_INT;
84 keyinfo[1].seg[0].flag=0;
85 keyinfo[1].key_alg=HA_KEY_ALG_BTREE;
86 keyinfo[1].keysegs=1;
87 keyinfo[1].flag =HA_NOSAME;
88 keyinfo[1].block_length= 0; /* Default block length */
89
90 recinfo[0].type=0;
91 recinfo[0].length=sizeof(record.id);
92 recinfo[1].type=0;
93 recinfo[1].length=sizeof(record.nr);
94 recinfo[2].type=0;
95 recinfo[2].length=sizeof(record.text);
96
97 puts("- Creating myisam-file");
98 my_delete(filename,MYF(0)); /* Remove old locks under gdb */
99 if (mi_create(filename,2,&keyinfo[0],2,&recinfo[0],0,(MI_UNIQUEDEF*) 0,
100 (MI_CREATE_INFO*) 0,0))
101 exit(1);
102
103 rnd_init(0);
104 printf("- Starting %d processes\n",forks); fflush(stdout);
105 for (i=0 ; i < forks; i++)
106 {
107 if (!fork())
108 {
109 start_test(i+1);
110 sleep(1);
111 return 0;
112 }
113 (void) rnd(1);
114 }
115
116 for (i=0 ; i < forks ; i++)
117 while ((wait_ret=wait(&status)) && wait_ret == -1);
118 return 0;
119}
120
121
122static void get_options(int argc, char **argv)
123{
124 char *pos,*progname;
125
126 progname= argv[0];
127
128 while (--argc >0 && *(pos = *(++argv)) == '-' ) {
129 switch(*++pos) {
130 case 'l':
131 use_log=1;
132 break;
133 case 'f':
134 forks=atoi(++pos);
135 break;
136 case 't':
137 tests=atoi(++pos);
138 break;
139 case 'K': /* Use key cacheing */
140 key_cacheing=1;
141 break;
142 case 'A': /* All flags */
143 use_log=key_cacheing=1;
144 break;
145 case '?':
146 case 'I':
147 case 'V':
148 printf("%s Ver 1.0 for %s at %s\n",progname,SYSTEM_TYPE,MACHINE_TYPE);
149 puts("By Monty, for your professional use\n");
150 puts("Test av locking with threads\n");
151 printf("Usage: %s [-?lKA] [-f#] [-t#]\n",progname);
152 exit(0);
153 case '#':
154 DBUG_PUSH (++pos);
155 break;
156 default:
157 printf("Illegal option: '%c'\n",*pos);
158 break;
159 }
160 }
161 return;
162}
163
164
165void start_test(int id)
166{
167 uint i;
168 int error,lock_type;
169 MI_ISAMINFO isam_info;
170 MI_INFO *file,*file1,*file2=0,*lock;
171
172 if (use_log)
173 mi_log(1);
174 if (!(file1=mi_open(filename,O_RDWR,HA_OPEN_WAIT_IF_LOCKED)) ||
175 !(file2=mi_open(filename,O_RDWR,HA_OPEN_WAIT_IF_LOCKED)))
176 {
177 fprintf(stderr,"Can't open isam-file: %s\n",filename);
178 exit(1);
179 }
180 if (key_cacheing && rnd(2) == 0)
181 init_key_cache(dflt_key_cache, KEY_CACHE_BLOCK_SIZE, 65536L, 0, 0,
182 0, DEFAULT_KEY_CACHE_PARTITIONS);
183 printf("Process %d, pid: %ld\n", id, (long) getpid());
184 fflush(stdout);
185
186 for (error=i=0 ; i < tests && !error; i++)
187 {
188 file= (rnd(2) == 1) ? file1 : file2;
189 lock=0 ; lock_type=0;
190 if (rnd(10) == 0)
191 {
192 if (mi_lock_database(lock=(rnd(2) ? file1 : file2),
193 lock_type=(rnd(2) == 0 ? F_RDLCK : F_WRLCK)))
194 {
195 fprintf(stderr,"%2d: start: Can't lock table %d\n",id,my_errno);
196 error=1;
197 break;
198 }
199 }
200 switch (rnd(4)) {
201 case 0: error=test_read(file,id); break;
202 case 1: error=test_rrnd(file,id); break;
203 case 2: error=test_write(file,id,lock_type); break;
204 case 3: error=test_update(file,id,lock_type); break;
205 }
206 if (lock)
207 mi_lock_database(lock,F_UNLCK);
208 }
209 if (!error)
210 {
211 mi_status(file1,&isam_info,HA_STATUS_VARIABLE);
212 printf("%2d: End of test. Records: %ld Deleted: %ld\n",
213 id,(long) isam_info.records, (long) isam_info.deleted);
214 fflush(stdout);
215 }
216
217 mi_close(file1);
218 mi_close(file2);
219 if (use_log)
220 mi_log(0);
221 if (error)
222 {
223 printf("%2d: Aborted\n",id); fflush(stdout);
224 exit(1);
225 }
226}
227
228
229int test_read(MI_INFO *file,int id)
230{
231 uint i,lock,found,next,prev;
232 ulong find;
233
234 lock=0;
235 if (rnd(2) == 0)
236 {
237 lock=1;
238 if (mi_lock_database(file,F_RDLCK))
239 {
240 fprintf(stderr,"%2d: Can't lock table %d\n",id,my_errno);
241 return 1;
242 }
243 }
244
245 found=next=prev=0;
246 for (i=0 ; i < 100 ; i++)
247 {
248 find=rnd(100000);
249 if (!mi_rkey(file,record.id,1,(uchar*) &find, HA_WHOLE_KEY,
250 HA_READ_KEY_EXACT))
251 found++;
252 else
253 {
254 if (my_errno != HA_ERR_KEY_NOT_FOUND)
255 {
256 fprintf(stderr,"%2d: Got error %d from read in read\n",id,my_errno);
257 return 1;
258 }
259 else if (!mi_rnext(file,record.id,1))
260 next++;
261 else
262 {
263 if (my_errno != HA_ERR_END_OF_FILE)
264 {
265 fprintf(stderr,"%2d: Got error %d from rnext in read\n",id,my_errno);
266 return 1;
267 }
268 else if (!mi_rprev(file,record.id,1))
269 prev++;
270 else
271 {
272 if (my_errno != HA_ERR_END_OF_FILE)
273 {
274 fprintf(stderr,"%2d: Got error %d from rnext in read\n",
275 id,my_errno);
276 return 1;
277 }
278 }
279 }
280 }
281 }
282 if (lock)
283 {
284 if (mi_lock_database(file,F_UNLCK))
285 {
286 fprintf(stderr,"%2d: Can't unlock table\n",id);
287 return 1;
288 }
289 }
290 printf("%2d: read: found: %5d next: %5d prev: %5d\n",
291 id,found,next,prev);
292 fflush(stdout);
293 return 0;
294}
295
296
297int test_rrnd(MI_INFO *file,int id)
298{
299 uint count,lock;
300
301 lock=0;
302 if (rnd(2) == 0)
303 {
304 lock=1;
305 if (mi_lock_database(file,F_RDLCK))
306 {
307 fprintf(stderr,"%2d: Can't lock table (%d)\n",id,my_errno);
308 mi_close(file);
309 return 1;
310 }
311 if (rnd(2) == 0)
312 mi_extra(file,HA_EXTRA_CACHE,0);
313 }
314
315 count=0;
316 if (mi_rrnd(file,record.id,0L))
317 {
318 if (my_errno == HA_ERR_END_OF_FILE)
319 goto end;
320 fprintf(stderr,"%2d: Can't read first record (%d)\n",id,my_errno);
321 return 1;
322 }
323 for (count=1 ; !mi_rrnd(file,record.id,HA_OFFSET_ERROR) ;count++) ;
324 if (my_errno != HA_ERR_END_OF_FILE)
325 {
326 fprintf(stderr,"%2d: Got error %d from rrnd\n",id,my_errno);
327 return 1;
328 }
329
330end:
331 if (lock)
332 {
333 mi_extra(file,HA_EXTRA_NO_CACHE,0);
334 if (mi_lock_database(file,F_UNLCK))
335 {
336 fprintf(stderr,"%2d: Can't unlock table\n",id);
337 exit(0);
338 }
339 }
340 printf("%2d: rrnd: %5d\n",id,count); fflush(stdout);
341 return 0;
342}
343
344
345int test_write(MI_INFO *file,int id,int lock_type)
346{
347 uint i,tries,count,lock;
348
349 lock=0;
350 if (rnd(2) == 0 || lock_type == F_RDLCK)
351 {
352 lock=1;
353 if (mi_lock_database(file,F_WRLCK))
354 {
355 if (lock_type == F_RDLCK && my_errno == EDEADLK)
356 {
357 printf("%2d: write: deadlock\n",id); fflush(stdout);
358 return 0;
359 }
360 fprintf(stderr,"%2d: Can't lock table (%d)\n",id,my_errno);
361 mi_close(file);
362 return 1;
363 }
364 if (rnd(2) == 0)
365 mi_extra(file,HA_EXTRA_WRITE_CACHE,0);
366 }
367
368 sprintf((char*) record.id,"%7ld",(long) getpid());
369 strnmov((char*) record.text,"Testing...", sizeof(record.text));
370
371 tries=(uint) rnd(100)+10;
372 for (i=count=0 ; i < tries ; i++)
373 {
374 uint32 tmp=rnd(80000)+20000;
375 int4store(record.nr,tmp);
376 if (!mi_write(file,record.id))
377 count++;
378 else
379 {
380 if (my_errno != HA_ERR_FOUND_DUPP_KEY)
381 {
382 fprintf(stderr,"%2d: Got error %d (errno %d) from write\n",id,my_errno,
383 errno);
384 return 1;
385 }
386 }
387 }
388 if (lock)
389 {
390 mi_extra(file,HA_EXTRA_NO_CACHE,0);
391 if (mi_lock_database(file,F_UNLCK))
392 {
393 fprintf(stderr,"%2d: Can't unlock table\n",id);
394 exit(0);
395 }
396 }
397 printf("%2d: write: %5d\n",id,count); fflush(stdout);
398 return 0;
399}
400
401
402int test_update(MI_INFO *file,int id,int lock_type)
403{
404 uint i,lock,found,next,prev,update;
405 uint32 tmp;
406 char find[4];
407 struct record new_record;
408
409 lock=0;
410 if (rnd(2) == 0 || lock_type == F_RDLCK)
411 {
412 lock=1;
413 if (mi_lock_database(file,F_WRLCK))
414 {
415 if (lock_type == F_RDLCK && my_errno == EDEADLK)
416 {
417 printf("%2d: write: deadlock\n",id); fflush(stdout);
418 return 0;
419 }
420 fprintf(stderr,"%2d: Can't lock table (%d)\n",id,my_errno);
421 return 1;
422 }
423 }
424 bzero((char*) &new_record,sizeof(new_record));
425 strmov((char*) new_record.text,"Updated");
426
427 found=next=prev=update=0;
428 for (i=0 ; i < 100 ; i++)
429 {
430 tmp=rnd(100000);
431 int4store(find,tmp);
432 if (!mi_rkey(file,record.id,1,(uchar*) find, HA_WHOLE_KEY,
433 HA_READ_KEY_EXACT))
434 found++;
435 else
436 {
437 if (my_errno != HA_ERR_KEY_NOT_FOUND)
438 {
439 fprintf(stderr,"%2d: Got error %d from read in update\n",id,my_errno);
440 return 1;
441 }
442 else if (!mi_rnext(file,record.id,1))
443 next++;
444 else
445 {
446 if (my_errno != HA_ERR_END_OF_FILE)
447 {
448 fprintf(stderr,"%2d: Got error %d from rnext in update\n",
449 id,my_errno);
450 return 1;
451 }
452 else if (!mi_rprev(file,record.id,1))
453 prev++;
454 else
455 {
456 if (my_errno != HA_ERR_END_OF_FILE)
457 {
458 fprintf(stderr,"%2d: Got error %d from rnext in update\n",
459 id,my_errno);
460 return 1;
461 }
462 continue;
463 }
464 }
465 }
466 memcpy(new_record.id, record.id, sizeof(record.id));
467 tmp=rnd(20000)+40000;
468 int4store(new_record.nr,tmp);
469 if (!mi_update(file,record.id,new_record.id))
470 update++;
471 else
472 {
473 if (my_errno != HA_ERR_RECORD_CHANGED &&
474 my_errno != HA_ERR_RECORD_DELETED &&
475 my_errno != HA_ERR_FOUND_DUPP_KEY)
476 {
477 fprintf(stderr,"%2d: Got error %d from update\n",id,my_errno);
478 return 1;
479 }
480 }
481 }
482 if (lock)
483 {
484 if (mi_lock_database(file,F_UNLCK))
485 {
486 fprintf(stderr,"Can't unlock table,id, error%d\n",my_errno);
487 return 1;
488 }
489 }
490 printf("%2d: update: %5d\n",id,update); fflush(stdout);
491 return 0;
492}
493
494#include "mi_extrafunc.h"
495#else /* _WIN32 */
496
497#include <stdio.h>
498
499int main()
500{
501 fprintf(stderr,"this test has not been ported to Windows\n");
502 return 0;
503}
504
505#endif /* _WIN32 */
506