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