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 | Use diag() instead of fprintf(stderr). |
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*10) |
28 | |
29 | #ifndef DBUG_OFF |
30 | static const char* default_dbug_option; |
31 | #endif |
32 | |
33 | #ifndef BIG |
34 | #undef SKIP_BIG_TESTS |
35 | #define SKIP_BIG_TESTS(X) /* no-op */ |
36 | #endif |
37 | |
38 | static const char *base_file1_name= "page_cache_test_file_1" ; |
39 | static const char *base_file2_name= "page_cache_test_file_2" ; |
40 | static char file1_name[FN_REFLEN], file2_name[FN_REFLEN]; |
41 | static PAGECACHE_FILE file1; |
42 | static pthread_cond_t COND_thread_count; |
43 | static pthread_mutex_t LOCK_thread_count; |
44 | static uint thread_count; |
45 | static PAGECACHE pagecache; |
46 | |
47 | /* |
48 | File contance descriptors |
49 | */ |
50 | static struct file_desc simple_read_write_test_file[]= |
51 | { |
52 | { TEST_PAGE_SIZE, '\1'}, |
53 | {0, 0} |
54 | }; |
55 | static struct file_desc simple_read_change_write_read_test_file[]= |
56 | { |
57 | { TEST_PAGE_SIZE/2, '\65'}, |
58 | { TEST_PAGE_SIZE/2, '\1'}, |
59 | {0, 0} |
60 | }; |
61 | static struct file_desc simple_pin_test_file1[]= |
62 | { |
63 | { TEST_PAGE_SIZE*2, '\1'}, |
64 | {0, 0} |
65 | }; |
66 | static struct file_desc simple_pin_test_file2[]= |
67 | { |
68 | { TEST_PAGE_SIZE/2, '\1'}, |
69 | { TEST_PAGE_SIZE/2, (unsigned char)129}, |
70 | { TEST_PAGE_SIZE, '\1'}, |
71 | {0, 0} |
72 | }; |
73 | static struct file_desc simple_pin_no_lock_test_file1[]= |
74 | { |
75 | { TEST_PAGE_SIZE, '\4'}, |
76 | {0, 0} |
77 | }; |
78 | static struct file_desc simple_pin_no_lock_test_file2[]= |
79 | { |
80 | { TEST_PAGE_SIZE, '\5'}, |
81 | {0, 0} |
82 | }; |
83 | static struct file_desc simple_pin_no_lock_test_file3[]= |
84 | { |
85 | { TEST_PAGE_SIZE, '\6'}, |
86 | {0, 0} |
87 | }; |
88 | static struct file_desc simple_delete_forget_test_file[]= |
89 | { |
90 | { TEST_PAGE_SIZE, '\1'}, |
91 | {0, 0} |
92 | }; |
93 | static struct file_desc simple_delete_flush_test_file[]= |
94 | { |
95 | { TEST_PAGE_SIZE, '\2'}, |
96 | {0, 0} |
97 | }; |
98 | |
99 | |
100 | /* |
101 | Recreate and reopen a file for test |
102 | |
103 | SYNOPSIS |
104 | reset_file() |
105 | file File to reset |
106 | file_name Path (and name) of file which should be reset |
107 | */ |
108 | |
109 | void reset_file(PAGECACHE_FILE *file, const char *file_name) |
110 | { |
111 | flush_pagecache_blocks(&pagecache, file, FLUSH_RELEASE); |
112 | if (my_close(file->file, MYF(MY_WME))) |
113 | exit(1); |
114 | my_delete(file_name, MYF(MY_WME)); |
115 | if ((file->file= my_open(file_name, |
116 | O_CREAT | O_TRUNC | O_RDWR, MYF(0))) == -1) |
117 | { |
118 | diag("Got error during %s creation from open() (errno: %d)\n" , |
119 | file_name, my_errno); |
120 | exit(1); |
121 | } |
122 | } |
123 | |
124 | /* |
125 | Write then read page, check file on disk |
126 | */ |
127 | |
128 | int simple_read_write_test() |
129 | { |
130 | unsigned char *buffw= malloc(TEST_PAGE_SIZE); |
131 | unsigned char *buffr= malloc(TEST_PAGE_SIZE); |
132 | int res; |
133 | DBUG_ENTER("simple_read_write_test" ); |
134 | bfill(buffw, TEST_PAGE_SIZE, '\1'); |
135 | pagecache_write(&pagecache, &file1, 0, 3, buffw, |
136 | PAGECACHE_PLAIN_PAGE, |
137 | PAGECACHE_LOCK_LEFT_UNLOCKED, |
138 | PAGECACHE_PIN_LEFT_UNPINNED, |
139 | PAGECACHE_WRITE_DELAY, |
140 | 0, LSN_IMPOSSIBLE); |
141 | pagecache_read(&pagecache, &file1, 0, 3, buffr, |
142 | PAGECACHE_PLAIN_PAGE, |
143 | PAGECACHE_LOCK_LEFT_UNLOCKED, |
144 | 0); |
145 | ok((res= MY_TEST(memcmp(buffr, buffw, TEST_PAGE_SIZE) == 0)), |
146 | "Simple write-read page " ); |
147 | if (flush_pagecache_blocks(&pagecache, &file1, FLUSH_FORCE_WRITE)) |
148 | { |
149 | diag("Got error during flushing pagecache\n" ); |
150 | exit(1); |
151 | } |
152 | ok((res&= MY_TEST(test_file(file1, file1_name, TEST_PAGE_SIZE, TEST_PAGE_SIZE, |
153 | simple_read_write_test_file))), |
154 | "Simple write-read page file" ); |
155 | if (res) |
156 | reset_file(&file1, file1_name); |
157 | free(buffw); |
158 | free(buffr); |
159 | DBUG_RETURN(res); |
160 | } |
161 | |
162 | |
163 | /* |
164 | Prepare page, then read (and lock), change (write new value and unlock), |
165 | then check the page in the cache and on the disk |
166 | */ |
167 | int simple_read_change_write_read_test() |
168 | { |
169 | unsigned char *buffw= malloc(TEST_PAGE_SIZE); |
170 | unsigned char *buffr= malloc(TEST_PAGE_SIZE); |
171 | int res, res2; |
172 | DBUG_ENTER("simple_read_change_write_read_test" ); |
173 | |
174 | /* prepare the file */ |
175 | bfill(buffw, TEST_PAGE_SIZE, '\1'); |
176 | pagecache_write(&pagecache, &file1, 0, 3, buffw, |
177 | PAGECACHE_PLAIN_PAGE, |
178 | PAGECACHE_LOCK_LEFT_UNLOCKED, |
179 | PAGECACHE_PIN_LEFT_UNPINNED, |
180 | PAGECACHE_WRITE_DELAY, |
181 | 0, LSN_IMPOSSIBLE); |
182 | if (flush_pagecache_blocks(&pagecache, &file1, FLUSH_FORCE_WRITE)) |
183 | { |
184 | diag("Got error during flushing pagecache\n" ); |
185 | exit(1); |
186 | } |
187 | /* test */ |
188 | pagecache_read(&pagecache, &file1, 0, 3, buffw, |
189 | PAGECACHE_PLAIN_PAGE, |
190 | PAGECACHE_LOCK_WRITE, |
191 | 0); |
192 | bfill(buffw, TEST_PAGE_SIZE/2, '\65'); |
193 | pagecache_write(&pagecache, &file1, 0, 3, buffw, |
194 | PAGECACHE_PLAIN_PAGE, |
195 | PAGECACHE_LOCK_WRITE_UNLOCK, |
196 | PAGECACHE_UNPIN, |
197 | PAGECACHE_WRITE_DELAY, |
198 | 0, LSN_IMPOSSIBLE); |
199 | |
200 | pagecache_read(&pagecache, &file1, 0, 3, buffr, |
201 | PAGECACHE_PLAIN_PAGE, |
202 | PAGECACHE_LOCK_LEFT_UNLOCKED, |
203 | 0); |
204 | ok((res= MY_TEST(memcmp(buffr, buffw, TEST_PAGE_SIZE) == 0)), |
205 | "Simple read-change-write-read page " ); |
206 | DBUG_ASSERT(pagecache.blocks_changed == 1); |
207 | if (flush_pagecache_blocks(&pagecache, &file1, FLUSH_FORCE_WRITE)) |
208 | { |
209 | diag("Got error during flushing pagecache\n" ); |
210 | exit(1); |
211 | } |
212 | DBUG_ASSERT(pagecache.blocks_changed == 0); |
213 | ok((res2= MY_TEST(test_file(file1, file1_name, TEST_PAGE_SIZE, TEST_PAGE_SIZE, |
214 | simple_read_change_write_read_test_file))), |
215 | "Simple read-change-write-read page file" ); |
216 | if (res && res2) |
217 | reset_file(&file1, file1_name); |
218 | free(buffw); |
219 | free(buffr); |
220 | DBUG_RETURN(res && res2); |
221 | } |
222 | |
223 | |
224 | /* |
225 | Prepare page, read page 0 (and pin) then write page 1 and page 0. |
226 | Flush the file (should flush only page 1 and return 1 (page 0 is |
227 | still pinned). |
228 | Check file on the disk. |
229 | Unpin and flush. |
230 | Check file on the disk. |
231 | */ |
232 | int simple_pin_test() |
233 | { |
234 | unsigned char *buffw= malloc(TEST_PAGE_SIZE); |
235 | int res; |
236 | DBUG_ENTER("simple_pin_test" ); |
237 | /* prepare the file */ |
238 | bfill(buffw, TEST_PAGE_SIZE, '\1'); |
239 | pagecache_write(&pagecache, &file1, 0, 3, buffw, |
240 | PAGECACHE_PLAIN_PAGE, |
241 | PAGECACHE_LOCK_LEFT_UNLOCKED, |
242 | PAGECACHE_PIN_LEFT_UNPINNED, |
243 | PAGECACHE_WRITE_DELAY, |
244 | 0, LSN_IMPOSSIBLE); |
245 | /* test */ |
246 | if (flush_pagecache_blocks(&pagecache, &file1, FLUSH_FORCE_WRITE)) |
247 | { |
248 | diag("Got error during flushing pagecache\n" ); |
249 | exit(1); |
250 | } |
251 | pagecache_read(&pagecache, &file1, 0, 3, buffw, |
252 | PAGECACHE_PLAIN_PAGE, |
253 | PAGECACHE_LOCK_WRITE, |
254 | 0); |
255 | pagecache_write(&pagecache, &file1, 1, 3, buffw, |
256 | PAGECACHE_PLAIN_PAGE, |
257 | PAGECACHE_LOCK_LEFT_UNLOCKED, |
258 | PAGECACHE_PIN_LEFT_UNPINNED, |
259 | PAGECACHE_WRITE_DELAY, |
260 | 0, LSN_IMPOSSIBLE); |
261 | bfill(buffw + TEST_PAGE_SIZE/2, TEST_PAGE_SIZE/2, ((unsigned char) 129)); |
262 | pagecache_write(&pagecache, &file1, 0, 3, buffw, |
263 | PAGECACHE_PLAIN_PAGE, |
264 | PAGECACHE_LOCK_LEFT_WRITELOCKED, |
265 | PAGECACHE_PIN_LEFT_PINNED, |
266 | PAGECACHE_WRITE_DELAY, |
267 | 0, LSN_IMPOSSIBLE); |
268 | /* |
269 | We have to get error because one page of the file is pinned, |
270 | other page should be flushed |
271 | */ |
272 | if (!flush_pagecache_blocks(&pagecache, &file1, FLUSH_FORCE_WRITE)) |
273 | { |
274 | diag("Did not get error in flush_pagecache_blocks\n" ); |
275 | res= 0; |
276 | goto err; |
277 | } |
278 | ok((res= MY_TEST(test_file(file1, file1_name, TEST_PAGE_SIZE * 2, |
279 | TEST_PAGE_SIZE * 2, simple_pin_test_file1))), |
280 | "Simple pin page file with pin" ); |
281 | pagecache_unlock(&pagecache, |
282 | &file1, |
283 | 0, |
284 | PAGECACHE_LOCK_WRITE_UNLOCK, |
285 | PAGECACHE_UNPIN, |
286 | 0, 0, 0); |
287 | if (flush_pagecache_blocks(&pagecache, &file1, FLUSH_FORCE_WRITE)) |
288 | { |
289 | diag("Got error in flush_pagecache_blocks\n" ); |
290 | res= 0; |
291 | goto err; |
292 | } |
293 | ok((res&= MY_TEST(test_file(file1, file1_name, TEST_PAGE_SIZE * 2, |
294 | TEST_PAGE_SIZE, simple_pin_test_file2))), |
295 | "Simple pin page result file" ); |
296 | if (res) |
297 | reset_file(&file1, file1_name); |
298 | err: |
299 | free(buffw); |
300 | DBUG_RETURN(res); |
301 | } |
302 | |
303 | /* |
304 | Prepare page, read page 0 (and pin) then write page 1 and page 0. |
305 | Flush the file (should flush only page 1 and return 1 (page 0 is |
306 | still pinned). |
307 | Check file on the disk. |
308 | Unpin and flush. |
309 | Check file on the disk. |
310 | */ |
311 | int simple_pin_test2() |
312 | { |
313 | unsigned char *buffw= malloc(TEST_PAGE_SIZE); |
314 | int res; |
315 | DBUG_ENTER("simple_pin_test2" ); |
316 | /* prepare the file */ |
317 | bfill(buffw, TEST_PAGE_SIZE, '\1'); |
318 | pagecache_write(&pagecache, &file1, 0, 3, buffw, |
319 | PAGECACHE_PLAIN_PAGE, |
320 | PAGECACHE_LOCK_LEFT_UNLOCKED, |
321 | PAGECACHE_PIN_LEFT_UNPINNED, |
322 | PAGECACHE_WRITE_DELAY, |
323 | 0, LSN_IMPOSSIBLE); |
324 | /* test */ |
325 | if (flush_pagecache_blocks(&pagecache, &file1, FLUSH_FORCE_WRITE)) |
326 | { |
327 | diag("Got error during flushing pagecache\n" ); |
328 | exit(1); |
329 | } |
330 | pagecache_read(&pagecache, &file1, 0, 3, buffw, |
331 | PAGECACHE_PLAIN_PAGE, |
332 | PAGECACHE_LOCK_WRITE, |
333 | 0); |
334 | pagecache_write(&pagecache, &file1, 1, 3, buffw, |
335 | PAGECACHE_PLAIN_PAGE, |
336 | PAGECACHE_LOCK_LEFT_UNLOCKED, |
337 | PAGECACHE_PIN_LEFT_UNPINNED, |
338 | PAGECACHE_WRITE_DELAY, |
339 | 0, LSN_IMPOSSIBLE); |
340 | bfill(buffw + TEST_PAGE_SIZE/2, TEST_PAGE_SIZE/2, ((unsigned char) 129)); |
341 | pagecache_write(&pagecache, &file1, 0, 3, buffw, |
342 | PAGECACHE_PLAIN_PAGE, |
343 | PAGECACHE_LOCK_WRITE_TO_READ, |
344 | PAGECACHE_PIN_LEFT_PINNED, |
345 | PAGECACHE_WRITE_DELAY, |
346 | 0, LSN_IMPOSSIBLE); |
347 | /* |
348 | We have to get error because one page of the file is pinned, |
349 | other page should be flushed |
350 | */ |
351 | if (!flush_pagecache_blocks(&pagecache, &file1, FLUSH_KEEP_LAZY)) |
352 | { |
353 | diag("Did not get error in flush_pagecache_blocks 2\n" ); |
354 | res= 0; |
355 | goto err; |
356 | } |
357 | ok((res= MY_TEST(test_file(file1, file1_name, TEST_PAGE_SIZE * 2, |
358 | TEST_PAGE_SIZE * 2, simple_pin_test_file1))), |
359 | "Simple pin page file with pin 2" ); |
360 | |
361 | /* Test that a normal flush goes through */ |
362 | if (flush_pagecache_blocks(&pagecache, &file1, FLUSH_FORCE_WRITE)) |
363 | { |
364 | diag("Got error in flush_pagecache_blocks 3\n" ); |
365 | res= 0; |
366 | goto err; |
367 | } |
368 | pagecache_unlock(&pagecache, |
369 | &file1, |
370 | 0, |
371 | PAGECACHE_LOCK_READ_UNLOCK, |
372 | PAGECACHE_UNPIN, |
373 | 0, 0, 0); |
374 | if (flush_pagecache_blocks(&pagecache, &file1, FLUSH_FORCE_WRITE)) |
375 | { |
376 | diag("Got error in flush_pagecache_blocks 4\n" ); |
377 | res= 0; |
378 | goto err; |
379 | } |
380 | ok((res&= MY_TEST(test_file(file1, file1_name, TEST_PAGE_SIZE * 2, |
381 | TEST_PAGE_SIZE, simple_pin_test_file2))), |
382 | "Simple pin page result file 2" ); |
383 | if (res) |
384 | reset_file(&file1, file1_name); |
385 | err: |
386 | free(buffw); |
387 | DBUG_RETURN(res); |
388 | } |
389 | |
390 | /* |
391 | Checks pins without lock. |
392 | */ |
393 | int simple_pin_no_lock_test() |
394 | { |
395 | unsigned char *buffw= malloc(TEST_PAGE_SIZE); |
396 | PAGECACHE_BLOCK_LINK *link; |
397 | int res; |
398 | DBUG_ENTER("simple_pin_no_lock_test" ); |
399 | /* prepare the file */ |
400 | bfill(buffw, TEST_PAGE_SIZE, '\4'); |
401 | pagecache_write(&pagecache, &file1, 0, 3, buffw, |
402 | PAGECACHE_PLAIN_PAGE, |
403 | PAGECACHE_LOCK_LEFT_UNLOCKED, |
404 | PAGECACHE_PIN_LEFT_UNPINNED, |
405 | PAGECACHE_WRITE_DELAY, |
406 | 0, LSN_IMPOSSIBLE); |
407 | /* test */ |
408 | if (flush_pagecache_blocks(&pagecache, &file1, FLUSH_FORCE_WRITE)) |
409 | { |
410 | diag("Got error during flushing pagecache 2\n" ); |
411 | exit(1); |
412 | } |
413 | bfill(buffw, TEST_PAGE_SIZE, '\5'); |
414 | pagecache_write(&pagecache, &file1, 0, 3, buffw, |
415 | PAGECACHE_PLAIN_PAGE, |
416 | PAGECACHE_LOCK_LEFT_UNLOCKED, |
417 | PAGECACHE_PIN, |
418 | PAGECACHE_WRITE_DELAY, |
419 | 0, LSN_IMPOSSIBLE); |
420 | /* |
421 | We have to get error because one page of the file is pinned, |
422 | other page should be flushed |
423 | */ |
424 | if (!flush_pagecache_blocks(&pagecache, &file1, FLUSH_KEEP_LAZY)) |
425 | { |
426 | diag("Did not get error in flush_pagecache_blocks 2\n" ); |
427 | res= 0; |
428 | goto err; |
429 | } |
430 | ok((res= MY_TEST(test_file(file1, file1_name, TEST_PAGE_SIZE, TEST_PAGE_SIZE, |
431 | simple_pin_no_lock_test_file1))), |
432 | "Simple pin (no lock) page file with pin 2" ); |
433 | pagecache_unlock(&pagecache, |
434 | &file1, |
435 | 0, |
436 | PAGECACHE_LOCK_LEFT_UNLOCKED, |
437 | PAGECACHE_UNPIN, |
438 | 0, 0, 0); |
439 | if (flush_pagecache_blocks(&pagecache, &file1, FLUSH_FORCE_WRITE)) |
440 | { |
441 | diag("Got error in flush_pagecache_blocks 2\n" ); |
442 | res= 0; |
443 | goto err; |
444 | } |
445 | ok((res&= MY_TEST(test_file(file1, file1_name, TEST_PAGE_SIZE, TEST_PAGE_SIZE, |
446 | simple_pin_no_lock_test_file2))), |
447 | "Simple pin (no lock) page result file 2" ); |
448 | |
449 | bfill(buffw, TEST_PAGE_SIZE, '\6'); |
450 | pagecache_write(&pagecache, &file1, 0, 3, buffw, |
451 | PAGECACHE_PLAIN_PAGE, |
452 | PAGECACHE_LOCK_WRITE, |
453 | PAGECACHE_PIN, |
454 | PAGECACHE_WRITE_DELAY, |
455 | &link, LSN_IMPOSSIBLE); |
456 | pagecache_unlock_by_link(&pagecache, link, |
457 | PAGECACHE_LOCK_WRITE_UNLOCK, |
458 | PAGECACHE_PIN_LEFT_PINNED, 0, 0, 1, FALSE); |
459 | if (!flush_pagecache_blocks(&pagecache, &file1, FLUSH_KEEP_LAZY)) |
460 | { |
461 | diag("Did not get error in flush_pagecache_blocks 3\n" ); |
462 | res= 0; |
463 | goto err; |
464 | } |
465 | ok((res= MY_TEST(test_file(file1, file1_name, TEST_PAGE_SIZE, TEST_PAGE_SIZE, |
466 | simple_pin_no_lock_test_file2))), |
467 | "Simple pin (no lock) page file with pin 3" ); |
468 | pagecache_unpin_by_link(&pagecache, link, 0); |
469 | if (flush_pagecache_blocks(&pagecache, &file1, FLUSH_FORCE_WRITE)) |
470 | { |
471 | diag("Got error in flush_pagecache_blocks 3\n" ); |
472 | res= 0; |
473 | goto err; |
474 | } |
475 | ok((res&= MY_TEST(test_file(file1, file1_name, TEST_PAGE_SIZE, TEST_PAGE_SIZE, |
476 | simple_pin_no_lock_test_file3))), |
477 | "Simple pin (no lock) page result file 3" ); |
478 | if (res) |
479 | reset_file(&file1, file1_name); |
480 | err: |
481 | free(buffw); |
482 | DBUG_RETURN(res); |
483 | } |
484 | /* |
485 | Prepare page, write new value, then delete page from cache without flush, |
486 | on the disk should be page with old content written during preparation |
487 | */ |
488 | |
489 | int simple_delete_forget_test() |
490 | { |
491 | unsigned char *buffw= malloc(TEST_PAGE_SIZE); |
492 | unsigned char *buffr= malloc(TEST_PAGE_SIZE); |
493 | int res; |
494 | DBUG_ENTER("simple_delete_forget_test" ); |
495 | /* prepare the file */ |
496 | bfill(buffw, TEST_PAGE_SIZE, '\1'); |
497 | pagecache_write(&pagecache, &file1, 0, 3, buffw, |
498 | PAGECACHE_PLAIN_PAGE, |
499 | PAGECACHE_LOCK_LEFT_UNLOCKED, |
500 | PAGECACHE_PIN_LEFT_UNPINNED, |
501 | PAGECACHE_WRITE_DELAY, |
502 | 0, LSN_IMPOSSIBLE); |
503 | flush_pagecache_blocks(&pagecache, &file1, FLUSH_FORCE_WRITE); |
504 | /* test */ |
505 | bfill(buffw, TEST_PAGE_SIZE, '\2'); |
506 | pagecache_write(&pagecache, &file1, 0, 3, buffw, |
507 | PAGECACHE_PLAIN_PAGE, |
508 | PAGECACHE_LOCK_LEFT_UNLOCKED, |
509 | PAGECACHE_PIN_LEFT_UNPINNED, |
510 | PAGECACHE_WRITE_DELAY, |
511 | 0, LSN_IMPOSSIBLE); |
512 | pagecache_delete(&pagecache, &file1, 0, |
513 | PAGECACHE_LOCK_WRITE, 0); |
514 | flush_pagecache_blocks(&pagecache, &file1, FLUSH_FORCE_WRITE); |
515 | ok((res= MY_TEST(test_file(file1, file1_name, TEST_PAGE_SIZE, TEST_PAGE_SIZE, |
516 | simple_delete_forget_test_file))), |
517 | "Simple delete-forget page file" ); |
518 | if (res) |
519 | reset_file(&file1, file1_name); |
520 | free(buffw); |
521 | free(buffr); |
522 | DBUG_RETURN(res); |
523 | } |
524 | |
525 | /* |
526 | Prepare page with locking, write new content to the page, |
527 | delete page with flush and on existing lock, |
528 | check that page on disk contain new value. |
529 | */ |
530 | |
531 | int simple_delete_flush_test() |
532 | { |
533 | unsigned char *buffw= malloc(TEST_PAGE_SIZE); |
534 | unsigned char *buffr= malloc(TEST_PAGE_SIZE); |
535 | PAGECACHE_BLOCK_LINK *link; |
536 | int res; |
537 | DBUG_ENTER("simple_delete_flush_test" ); |
538 | /* prepare the file */ |
539 | bfill(buffw, TEST_PAGE_SIZE, '\1'); |
540 | pagecache_write(&pagecache, &file1, 0, 3, buffw, |
541 | PAGECACHE_PLAIN_PAGE, |
542 | PAGECACHE_LOCK_WRITE, |
543 | PAGECACHE_PIN, |
544 | PAGECACHE_WRITE_DELAY, |
545 | &link, LSN_IMPOSSIBLE); |
546 | flush_pagecache_blocks(&pagecache, &file1, FLUSH_FORCE_WRITE); |
547 | /* test */ |
548 | bfill(buffw, TEST_PAGE_SIZE, '\2'); |
549 | pagecache_write(&pagecache, &file1, 0, 3, buffw, |
550 | PAGECACHE_PLAIN_PAGE, |
551 | PAGECACHE_LOCK_LEFT_WRITELOCKED, |
552 | PAGECACHE_PIN_LEFT_PINNED, |
553 | PAGECACHE_WRITE_DELAY, |
554 | 0, LSN_IMPOSSIBLE); |
555 | if (pagecache_delete_by_link(&pagecache, link, |
556 | PAGECACHE_LOCK_LEFT_WRITELOCKED, 1)) |
557 | { |
558 | diag("simple_delete_flush_test: error during delete" ); |
559 | exit(1); |
560 | } |
561 | flush_pagecache_blocks(&pagecache, &file1, FLUSH_FORCE_WRITE); |
562 | ok((res= MY_TEST(test_file(file1, file1_name, TEST_PAGE_SIZE, TEST_PAGE_SIZE, |
563 | simple_delete_flush_test_file))), |
564 | "Simple delete flush (link) page file" ); |
565 | if (res) |
566 | reset_file(&file1, file1_name); |
567 | free(buffw); |
568 | free(buffr); |
569 | DBUG_RETURN(res); |
570 | } |
571 | |
572 | |
573 | /* |
574 | write then read file bigger then cache |
575 | */ |
576 | |
577 | int simple_big_test() |
578 | { |
579 | unsigned char *buffw= (unsigned char *) my_malloc(TEST_PAGE_SIZE, MYF(MY_WME)); |
580 | unsigned char *buffr= (unsigned char *) my_malloc(TEST_PAGE_SIZE, MYF(MY_WME)); |
581 | struct file_desc *desc= ((struct file_desc *) |
582 | my_malloc((PCACHE_SIZE/(TEST_PAGE_SIZE/2) + 1) * |
583 | sizeof(struct file_desc), MYF(MY_WME))); |
584 | int res, i; |
585 | DBUG_ENTER("simple_big_test" ); |
586 | |
587 | /* prepare the file twice larger then cache */ |
588 | for (i= 0; i < PCACHE_SIZE/(TEST_PAGE_SIZE/2); i++) |
589 | { |
590 | bfill(buffw, TEST_PAGE_SIZE, (unsigned char) (i & 0xff)); |
591 | desc[i].length= TEST_PAGE_SIZE; |
592 | desc[i].content= (i & 0xff); |
593 | pagecache_write(&pagecache, &file1, i, 3, buffw, |
594 | PAGECACHE_PLAIN_PAGE, |
595 | PAGECACHE_LOCK_LEFT_UNLOCKED, |
596 | PAGECACHE_PIN_LEFT_UNPINNED, |
597 | PAGECACHE_WRITE_DELAY, |
598 | 0, LSN_IMPOSSIBLE); |
599 | } |
600 | desc[i].length= 0; |
601 | desc[i].content= '\0'; |
602 | ok(1, "Simple big file write" ); |
603 | /* check written pages sequentally read */ |
604 | for (i= 0; i < PCACHE_SIZE/(TEST_PAGE_SIZE/2); i++) |
605 | { |
606 | int j; |
607 | pagecache_read(&pagecache, &file1, i, 3, buffr, |
608 | PAGECACHE_PLAIN_PAGE, |
609 | PAGECACHE_LOCK_LEFT_UNLOCKED, |
610 | 0); |
611 | for(j= 0; j < TEST_PAGE_SIZE; j++) |
612 | { |
613 | if (buffr[j] != (i & 0xff)) |
614 | { |
615 | diag("simple_big_test seq: page %u byte %u mismatch\n" , i, j); |
616 | res= 0; |
617 | goto err; |
618 | } |
619 | } |
620 | } |
621 | ok(1, "Simple big file sequential read" ); |
622 | /* chack random reads */ |
623 | for (i= 0; i < PCACHE_SIZE/(TEST_PAGE_SIZE); i++) |
624 | { |
625 | int j, page; |
626 | page= rand() % (PCACHE_SIZE/(TEST_PAGE_SIZE/2)); |
627 | pagecache_read(&pagecache, &file1, page, 3, buffr, |
628 | PAGECACHE_PLAIN_PAGE, |
629 | PAGECACHE_LOCK_LEFT_UNLOCKED, |
630 | 0); |
631 | for(j= 0; j < TEST_PAGE_SIZE; j++) |
632 | { |
633 | if (buffr[j] != (page & 0xff)) |
634 | { |
635 | diag("simple_big_test rnd: page %u byte %u mismatch\n" , page, j); |
636 | res= 0; |
637 | goto err; |
638 | } |
639 | } |
640 | } |
641 | ok(1, "Simple big file random read" ); |
642 | flush_pagecache_blocks(&pagecache, &file1, FLUSH_FORCE_WRITE); |
643 | |
644 | ok((res= MY_TEST(test_file(file1, file1_name, PCACHE_SIZE * 2, TEST_PAGE_SIZE, |
645 | desc))), |
646 | "Simple big file" ); |
647 | if (res) |
648 | reset_file(&file1, file1_name); |
649 | |
650 | err: |
651 | my_free(buffw); |
652 | my_free(buffr); |
653 | my_free(desc); |
654 | DBUG_RETURN(res); |
655 | } |
656 | |
657 | |
658 | /* |
659 | Thread function |
660 | */ |
661 | |
662 | static void *test_thread(void *arg) |
663 | { |
664 | #ifndef DBUG_OFF |
665 | int param= *((int*) arg); |
666 | #endif |
667 | |
668 | my_thread_init(); |
669 | { |
670 | DBUG_ENTER("test_thread" ); |
671 | DBUG_PRINT("enter" , ("param: %d" , param)); |
672 | |
673 | if (!simple_read_write_test() || |
674 | !simple_read_change_write_read_test() || |
675 | !simple_pin_test() || |
676 | !simple_pin_test2() || |
677 | !simple_pin_no_lock_test() || |
678 | !simple_delete_forget_test() || |
679 | !simple_delete_flush_test()) |
680 | exit(1); |
681 | |
682 | SKIP_BIG_TESTS(4) |
683 | { |
684 | if (!simple_big_test()) |
685 | exit(1); |
686 | } |
687 | |
688 | DBUG_PRINT("info" , ("Thread %s ended\n" , my_thread_name())); |
689 | pthread_mutex_lock(&LOCK_thread_count); |
690 | thread_count--; |
691 | pthread_cond_signal(&COND_thread_count); /* Tell main we are ready */ |
692 | pthread_mutex_unlock(&LOCK_thread_count); |
693 | free((uchar*) arg); |
694 | my_thread_end(); |
695 | DBUG_RETURN(0); |
696 | } |
697 | } |
698 | |
699 | |
700 | static char *create_tmpdir(const char *progname) |
701 | { |
702 | static char test_dirname[FN_REFLEN]; |
703 | char tmp_name[FN_REFLEN]; |
704 | size_t length; |
705 | |
706 | /* Create a temporary directory of name TMP-'executable', but without the -t extension */ |
707 | fn_format(tmp_name, progname, "" , "" , MY_REPLACE_DIR | MY_REPLACE_EXT); |
708 | length= strlen(tmp_name); |
709 | if (length > 2 && tmp_name[length-2] == '-' && tmp_name[length-1] == 't') |
710 | tmp_name[length-2]= 0; |
711 | strxmov(test_dirname, "TMP-" , tmp_name, NullS); |
712 | |
713 | /* |
714 | Don't give an error if we can't create dir, as it may already exist from a previously aborted |
715 | run |
716 | */ |
717 | (void) my_mkdir(test_dirname, 0777, MYF(0)); |
718 | return test_dirname; |
719 | } |
720 | |
721 | |
722 | int main(int argc __attribute__((unused)), |
723 | char **argv __attribute__((unused))) |
724 | { |
725 | pthread_t tid; |
726 | pthread_attr_t thr_attr; |
727 | int *param, error; |
728 | size_t pagen; |
729 | File tmp_file; |
730 | MY_INIT(argv[0]); |
731 | |
732 | #ifndef DBUG_OFF |
733 | #if defined(__WIN__) |
734 | default_dbug_option= "d:t:i:O,\\test_pagecache_single.trace" ; |
735 | #else |
736 | default_dbug_option= "d:t:i:o,/tmp/test_pagecache_single.trace" ; |
737 | #endif |
738 | if (argc > 1) |
739 | { |
740 | DBUG_SET(default_dbug_option); |
741 | DBUG_SET_INITIAL(default_dbug_option); |
742 | } |
743 | #endif |
744 | { |
745 | DBUG_ENTER("main" ); |
746 | DBUG_PRINT("info" , ("Main thread: %s\n" , my_thread_name())); |
747 | |
748 | plan(18); |
749 | SKIP_BIG_TESTS(18) |
750 | { |
751 | char *test_dirname= create_tmpdir(argv[0]); |
752 | fn_format(file1_name, base_file1_name, test_dirname, "" , MYF(0)); |
753 | fn_format(file2_name, base_file2_name, test_dirname, "" , MYF(0)); |
754 | |
755 | if ((tmp_file= my_open(file2_name, O_CREAT | O_TRUNC | O_RDWR, |
756 | MYF(MY_WME))) < 0) |
757 | exit(1); |
758 | |
759 | if ((file1.file= my_open(file1_name, |
760 | O_CREAT | O_TRUNC | O_RDWR, MYF(0))) == -1) |
761 | { |
762 | fprintf(stderr, "Got error during file1 creation from open() (errno: %d)\n" , |
763 | errno); |
764 | exit(1); |
765 | } |
766 | pagecache_file_set_null_hooks(&file1); |
767 | my_close(tmp_file, MYF(0)); |
768 | my_delete(file2_name, MYF(0)); |
769 | |
770 | DBUG_PRINT("info" , ("file1: %d" , file1.file)); |
771 | if (my_chmod(file1_name, 0777, MYF(MY_WME))) |
772 | exit(1); |
773 | my_pwrite(file1.file, (const uchar*)"test file" , 9, 0, MYF(MY_WME)); |
774 | |
775 | if ((error= pthread_cond_init(&COND_thread_count, NULL))) |
776 | { |
777 | fprintf(stderr, "Got error: %d from pthread_cond_init (errno: %d)\n" , |
778 | error, errno); |
779 | exit(1); |
780 | } |
781 | if ((error= pthread_mutex_init(&LOCK_thread_count, MY_MUTEX_INIT_FAST))) |
782 | { |
783 | fprintf(stderr, "Got error: %d from pthread_cond_init (errno: %d)\n" , |
784 | error, errno); |
785 | exit(1); |
786 | } |
787 | |
788 | if ((error= pthread_attr_init(&thr_attr))) |
789 | { |
790 | fprintf(stderr,"Got error: %d from pthread_attr_init (errno: %d)\n" , |
791 | error,errno); |
792 | exit(1); |
793 | } |
794 | if ((error= pthread_attr_setdetachstate(&thr_attr, PTHREAD_CREATE_DETACHED))) |
795 | { |
796 | fprintf(stderr, |
797 | "Got error: %d from pthread_attr_setdetachstate (errno: %d)\n" , |
798 | error,errno); |
799 | exit(1); |
800 | } |
801 | |
802 | #ifdef HAVE_THR_SETCONCURRENCY |
803 | thr_setconcurrency(2); |
804 | #endif |
805 | |
806 | if ((pagen= init_pagecache(&pagecache, PCACHE_SIZE, 0, 0, |
807 | TEST_PAGE_SIZE, 0, MYF(MY_WME))) == 0) |
808 | { |
809 | fprintf(stderr,"Got error: init_pagecache() (errno: %d)\n" , |
810 | errno); |
811 | exit(1); |
812 | } |
813 | DBUG_PRINT("info" , ("Page cache %zd pages" , pagen)); |
814 | |
815 | pthread_mutex_lock(&LOCK_thread_count); |
816 | param=(int*) malloc(sizeof(int)); |
817 | *param= 1; |
818 | if ((error= pthread_create(&tid, &thr_attr, test_thread, (void*) param))) |
819 | { |
820 | fprintf(stderr,"Got error: %d from pthread_create (errno: %d)\n" , |
821 | error,errno); |
822 | exit(1); |
823 | } |
824 | thread_count++; |
825 | DBUG_PRINT("info" , ("Thread started" )); |
826 | pthread_mutex_unlock(&LOCK_thread_count); |
827 | |
828 | pthread_attr_destroy(&thr_attr); |
829 | |
830 | pthread_mutex_lock(&LOCK_thread_count); |
831 | while (thread_count) |
832 | { |
833 | if ((error= pthread_cond_wait(&COND_thread_count,&LOCK_thread_count))) |
834 | fprintf(stderr,"Got error: %d from pthread_cond_wait\n" ,error); |
835 | } |
836 | pthread_mutex_unlock(&LOCK_thread_count); |
837 | DBUG_PRINT("info" , ("thread ended" )); |
838 | |
839 | end_pagecache(&pagecache, 1); |
840 | DBUG_PRINT("info" , ("Page cache ended" )); |
841 | |
842 | if (my_close(file1.file, MYF(MY_WME))) |
843 | exit(1); |
844 | |
845 | my_delete(file1_name, MYF(0)); |
846 | rmdir(test_dirname); |
847 | |
848 | } /* SKIP_BIG_TESTS */ |
849 | DBUG_PRINT("info" , ("file1 (%d) closed" , file1.file)); |
850 | DBUG_PRINT("info" , ("Program end" )); |
851 | |
852 | my_end(0); |
853 | } |
854 | return exit_status(); |
855 | } |
856 | |
857 | #include "../ma_check_standalone.h" |
858 | |