1 /* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 // vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
3 #ident "$Id$"
4 /*======
5 This file is part of PerconaFT.
6
7
8 Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
9
10 PerconaFT is free software: you can redistribute it and/or modify
11 it under the terms of the GNU General Public License, version 2,
12 as published by the Free Software Foundation.
13
14 PerconaFT is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
21
22 ----------------------------------------
23
24 PerconaFT is free software: you can redistribute it and/or modify
25 it under the terms of the GNU Affero General Public License, version 3,
26 as published by the Free Software Foundation.
27
28 PerconaFT is distributed in the hope that it will be useful,
29 but WITHOUT ANY WARRANTY; without even the implied warranty of
30 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
31 GNU Affero General Public License for more details.
32
33 You should have received a copy of the GNU Affero General Public License
34 along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
35 ======= */
36
37 #ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
38
39 #include "test.h"
40
41 static void
flush(CACHEFILE f,int UU (fd),CACHEKEY k,void * v,void ** UU (dd),void * e,PAIR_ATTR s,PAIR_ATTR * new_size,bool w,bool keep,bool c,bool UU (is_clone))42 flush (CACHEFILE f __attribute__((__unused__)),
43 int UU(fd),
44 CACHEKEY k __attribute__((__unused__)),
45 void *v __attribute__((__unused__)),
46 void** UU(dd),
47 void *e __attribute__((__unused__)),
48 PAIR_ATTR s __attribute__((__unused__)),
49 PAIR_ATTR* new_size __attribute__((__unused__)),
50 bool w __attribute__((__unused__)),
51 bool keep __attribute__((__unused__)),
52 bool c __attribute__((__unused__)),
53 bool UU(is_clone)
54 ) {
55 if (w) {
56 assert(c);
57 assert(keep);
58 }
59 }
60
kibbutz_work(void * fe_v)61 static void kibbutz_work(void *fe_v)
62 {
63 CACHEFILE CAST_FROM_VOIDP(f1, fe_v);
64 sleep(2);
65 int r = toku_test_cachetable_unpin(f1, make_blocknum(1), 1, CACHETABLE_CLEAN, make_pair_attr(8));
66 assert(r==0);
67 remove_background_job_from_cf(f1);
68 }
69
70 static void
unlock_dummy(void * UU (v))71 unlock_dummy (void* UU(v)) {
72 }
73
reset_unlockers(UNLOCKERS unlockers)74 static void reset_unlockers(UNLOCKERS unlockers) {
75 unlockers->locked = true;
76 }
77
78 static void
run_case_that_should_succeed(CACHEFILE f1,pair_lock_type first_lock,pair_lock_type second_lock)79 run_case_that_should_succeed(CACHEFILE f1, pair_lock_type first_lock, pair_lock_type second_lock) {
80 void* v1;
81 CACHETABLE_WRITE_CALLBACK wc = def_write_callback(NULL);
82 wc.flush_callback = flush;
83 struct unlockers unlockers = {true, unlock_dummy, NULL, NULL};
84 int r = toku_cachetable_get_and_pin_nonblocking(f1, make_blocknum(1), 1, &v1, wc, def_fetch, def_pf_req_callback, def_pf_callback, first_lock, NULL, NULL);
85 assert(r==0);
86 cachefile_kibbutz_enq(f1, kibbutz_work, f1);
87 reset_unlockers(&unlockers);
88 r = toku_cachetable_get_and_pin_nonblocking(f1, make_blocknum(1), 1, &v1, wc, def_fetch, def_pf_req_callback, def_pf_callback, second_lock, NULL, &unlockers);
89 assert(r==0); assert(unlockers.locked);
90 r = toku_test_cachetable_unpin(f1, make_blocknum(1), 1, CACHETABLE_CLEAN, make_pair_attr(8)); assert(r==0);
91 }
92
93 static void
run_case_that_should_fail(CACHEFILE f1,pair_lock_type first_lock,pair_lock_type second_lock)94 run_case_that_should_fail(CACHEFILE f1, pair_lock_type first_lock, pair_lock_type second_lock) {
95 void* v1;
96 CACHETABLE_WRITE_CALLBACK wc = def_write_callback(NULL);
97 wc.flush_callback = flush;
98 struct unlockers unlockers = {true, unlock_dummy, NULL, NULL};
99 int r = toku_cachetable_get_and_pin_nonblocking(f1, make_blocknum(1), 1, &v1, wc, def_fetch, def_pf_req_callback, def_pf_callback, first_lock, NULL, NULL);
100 assert(r==0);
101 cachefile_kibbutz_enq(f1, kibbutz_work, f1);
102 reset_unlockers(&unlockers);
103 r = toku_cachetable_get_and_pin_nonblocking(f1, make_blocknum(1), 1, &v1, wc, def_fetch, def_pf_req_callback, def_pf_callback, second_lock, NULL, &unlockers);
104 assert(r == TOKUDB_TRY_AGAIN); assert(!unlockers.locked);
105 }
106
107
108 static void
run_test(void)109 run_test (void) {
110 // sometimes the cachetable evictor runs during the test. this sometimes causes cachetable pair locking contention,
111 // which results with a TOKUDB_TRY_AGAIN error occurring. unfortunately, the test does not expect this and fails.
112 // set cachetable size limit to a value big enough so that the cachetable evictor is not triggered during the test.
113 const int test_limit = 100;
114
115 int r;
116 CACHETABLE ct;
117 toku_cachetable_create(&ct, test_limit, ZERO_LSN, nullptr);
118 const char *fname1 = TOKU_TEST_FILENAME;
119 unlink(fname1);
120 CACHEFILE f1;
121 r = toku_cachetable_openf(&f1, ct, fname1, O_RDWR|O_CREAT, S_IRWXU|S_IRWXG|S_IRWXO); assert(r == 0);
122
123 void* v1;
124 CACHETABLE_WRITE_CALLBACK wc = def_write_callback(NULL);
125 wc.flush_callback = flush;
126 //
127 // test that if we are getting a PAIR for the first time that TOKUDB_TRY_AGAIN is returned
128 // because the PAIR was not in the cachetable.
129 //
130 r = toku_cachetable_get_and_pin_nonblocking(f1, make_blocknum(1), 1, &v1, wc, def_fetch, def_pf_req_callback, def_pf_callback, PL_WRITE_EXPENSIVE, NULL, NULL);
131 assert(r==TOKUDB_TRY_AGAIN);
132
133
134 run_case_that_should_succeed(f1, PL_READ, PL_WRITE_CHEAP);
135 run_case_that_should_succeed(f1, PL_READ, PL_WRITE_EXPENSIVE);
136
137 run_case_that_should_succeed(f1, PL_WRITE_CHEAP, PL_READ);
138 run_case_that_should_succeed(f1, PL_WRITE_CHEAP, PL_WRITE_CHEAP);
139 run_case_that_should_succeed(f1, PL_WRITE_CHEAP, PL_WRITE_EXPENSIVE);
140
141 run_case_that_should_fail(f1, PL_WRITE_EXPENSIVE, PL_READ);
142 run_case_that_should_fail(f1, PL_WRITE_EXPENSIVE, PL_WRITE_CHEAP);
143 run_case_that_should_fail(f1, PL_WRITE_EXPENSIVE, PL_WRITE_EXPENSIVE);
144
145 toku_cachetable_verify(ct);
146 toku_cachefile_close(&f1, false, ZERO_LSN);
147 toku_cachetable_close(&ct);
148 }
149
150 int
test_main(int argc,const char * argv[])151 test_main(int argc, const char *argv[]) {
152 default_parse_args(argc, argv);
153 run_test();
154 return 0;
155 }
156