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 that if a node is pinned by a thread
45 // doing get_and_pin_nonblocking while another thread is trying
46 // to unpin_and_remove it, that nothing bad happens.
47 //
48
49 CACHEFILE f1;
50 PAIR p1;
51 PAIR p2;
52
53
54 static int
fetch_one(CACHEFILE f,PAIR UU (p),int UU (fd),CACHEKEY k,uint32_t fullhash,void ** value,void ** dd,PAIR_ATTR * sizep,int * dirtyp,void * extraargs)55 fetch_one(CACHEFILE f __attribute__((__unused__)),
56 PAIR UU(p),
57 int UU(fd),
58 CACHEKEY k __attribute__((__unused__)),
59 uint32_t fullhash __attribute__((__unused__)),
60 void **value __attribute__((__unused__)),
61 void **dd __attribute__((__unused__)),
62 PAIR_ATTR *sizep __attribute__((__unused__)),
63 int *dirtyp,
64 void *extraargs __attribute__((__unused__))
65 ) {
66 *dirtyp = 0;
67 *value = NULL;
68 *sizep = make_pair_attr(8);
69 assert(k.b == 1);
70 p1 = p;
71 return 0;
72 }
73
74 static int
fetch_two(CACHEFILE f,PAIR UU (p),int UU (fd),CACHEKEY k,uint32_t fullhash,void ** value,void ** dd,PAIR_ATTR * sizep,int * dirtyp,void * extraargs)75 fetch_two (CACHEFILE f __attribute__((__unused__)),
76 PAIR UU(p),
77 int UU(fd),
78 CACHEKEY k __attribute__((__unused__)),
79 uint32_t fullhash __attribute__((__unused__)),
80 void **value __attribute__((__unused__)),
81 void **dd __attribute__((__unused__)),
82 PAIR_ATTR *sizep __attribute__((__unused__)),
83 int *dirtyp,
84 void *extraargs __attribute__((__unused__))
85 ) {
86 *dirtyp = 0;
87 *value = NULL;
88 *sizep = make_pair_attr(8);
89 assert(k.b == 2);
90 p2 = p;
91 return 0;
92 }
93
94 toku_pthread_t unpin_and_remove_tid;
95
unpin_and_remove_one(void * UU (arg))96 static void *unpin_and_remove_one(void *UU(arg)) {
97 int r = toku_cachetable_unpin_and_remove(
98 f1,
99 p1,
100 NULL,
101 NULL
102 );
103 assert_zero(r);
104 return arg;
105 }
106
107 static void
unpin_two(void * UU (v))108 unpin_two (void* UU(v)) {
109 int r = toku_cachetable_unpin_ct_prelocked_no_flush(
110 f1,
111 p2,
112 CACHETABLE_DIRTY,
113 make_pair_attr(8)
114 );
115 assert_zero(r);
116
117 // at this point, we have p1 pinned, want to start a thread to do an
118 // unpin_and_remove
119 // on p1
120 r = toku_pthread_create(toku_uninstrumented,
121 &unpin_and_remove_tid,
122 nullptr,
123 unpin_and_remove_one,
124 nullptr);
125 assert_zero(r);
126 // sleep to give a chance for the unpin_and_remove to get going
127 usleep(512*1024);
128 }
129
repin_one(void * UU (arg))130 static void *repin_one(void *UU(arg)) {
131 CACHETABLE_WRITE_CALLBACK wc = def_write_callback(NULL);
132 struct unlockers unlockers = {true, unpin_two, NULL, NULL};
133 void* v1;
134 int r = toku_cachetable_get_and_pin_nonblocking(
135 f1,
136 make_blocknum(1),
137 1,
138 &v1,
139 wc,
140 def_fetch,
141 def_pf_req_callback,
142 def_pf_callback,
143 PL_WRITE_EXPENSIVE,
144 NULL,
145 &unlockers
146 );
147 assert(r == TOKUDB_TRY_AGAIN);
148 return arg;
149 }
150
151
152
153 static void
cachetable_test(void)154 cachetable_test (void) {
155 const int test_limit = 1000;
156 int r;
157 toku_pair_list_set_lock_size(2); // set two bucket mutexes
158 CACHETABLE ct;
159 toku_cachetable_create(&ct, test_limit, ZERO_LSN, nullptr);
160 const char *fname1 = TOKU_TEST_FILENAME;
161 unlink(fname1);
162 r = toku_cachetable_openf(&f1, ct, fname1, O_RDWR|O_CREAT, S_IRWXU|S_IRWXG|S_IRWXO); assert(r == 0);
163
164 void* v1;
165 CACHETABLE_WRITE_CALLBACK wc = def_write_callback(NULL);
166
167 // bring pairs 1 and 2 into memory, then unpin
168 r = toku_cachetable_get_and_pin(f1, make_blocknum(1), 1, &v1, wc, fetch_one, def_pf_req_callback, def_pf_callback, true, NULL);
169 assert_zero(r);
170 r = toku_cachetable_get_and_pin(f1, make_blocknum(2), 2, &v1, wc, fetch_two, def_pf_req_callback, def_pf_callback, true, NULL);
171 assert_zero(r);
172
173 toku_pthread_t tid1;
174 r = toku_pthread_create(
175 toku_uninstrumented, &tid1, nullptr, repin_one, nullptr);
176 assert_zero(r);
177
178 void *ret;
179 r = toku_pthread_join(tid1, &ret);
180 assert_zero(r);
181 r = toku_pthread_join(unpin_and_remove_tid, &ret);
182 assert_zero(r);
183
184 toku_cachetable_verify(ct);
185 toku_cachefile_close(&f1, false, ZERO_LSN);
186 toku_cachetable_close(&ct);
187 }
188
189 int
test_main(int argc,const char * argv[])190 test_main(int argc, const char *argv[]) {
191 default_parse_args(argc, argv);
192 // test ought to run bunch of times in hope of hitting bug
193 uint32_t num_test_runs = 1;
194 for (uint32_t i = 0; i < num_test_runs; i++) {
195 if (verbose) {
196 printf("starting test run %" PRIu32 " \n", i);
197 }
198 cachetable_test();
199 }
200 return 0;
201 }
202