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 = <.m_cmp;
107
108 #define do_verify() \
109 do { verify_fn.saw_the_other = false; locktree_iterate<verify_fn_obj>(<, &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 = ⦥
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 = ⦥
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 = ⦥
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 = ⦥
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