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 CACHEFILE f1;
42 CACHEFILE f2;
43
44 bool check_flush;
45 bool dirty_flush_called;
46 bool check_pe_callback;
47 bool pe_callback_called;
48 bool enable_partial_eviction;
49
50 CACHETABLE ct;
51
52 static int
pe_callback(void * ftnode_pv,PAIR_ATTR bytes_to_free,void * extraargs,void (* finalize)(PAIR_ATTR bytes_freed,void * extra),void * finalize_extra)53 pe_callback (
54 void *ftnode_pv __attribute__((__unused__)),
55 PAIR_ATTR bytes_to_free __attribute__((__unused__)),
56 void* extraargs __attribute__((__unused__)),
57 void (*finalize)(PAIR_ATTR bytes_freed, void *extra),
58 void *finalize_extra
59 )
60 {
61 if (check_pe_callback) {
62 pe_callback_called = true;
63 }
64 usleep(4*1024*1024);
65 finalize(make_pair_attr(1), finalize_extra);
66 return 0;
67 }
68
69 static void
flush(CACHEFILE f,int UU (fd),CACHEKEY k,void * v,void ** dd,void * e,PAIR_ATTR s,PAIR_ATTR * new_size,bool w,bool keep,bool c,bool UU (is_clone))70 flush (CACHEFILE f __attribute__((__unused__)),
71 int UU(fd),
72 CACHEKEY k __attribute__((__unused__)),
73 void *v __attribute__((__unused__)),
74 void **dd __attribute__((__unused__)),
75 void *e __attribute__((__unused__)),
76 PAIR_ATTR s __attribute__((__unused__)),
77 PAIR_ATTR* new_size __attribute__((__unused__)),
78 bool w __attribute__((__unused__)),
79 bool keep __attribute__((__unused__)),
80 bool c __attribute__((__unused__)),
81 bool UU(is_clone)
82 ) {
83 if (check_flush && w) {
84 dirty_flush_called = true;
85 }
86 }
87
f2_pin(void * arg)88 static void *f2_pin(void *arg) {
89 int r;
90 void* v1;
91 CACHETABLE_WRITE_CALLBACK wc = def_write_callback(NULL);
92 //
93 // these booleans for pe_callback just ensure that the
94 // test is working as we expect it to. We expect the get_and_pin to
95 // cause a partial eviction of f1's PAIR, reducing its size from 8 to 1
96 // and we expect that to be enough so that the unpin does not invoke a partial eviction
97 // This is just to ensure that the bug is being exercised
98 //
99 check_pe_callback = true;
100 r = toku_cachetable_get_and_pin(f2, make_blocknum(1), 1, &v1, wc, def_fetch, def_pf_req_callback, def_pf_callback, true, NULL);
101 assert(r == 0);
102 ct->ev.signal_eviction_thread();
103 usleep(1*1024*1024);
104 if (enable_partial_eviction)
105 assert(pe_callback_called);
106 else
107 assert(!pe_callback_called);
108 pe_callback_called = false;
109 r = toku_test_cachetable_unpin(f2, make_blocknum(1), 1, CACHETABLE_CLEAN, make_pair_attr(8));
110 check_pe_callback = false;
111 assert(!pe_callback_called);
112 assert(r == 0);
113
114 return arg;
115 }
116
117 static void
cachetable_test(void)118 cachetable_test (void) {
119 const int test_limit = 12;
120 int r;
121 check_flush = false;
122 dirty_flush_called = false;
123
124 toku_cachetable_create(&ct, test_limit, ZERO_LSN, nullptr);
125 evictor_test_helpers::disable_ev_thread(&ct->ev); // disable eviction thread
126
127 toku_set_enable_partial_eviction(ct, enable_partial_eviction);
128
129 toku_os_recursive_delete(TOKU_TEST_FILENAME);
130 r = toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU);
131 assert_zero(r);
132 char fname1[TOKU_PATH_MAX + 1];
133 unlink(toku_path_join(fname1, 2, TOKU_TEST_FILENAME, "test1.dat"));
134 char fname2[TOKU_PATH_MAX + 1];
135 unlink(toku_path_join(fname2, 2, TOKU_TEST_FILENAME, "test2.dat"));
136
137 r = toku_cachetable_openf(&f1, ct, fname1, O_RDWR|O_CREAT, S_IRWXU|S_IRWXG|S_IRWXO);
138 assert(r == 0);
139 r = toku_cachetable_openf(&f2, ct, fname2, O_RDWR|O_CREAT, S_IRWXU|S_IRWXG|S_IRWXO);
140 assert(r == 0);
141
142 void* v1;
143 CACHETABLE_WRITE_CALLBACK wc = def_write_callback(NULL);
144 wc.pe_callback = pe_callback;
145 wc.flush_callback = flush;
146 // pin and unpin a node 20 times, just to get clock count up
147 for (int i = 0; i < 20; i++) {
148 r = toku_cachetable_get_and_pin(f1, make_blocknum(1), 1, &v1, wc, def_fetch, def_pf_req_callback, def_pf_callback, true, NULL);
149 assert(r == 0);
150 r = toku_test_cachetable_unpin(f1, make_blocknum(1), 1, CACHETABLE_DIRTY, make_pair_attr(8));
151 assert(r == 0);
152 }
153
154 // at this point, we have a dirty PAIR in the cachetable associated with
155 // cachefile f1
156 // launch a thread that will put another PAIR in the cachetable, and get
157 // partial eviction started
158 toku_pthread_t tid;
159 r = toku_pthread_create(
160 toku_uninstrumented, &tid, nullptr, f2_pin, nullptr);
161 assert_zero(r);
162
163 usleep(2 * 1024 * 1024);
164 check_flush = true;
165 toku_cachefile_close(&f1, false, ZERO_LSN);
166 if (enable_partial_eviction)
167 assert(dirty_flush_called);
168 else
169 assert(!dirty_flush_called);
170 check_flush = false;
171
172 void *ret;
173 r = toku_pthread_join(tid, &ret);
174 assert_zero(r);
175
176
177 toku_cachetable_verify(ct);
178 toku_cachefile_close(&f2, false, ZERO_LSN);
179 toku_cachetable_close(&ct);
180 }
181
182 int
test_main(int argc,const char * argv[])183 test_main(int argc, const char *argv[]) {
184 if (argc!=2) { printf("argcount should be 2.\n"); exit(1); }
185 if (strcmp(argv[1], "enable_pe") == 0) {
186 enable_partial_eviction = true;
187 } else {
188 enable_partial_eviction = false;
189 }
190 cachetable_test();
191 return 0;
192 }
193