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
42
43 //
44 // This test verifies the behavior that originally caused
45 // #5978 is fixed. Here is what we do. We have four pairs with
46 // blocknums and fullhashes of 1,2,3,4. The cachetable has only
47 // two bucket mutexes, so 1 and 3 share a pair mutex, as do 2 and 4.
48 // We pin all four with expensive write locks. Then, on background threads,
49 // we call get_and_pin_nonblocking on 3, where the unlockers unpins 2, and
50 // we call get_and_pin_nonblocking on 4, where the unlockers unpins 1. Run this
51 // enough times, and we should see a deadlock before the fix, and no deadlock
52 // after the fix.
53 //
54
55 CACHEFILE f1;
56 PAIR p3;
57 PAIR p4;
58
59
60 static int
fetch_three(CACHEFILE f,PAIR UU (p),int UU (fd),CACHEKEY k,uint32_t fullhash,void ** value,void ** dd,PAIR_ATTR * sizep,int * dirtyp,void * extraargs)61 fetch_three (CACHEFILE f __attribute__((__unused__)),
62 PAIR UU(p),
63 int UU(fd),
64 CACHEKEY k __attribute__((__unused__)),
65 uint32_t fullhash __attribute__((__unused__)),
66 void **value __attribute__((__unused__)),
67 void **dd __attribute__((__unused__)),
68 PAIR_ATTR *sizep __attribute__((__unused__)),
69 int *dirtyp,
70 void *extraargs __attribute__((__unused__))
71 ) {
72 *dirtyp = 0;
73 *value = NULL;
74 *sizep = make_pair_attr(8);
75 assert(k.b == 3);
76 p3 = p;
77 return 0;
78 }
79
80 static int
fetch_four(CACHEFILE f,PAIR UU (p),int UU (fd),CACHEKEY k,uint32_t fullhash,void ** value,void ** dd,PAIR_ATTR * sizep,int * dirtyp,void * extraargs)81 fetch_four (CACHEFILE f __attribute__((__unused__)),
82 PAIR UU(p),
83 int UU(fd),
84 CACHEKEY k __attribute__((__unused__)),
85 uint32_t fullhash __attribute__((__unused__)),
86 void **value __attribute__((__unused__)),
87 void **dd __attribute__((__unused__)),
88 PAIR_ATTR *sizep __attribute__((__unused__)),
89 int *dirtyp,
90 void *extraargs __attribute__((__unused__))
91 ) {
92 *dirtyp = 0;
93 *value = NULL;
94 *sizep = make_pair_attr(8);
95 assert(k.b == 4);
96 p4 = p;
97 return 0;
98 }
99
100
101
102 static void
unpin_four(void * UU (v))103 unpin_four (void* UU(v)) {
104 int r = toku_cachetable_unpin_ct_prelocked_no_flush(
105 f1,
106 p3,
107 CACHETABLE_DIRTY,
108 make_pair_attr(8)
109 );
110 assert_zero(r);
111 }
112
113 static void
unpin_three(void * UU (v))114 unpin_three (void* UU(v)) {
115 int r = toku_cachetable_unpin_ct_prelocked_no_flush(
116 f1,
117 p4,
118 CACHETABLE_DIRTY,
119 make_pair_attr(8)
120 );
121 assert_zero(r);
122 }
123
repin_one(void * UU (arg))124 static void *repin_one(void *UU(arg)) {
125 CACHETABLE_WRITE_CALLBACK wc = def_write_callback(NULL);
126 struct unlockers unlockers = {true, unpin_four, NULL, NULL};
127 void* v1;
128 int r = toku_cachetable_get_and_pin_nonblocking(
129 f1,
130 make_blocknum(1),
131 1,
132 &v1,
133 wc,
134 def_fetch,
135 def_pf_req_callback,
136 def_pf_callback,
137 PL_WRITE_EXPENSIVE,
138 NULL,
139 &unlockers
140 );
141 assert(r == TOKUDB_TRY_AGAIN);
142 return arg;
143 }
144
145
repin_two(void * UU (arg))146 static void *repin_two(void *UU(arg)) {
147 CACHETABLE_WRITE_CALLBACK wc = def_write_callback(NULL);
148 struct unlockers unlockers = {true, unpin_three, NULL, NULL};
149 void* v1;
150 int r = toku_cachetable_get_and_pin_nonblocking(
151 f1,
152 make_blocknum(2),
153 2,
154 &v1,
155 wc,
156 def_fetch,
157 def_pf_req_callback,
158 def_pf_callback,
159 PL_WRITE_EXPENSIVE,
160 NULL,
161 &unlockers
162 );
163 assert(r == TOKUDB_TRY_AGAIN);
164 return arg;
165 }
166
167
168 static void
cachetable_test(void)169 cachetable_test (void) {
170 const int test_limit = 1000;
171 int r;
172 toku_pair_list_set_lock_size(2); // set two bucket mutexes
173 CACHETABLE ct;
174 toku_cachetable_create(&ct, test_limit, ZERO_LSN, nullptr);
175 const char *fname1 = TOKU_TEST_FILENAME;
176 unlink(fname1);
177 r = toku_cachetable_openf(&f1, ct, fname1, O_RDWR|O_CREAT, S_IRWXU|S_IRWXG|S_IRWXO); assert(r == 0);
178
179 void* v1;
180 CACHETABLE_WRITE_CALLBACK wc = def_write_callback(NULL);
181
182 // bring pairs 1 and 2 into memory, then unpin
183 r = toku_cachetable_get_and_pin(f1, make_blocknum(1), 1, &v1, wc, def_fetch, def_pf_req_callback, def_pf_callback, true, NULL);
184 assert_zero(r);
185 r = toku_cachetable_get_and_pin(f1, make_blocknum(2), 2, &v1, wc, def_fetch, def_pf_req_callback, def_pf_callback, true, NULL);
186 assert_zero(r);
187
188
189 // now pin pairs 3 and 4
190 r = toku_cachetable_get_and_pin(f1, make_blocknum(3), 3, &v1, wc, fetch_three, def_pf_req_callback, def_pf_callback, true, NULL);
191 assert_zero(r);
192 r = toku_cachetable_get_and_pin(f1, make_blocknum(4), 4, &v1, wc, fetch_four, def_pf_req_callback, def_pf_callback, true, NULL);
193 assert_zero(r);
194
195 toku_pthread_t tid1;
196 toku_pthread_t tid2;
197 r = toku_pthread_create(
198 toku_uninstrumented, &tid1, nullptr, repin_one, nullptr);
199 assert_zero(r);
200 r = toku_pthread_create(
201 toku_uninstrumented, &tid2, nullptr, repin_two, nullptr);
202 assert_zero(r);
203
204 // unpin 1 and 2 so tid1 and tid2 can make progress
205 usleep(512*1024);
206 r = toku_test_cachetable_unpin(f1, make_blocknum(1), 1, CACHETABLE_DIRTY, make_pair_attr(8));
207 assert_zero(r);
208 r = toku_test_cachetable_unpin(f1, make_blocknum(2), 2, CACHETABLE_DIRTY, make_pair_attr(8));
209 assert_zero(r);
210
211
212 void *ret;
213 r = toku_pthread_join(tid1, &ret);
214 assert_zero(r);
215 r = toku_pthread_join(tid2, &ret);
216 assert_zero(r);
217
218 toku_cachetable_verify(ct);
219 toku_cachefile_close(&f1, false, ZERO_LSN);
220 toku_cachetable_close(&ct);
221 }
222
223 int
test_main(int argc,const char * argv[])224 test_main(int argc, const char *argv[]) {
225 default_parse_args(argc, argv);
226 // test ought to run bunch of times in hope of hitting bug
227 uint32_t num_test_runs = 30;
228 for (uint32_t i = 0; i < num_test_runs; i++) {
229 if (verbose) {
230 printf("starting test run %" PRIu32 " \n", i);
231 }
232 cachetable_test();
233 }
234 return 0;
235 }
236