1 /* Copyright (C) 2006-2008 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   TODO: use pthread_join instead of wait_for_thread_count_to_be_zero, like in
18   my_atomic-t.c (see BUG#22320).
19 */
20 
21 #include <tap.h>
22 #include <my_sys.h>
23 #include <m_string.h>
24 #include "test_file.h"
25 #include <tap.h>
26 
27 #define PCACHE_SIZE (TEST_PAGE_SIZE*1024*8)
28 
29 #ifndef DBUG_OFF
30 static const char* default_dbug_option;
31 #endif
32 
33 
34 #define SLEEP my_sleep(5)
35 
36 static const char *base_file1_name= "page_cache_test_file_1";
37 static char file1_name[FN_REFLEN];
38 static PAGECACHE_FILE file1;
39 static pthread_cond_t COND_thread_count;
40 static pthread_mutex_t LOCK_thread_count;
41 static uint thread_count= 0;
42 static PAGECACHE pagecache;
43 
44 static uint number_of_readers= 5;
45 static uint number_of_writers= 5;
46 static uint number_of_read_tests= 2000;
47 static uint number_of_write_tests= 1000;
48 static uint read_sleep_limit= 3;
49 static uint report_divisor= 50;
50 
51 /**
52   @brief Checks page consistency
53 
54   @param buff            pointer to the page content
55   @param task            task ID
56 */
check_page(uchar * buff,int task)57 void check_page(uchar *buff, int task)
58 {
59   uint i;
60   DBUG_ENTER("check_page");
61 
62   for (i= 1; i < TEST_PAGE_SIZE; i++)
63   {
64     if (buff[0] != buff[i])
65       goto err;
66   }
67   DBUG_VOID_RETURN;
68 err:
69   diag("Task %d char #%u '%u' != '%u'", task, i, (uint) buff[0],
70        (uint) buff[i]);
71   DBUG_PRINT("err", ("try to flush"));
72   exit(1);
73 }
74 
75 
76 
reader(int num)77 void reader(int num)
78 {
79   unsigned char *buff;
80   uint i;
81   PAGECACHE_BLOCK_LINK *link;
82 
83   for (i= 0; i < number_of_read_tests; i++)
84   {
85     if (i % report_divisor == 0)
86       diag("Reader %d - %u", num, i);
87     buff= pagecache_read(&pagecache, &file1, 0, 3, NULL,
88                          PAGECACHE_PLAIN_PAGE,
89                          PAGECACHE_LOCK_READ,
90                          &link);
91     check_page(buff, num);
92     pagecache_unlock_by_link(&pagecache, link,
93                              PAGECACHE_LOCK_READ_UNLOCK,
94                              PAGECACHE_UNPIN, 0, 0, 0, FALSE);
95     {
96       int lim= rand() % read_sleep_limit;
97       int j;
98       for (j= 0; j < lim; j++)
99         SLEEP;
100     }
101   }
102 }
103 
104 
writer(int num)105 void writer(int num)
106 {
107   uint i;
108   uchar *buff;
109   PAGECACHE_BLOCK_LINK *link;
110 
111   for (i= 0; i < number_of_write_tests; i++)
112   {
113     uchar c= (uchar) rand() % 256;
114 
115     if (i % report_divisor == 0)
116       diag("Writer %d - %u", num, i);
117     buff= pagecache_read(&pagecache, &file1, 0, 3, NULL,
118                          PAGECACHE_PLAIN_PAGE,
119                          PAGECACHE_LOCK_WRITE,
120                          &link);
121 
122     check_page(buff, num);
123     bfill(buff, TEST_PAGE_SIZE / 2, c);
124     SLEEP;
125     bfill(buff + TEST_PAGE_SIZE/2, TEST_PAGE_SIZE / 2, c);
126     check_page(buff, num);
127     pagecache_unlock_by_link(&pagecache, link,
128                              PAGECACHE_LOCK_WRITE_UNLOCK,
129                              PAGECACHE_UNPIN, 0, 0, 1, FALSE);
130     SLEEP;
131   }
132 }
133 
134 
test_thread_reader(void * arg)135 static void *test_thread_reader(void *arg)
136 {
137   int param=*((int*) arg);
138   my_thread_init();
139   {
140     DBUG_ENTER("test_reader");
141 
142     DBUG_PRINT("enter", ("param: %d", param));
143 
144     reader(param);
145 
146     DBUG_PRINT("info", ("Thread %s ended", my_thread_name()));
147     pthread_mutex_lock(&LOCK_thread_count);
148     ok(1, "reader%d: done", param);
149     thread_count--;
150     pthread_cond_signal(&COND_thread_count); /* Tell main we are ready */
151     pthread_mutex_unlock(&LOCK_thread_count);
152     free((uchar*) arg);
153     my_thread_end();
154   }
155   return 0;
156 }
157 
158 
test_thread_writer(void * arg)159 static void *test_thread_writer(void *arg)
160 {
161   int param=*((int*) arg);
162   my_thread_init();
163   {
164     DBUG_ENTER("test_writer");
165 
166     writer(param);
167 
168     DBUG_PRINT("info", ("Thread %s ended", my_thread_name()));
169     pthread_mutex_lock(&LOCK_thread_count);
170     ok(1, "writer%d: done", param);
171     thread_count--;
172     pthread_cond_signal(&COND_thread_count); /* Tell main we are ready */
173     pthread_mutex_unlock(&LOCK_thread_count);
174     free((uchar*) arg);
175     my_thread_end();
176   }
177   return 0;
178 }
179 
create_tmpdir(const char * progname)180 char *create_tmpdir(const char *progname)
181 {
182   static char test_dirname[FN_REFLEN];
183   char tmp_name[FN_REFLEN];
184   size_t length;
185 
186   /* Create a temporary directory of name TMP-'executable', but without the -t extension */
187   fn_format(tmp_name, progname, "", "", MY_REPLACE_DIR | MY_REPLACE_EXT);
188   length= strlen(tmp_name);
189   if (length > 2 && tmp_name[length-2] == '-' && tmp_name[length-1] == 't')
190     tmp_name[length-2]= 0;
191   strxmov(test_dirname, "TMP-", tmp_name, NullS);
192 
193   /*
194     Don't give an error if we can't create dir, as it may already exist from a previously aborted
195     run
196   */
197   (void) my_mkdir(test_dirname, 0777, MYF(0));
198   return test_dirname;
199 }
200 
201 
main(int argc,char ** argv)202 int main(int argc __attribute__((unused)),
203          char **argv __attribute__((unused)))
204 {
205   pthread_t tid;
206   pthread_attr_t thr_attr;
207   int *param, error;
208   size_t pagen;
209 
210   MY_INIT(argv[0]);
211 
212 #ifndef DBUG_OFF
213 #if defined(__WIN__)
214   default_dbug_option= "d:t:i:O,\\test_pagecache_consist.trace";
215 #else
216   default_dbug_option= "d:t:i:O,/tmp/test_pagecache_consist.trace";
217 #endif
218   if (argc > 1)
219   {
220     DBUG_SET(default_dbug_option);
221     DBUG_SET_INITIAL(default_dbug_option);
222   }
223 #endif
224 
225   {
226   DBUG_ENTER("main");
227   DBUG_PRINT("info", ("Main thread: %s\n", my_thread_name()));
228   plan(number_of_writers + number_of_readers);
229   SKIP_BIG_TESTS(number_of_writers + number_of_readers)
230   {
231 
232   char *test_dirname= create_tmpdir(argv[0]);
233   fn_format(file1_name, base_file1_name, test_dirname, "", MYF(0));
234 
235   if ((file1.file= my_open(file1_name,
236                            O_CREAT | O_TRUNC | O_RDWR, MYF(0))) == -1)
237   {
238     diag( "Got error during file1 creation from open() (errno: %d)\n",
239 	    errno);
240     exit(1);
241   }
242   pagecache_file_set_null_hooks(&file1);
243   DBUG_PRINT("info", ("file1: %d", file1.file));
244   if (my_chmod(file1_name, 0777, MYF(MY_WME)))
245     exit(1);
246   my_pwrite(file1.file, (const uchar*) "test file", 9, 0, MYF(0));
247 
248   if ((error= pthread_cond_init(&COND_thread_count, NULL)))
249   {
250     diag( "COND_thread_count: %d from pthread_cond_init (errno: %d)\n",
251 	    error, errno);
252     exit(1);
253   }
254   if ((error= pthread_mutex_init(&LOCK_thread_count, MY_MUTEX_INIT_FAST)))
255   {
256     diag( "LOCK_thread_count: %d from pthread_cond_init (errno: %d)\n",
257 	    error, errno);
258     exit(1);
259   }
260 
261   if ((error= pthread_attr_init(&thr_attr)))
262   {
263     diag("Got error: %d from pthread_attr_init (errno: %d)\n",
264 	    error,errno);
265     exit(1);
266   }
267   if ((error= pthread_attr_setdetachstate(&thr_attr, PTHREAD_CREATE_DETACHED)))
268   {
269     diag(
270 	    "Got error: %d from pthread_attr_setdetachstate (errno: %d)\n",
271 	    error,errno);
272     exit(1);
273   }
274 
275 #ifdef HAVE_THR_SETCONCURRENCY
276   thr_setconcurrency(2);
277 #endif
278 
279   if ((pagen= init_pagecache(&pagecache, PCACHE_SIZE, 0, 0,
280                              TEST_PAGE_SIZE, 0, 0)) == 0)
281   {
282     diag("Got error: init_pagecache() (errno: %d)\n",
283             errno);
284     exit(1);
285   }
286   DBUG_PRINT("info", ("Page cache %zu pages", pagen));
287   {
288     unsigned char *buffr= malloc(TEST_PAGE_SIZE);
289     memset(buffr, '\0', TEST_PAGE_SIZE);
290     pagecache_write(&pagecache, &file1, 0, 3, buffr,
291                     PAGECACHE_PLAIN_PAGE,
292                     PAGECACHE_LOCK_LEFT_UNLOCKED,
293                     PAGECACHE_PIN_LEFT_UNPINNED,
294                     PAGECACHE_WRITE_DELAY,
295                     0, LSN_IMPOSSIBLE);
296   }
297   pthread_mutex_lock(&LOCK_thread_count);
298 
299   while (number_of_readers != 0 || number_of_writers != 0)
300   {
301     if (number_of_readers != 0)
302     {
303       param=(int*) malloc(sizeof(int));
304       *param= number_of_readers + number_of_writers;
305       if ((error= pthread_create(&tid, &thr_attr, test_thread_reader,
306                                  (void*) param)))
307       {
308         diag("Got error: %d from pthread_create (errno: %d)\n",
309                 error,errno);
310         exit(1);
311       }
312       thread_count++;
313       number_of_readers--;
314     }
315     if (number_of_writers != 0)
316     {
317       param=(int*) malloc(sizeof(int));
318       *param= number_of_writers + number_of_readers;
319       if ((error= pthread_create(&tid, &thr_attr, test_thread_writer,
320                                  (void*) param)))
321       {
322         diag("Got error: %d from pthread_create (errno: %d)\n",
323                 error,errno);
324         exit(1);
325       }
326       thread_count++;
327       number_of_writers--;
328     }
329   }
330   DBUG_PRINT("info", ("Thread started"));
331   pthread_mutex_unlock(&LOCK_thread_count);
332 
333   pthread_attr_destroy(&thr_attr);
334 
335   /* wait finishing */
336   pthread_mutex_lock(&LOCK_thread_count);
337   while (thread_count)
338   {
339     if ((error= pthread_cond_wait(&COND_thread_count, &LOCK_thread_count)))
340       diag("COND_thread_count: %d from pthread_cond_wait\n", error);
341   }
342   pthread_mutex_unlock(&LOCK_thread_count);
343   DBUG_PRINT("info", ("thread ended"));
344 
345   flush_pagecache_blocks(&pagecache, &file1, FLUSH_IGNORE_CHANGED);
346   end_pagecache(&pagecache, 1);
347   DBUG_PRINT("info", ("Page cache ended"));
348 
349   if (my_close(file1.file, MYF(0)) != 0)
350   {
351     diag( "Got error during file1 closing from close() (errno: %d)\n",
352 	    errno);
353     exit(1);
354   }
355   my_delete(file1_name, MYF(0));
356 
357   DBUG_PRINT("info", ("file1 (%d) closed", file1.file));
358   DBUG_PRINT("info", ("Program end"));
359 
360   rmdir(test_dirname);
361   } /* SKIP_BIG_TESTS */
362   my_end(0);
363 
364   return exit_status();
365   }
366 }
367 
368 #include "../ma_check_standalone.h"
369