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 02111-1301 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
30static const char* default_dbug_option;
31#endif
32
33
34#define SLEEP my_sleep(5)
35
36static const char *base_file1_name= "page_cache_test_file_1";
37static char file1_name[FN_REFLEN];
38static PAGECACHE_FILE file1;
39static pthread_cond_t COND_thread_count;
40static pthread_mutex_t LOCK_thread_count;
41static uint thread_count= 0;
42static PAGECACHE pagecache;
43
44static uint number_of_readers= 5;
45static uint number_of_writers= 5;
46static uint number_of_read_tests= 2000;
47static uint number_of_write_tests= 1000;
48static uint read_sleep_limit= 3;
49static 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*/
57void 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;
68err:
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
77void 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
105void 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
135static 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
159static 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
180char *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
202int 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