| 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 |  |