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 "locktree_unit_test.h"
40 
41 namespace toku {
42 
43 // test that the same txn can relock ranges it already owns
44 // ensure that existing read locks can be upgrading to
45 // write locks if overlapping and ensure that existing read
46 // or write locks are consolidated by overlapping relocks.
test_overlapping_relock(void)47 void locktree_unit_test::test_overlapping_relock(void) {
48     locktree lt;
49 
50     DICTIONARY_ID dict_id = { 1 };
51     lt.create(nullptr, dict_id, dbt_comparator);
52 
53     const DBT *zero = get_dbt(0);
54     const DBT *one = get_dbt(1);
55     const DBT *two = get_dbt(2);
56     const DBT *three = get_dbt(3);
57     const DBT *four = get_dbt(4);
58     const DBT *five = get_dbt(5);
59 
60     int r;
61     TXNID txnid_a = 1001;
62 
63     // because of the single txnid optimization, there is no consolidation
64     // of read or write ranges until there is at least two txnids in
65     // the locktree. so here we add some arbitrary txnid to get a point
66     // lock [100, 100] so that the test below can expect to actually
67     // do something. at the end of the test, we release 100, 100.
68     const TXNID the_other_txnid = 9999;
69     const DBT *hundred = get_dbt(100);
70     r = lt.acquire_write_lock(the_other_txnid, hundred, hundred, nullptr, false);
71     invariant(r == 0);
72 
73     for (int test_run = 0; test_run < 2; test_run++) {
74         // test_run == 0 means test with read lock
75         // test_run == 1 means test with write lock
76 #define ACQUIRE_LOCK(txn, left, right, conflicts) \
77         test_run == 0 ? lt.acquire_read_lock(txn, left, right, conflicts, false) \
78             : lt.acquire_write_lock(txn, left, right, conflicts, false)
79 
80         // lock [1,1] and [2,2]. then lock [1,2].
81         // ensure only [1,2] exists in the tree
82         r = ACQUIRE_LOCK(txnid_a, one, one, nullptr);
83         invariant(r == 0);
84         r = ACQUIRE_LOCK(txnid_a, two, two, nullptr);
85         invariant(r == 0);
86         r = ACQUIRE_LOCK(txnid_a, one, two, nullptr);
87         invariant(r == 0);
88 
89         struct verify_fn_obj {
90             bool saw_the_other;
91             TXNID expected_txnid;
92             keyrange *expected_range;
93             const comparator *cmp;
94             bool fn(const keyrange &range, TXNID txnid) {
95                 if (txnid == the_other_txnid) {
96                     invariant(!saw_the_other);
97                     saw_the_other = true;
98                     return true;
99                 }
100                 invariant(txnid == expected_txnid);
101                 keyrange::comparison c = range.compare(*cmp, *expected_range);
102                 invariant(c == keyrange::comparison::EQUALS);
103                 return true;
104             }
105         } verify_fn;
106         verify_fn.cmp = &lt.m_cmp;
107 
108 #define do_verify() \
109         do { verify_fn.saw_the_other = false; locktree_iterate<verify_fn_obj>(&lt, &verify_fn); } while (0)
110 
111         keyrange range;
112         range.create(one, two);
113         verify_fn.expected_txnid = txnid_a;
114         verify_fn.expected_range = &range;
115         do_verify();
116 
117         // unlocking [1,1] should remove the only range,
118         // the other unlocks shoudl do nothing.
119         lt.remove_overlapping_locks_for_txnid(txnid_a, one, one);
120         lt.remove_overlapping_locks_for_txnid(txnid_a, two, two);
121         lt.remove_overlapping_locks_for_txnid(txnid_a, one, two);
122 
123         // try overlapping from the right
124         r = ACQUIRE_LOCK(txnid_a, one, three, nullptr);
125         r = ACQUIRE_LOCK(txnid_a, two, five, nullptr);
126         verify_fn.expected_txnid = txnid_a;
127         range.create(one, five);
128         verify_fn.expected_range = &range;
129         do_verify();
130 
131         // now overlap from the left
132         r = ACQUIRE_LOCK(txnid_a, zero, four, nullptr);
133         verify_fn.expected_txnid = txnid_a;
134         range.create(zero, five);
135         verify_fn.expected_range = &range;
136         do_verify();
137 
138         // now relock in a range that is already dominated
139         r = ACQUIRE_LOCK(txnid_a, five, five, nullptr);
140         verify_fn.expected_txnid = txnid_a;
141         range.create(zero, five);
142         verify_fn.expected_range = &range;
143         do_verify();
144 
145         // release one of the locks we acquired. this should clean up the whole range.
146         lt.remove_overlapping_locks_for_txnid(txnid_a, zero, four);
147 
148 #undef ACQUIRE_LOCK
149     }
150 
151     // remove the other txnid's lock now
152     lt.remove_overlapping_locks_for_txnid(the_other_txnid, hundred, hundred);
153 
154     lt.release_reference();
155     lt.destroy();
156 }
157 
158 } /* namespace toku */
159 
main(void)160 int main(void) {
161     toku::locktree_unit_test test;
162     test.test_overlapping_relock();
163     return 0;
164 }
165