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