xref: /6.0.3/forestdb/tests/unit/bcache_test.cc (revision 733d0c43)
1/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2/*
3 *     Copyright 2010 Couchbase, Inc
4 *
5 *   Licensed under the Apache License, Version 2.0 (the "License");
6 *   you may not use this file except in compliance with the License.
7 *   You may obtain a copy of the License at
8 *
9 *       http://www.apache.org/licenses/LICENSE-2.0
10 *
11 *   Unless required by applicable law or agreed to in writing, software
12 *   distributed under the License is distributed on an "AS IS" BASIS,
13 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 *   See the License for the specific language governing permissions and
15 *   limitations under the License.
16 */
17
18#include <stdio.h>
19#include <stdlib.h>
20#include <string.h>
21
22#include "test.h"
23#include "blockcache.h"
24#include "filemgr.h"
25#include "filemgr_ops.h"
26#include "crc32.h"
27
28#include "memleak.h"
29
30#undef THREAD_SANITIZER
31#if __clang__
32#   if defined(__has_feature) && __has_feature(thread_sanitizer)
33#define THREAD_SANITIZER
34#   endif
35#endif
36
37void basic_test()
38{
39    TEST_INIT();
40
41    struct filemgr *file;
42    struct filemgr_config config;
43    int i;
44    uint8_t buf[4096];
45    char *fname = (char *) "./bcache_testfile";
46
47    memset(&config, 0, sizeof(config));
48    config.blocksize = 4096;
49    config.ncacheblock = 5;
50    config.options = FILEMGR_CREATE;
51    config.num_wal_shards = 8;
52    filemgr_open_result result = filemgr_open(fname, get_filemgr_ops(), &config, NULL);
53    file = result.file;
54
55    for (i=0;i<5;++i) {
56        filemgr_alloc(file, NULL);
57        filemgr_write(file, i, buf, NULL);
58    }
59    filemgr_commit(file, true, NULL);
60    for (i=5;i<10;++i) {
61        filemgr_alloc(file, NULL);
62        filemgr_write(file, i, buf, NULL);
63    }
64    filemgr_commit(file, true, NULL);
65
66    filemgr_read(file, 8, buf, NULL, true);
67    filemgr_read(file, 9, buf, NULL, true);
68
69    filemgr_read(file, 1, buf, NULL, true);
70    filemgr_read(file, 2, buf, NULL, true);
71    filemgr_read(file, 3, buf, NULL, true);
72
73    filemgr_read(file, 7, buf, NULL, true);
74    filemgr_read(file, 1, buf, NULL, true);
75    filemgr_read(file, 9, buf, NULL, true);
76
77    filemgr_alloc(file, NULL);
78    filemgr_write(file, 10, buf, NULL);
79
80    TEST_RESULT("basic test");
81}
82
83void basic_test2()
84{
85    TEST_INIT();
86
87    struct filemgr *file;
88    struct filemgr_config config;
89    int i;
90    uint8_t buf[4096];
91    char *fname = (char *) "./bcache_testfile";
92    int r;
93    r = system(SHELL_DEL " bcache_testfile");
94    (void)r;
95
96    memset(&config, 0, sizeof(config));
97    config.blocksize = 4096;
98    config.ncacheblock = 5;
99    config.flag = 0x0;
100    config.options = FILEMGR_CREATE;
101    config.num_wal_shards = 8;
102    filemgr_open_result result = filemgr_open(fname, get_filemgr_ops(), &config, NULL);
103    file = result.file;
104
105    for (i=0;i<5;++i) {
106        filemgr_alloc(file, NULL);
107        filemgr_write(file, i, buf, NULL);
108    }
109    for (i=5;i<10;++i) {
110        filemgr_alloc(file, NULL);
111        filemgr_write(file, i, buf, NULL);
112    }
113    filemgr_commit(file, true, NULL);
114    filemgr_close(file, true, NULL, NULL);
115    filemgr_shutdown();
116
117    TEST_RESULT("basic test");
118
119}
120
121struct worker_args{
122    size_t n;
123    struct filemgr *file;
124    size_t writer;
125    size_t nblocks;
126    size_t time_sec;
127};
128
129void * worker(void *voidargs)
130{
131    uint8_t *buf = (uint8_t *)malloc(4096);
132    struct worker_args *args = (struct worker_args*)voidargs;
133    struct timeval ts_begin, ts_cur, ts_gap;
134
135    ssize_t ret;
136    bid_t bid;
137    uint32_t crc, crc_file;
138    uint64_t i, c, run_count=0;
139    TEST_INIT();
140
141    memset(buf, 0, 4096);
142    gettimeofday(&ts_begin, NULL);
143
144    while(1) {
145        bid = rand() % args->nblocks;
146        ret = bcache_read(args->file, bid, buf);
147        if (ret <= 0) {
148            ret = args->file->ops->pread(args->file->fd, buf,
149                                         args->file->blocksize, bid * args->file->blocksize);
150            TEST_CHK(ret == (ssize_t)args->file->blocksize);
151            ret = bcache_write(args->file, bid, buf, BCACHE_REQ_CLEAN, false);
152            TEST_CHK(ret == (ssize_t)args->file->blocksize);
153        }
154        crc_file = crc32_8(buf, sizeof(uint64_t)*2, 0);
155        (void)crc_file;
156        memcpy(&i, buf, sizeof(i));
157        memcpy(&crc, buf + sizeof(uint64_t)*2, sizeof(crc));
158        // Disable checking the CRC value at this time as pread and pwrite are
159        // not thread-safe.
160        // TEST_CHK(crc == crc_file && i==bid);
161        //DBG("%d %d %d %x %x\n", (int)args->n, (int)i, (int)bid, (int)crc, (int)crc_file);
162
163        if (args->writer) {
164            memcpy(&c, buf+sizeof(i), sizeof(c));
165            c++;
166            memcpy(buf+sizeof(i), &c, sizeof(c));
167            crc = crc32_8(buf, sizeof(uint64_t)*2, 0);
168            memcpy(buf + sizeof(uint64_t)*2, &crc, sizeof(crc));
169
170            ret = bcache_write(args->file, bid, buf, BCACHE_REQ_DIRTY, true);
171            TEST_CHK(ret == (ssize_t)args->file->blocksize);
172        } else { // have some of the reader threads flush dirty immutable blocks
173            if (bid <= args->nblocks / 4) { // 25% probability
174                filemgr_flush_immutable(args->file, NULL);
175            }
176        }
177
178        gettimeofday(&ts_cur, NULL);
179        ts_gap = _utime_gap(ts_begin, ts_cur);
180        if ((size_t)ts_gap.tv_sec >= args->time_sec) break;
181
182        run_count++;
183    }
184
185    free(buf);
186    thread_exit(0);
187    return NULL;
188}
189
190void multi_thread_test(
191    int nblocks, int cachesize, int blocksize, int time_sec, int nwriters, int nreaders)
192{
193    TEST_INIT();
194
195    struct filemgr *file;
196    struct filemgr_config config;
197    int n = nwriters + nreaders;
198    uint64_t i, j;
199    uint32_t crc;
200    uint8_t *buf;
201    int r;
202    char *fname = (char *) "./bcache_testfile";
203    thread_t *tid = alca(thread_t, n);
204    struct worker_args *args = alca(struct worker_args, n);
205    void **ret = alca(void *, n);
206
207    r = system(SHELL_DEL " bcache_testfile");
208    (void)r;
209
210    memleak_start();
211
212    buf = (uint8_t *)malloc(4096);
213    memset(buf, 0, 4096);
214
215    memset(&config, 0, sizeof(config));
216    config.blocksize = blocksize;
217    config.ncacheblock = cachesize;
218    config.flag = 0x0;
219    config.options = FILEMGR_CREATE;
220    config.num_wal_shards = 8;
221    filemgr_open_result result = filemgr_open(fname, get_filemgr_ops(), &config, NULL);
222    file = result.file;
223
224    for (i=0;i<(uint64_t)nblocks;++i) {
225        memcpy(buf, &i, sizeof(i));
226        j = 0;
227        memcpy(buf + sizeof(i), &j, sizeof(j));
228        crc = crc32_8(buf, sizeof(i) + sizeof(j), 0);
229        memcpy(buf + sizeof(i) + sizeof(j), &crc, sizeof(crc));
230        bcache_write(file, (bid_t)i, buf, BCACHE_REQ_DIRTY, false);
231    }
232
233    for (i=0;i<(uint64_t)n;++i){
234        args[i].n = i;
235        args[i].file = file;
236        args[i].writer = ((i<(uint64_t)nwriters)?(1):(0));
237        args[i].nblocks = nblocks;
238        args[i].time_sec = time_sec;
239        thread_create(&tid[i], worker, &args[i]);
240    }
241
242    DBG("wait for %d seconds..\n", time_sec);
243    for (i=0;i<(uint64_t)n;++i){
244        thread_join(tid[i], &ret[i]);
245    }
246
247    filemgr_commit(file, true, NULL);
248    filemgr_close(file, true, NULL, NULL);
249    filemgr_shutdown();
250    free(buf);
251
252    memleak_end();
253    TEST_RESULT("multi thread test");
254}
255
256int main()
257{
258    basic_test2();
259#if !defined(THREAD_SANITIZER)
260    /**
261     * The following tests will be disabled when the code is run with
262     * thread sanitizer, because they point out a data race in writing/
263     * reading from a dirty block which will not happen in reality.
264     *
265     * The bcache partition lock is release iff a given dirty block has
266     * already been marked as immutable. These unit tests attempt to
267     * write to the same immutable block again causing this race. In
268     * reality, this won't happen as these operations go through
269     * filemgr_read() and filemgr_write().
270     */
271    multi_thread_test(4, 1, 32, 20, 1, 7);
272    multi_thread_test(100, 1, 32, 10, 1, 7);
273#endif
274
275    return 0;
276}
277