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