]> git.proxmox.com Git - ceph.git/blob - ceph/src/seastar/tests/unit/locking_test.cc
import quincy beta 17.1.0
[ceph.git] / ceph / src / seastar / tests / unit / locking_test.cc
1 /*
2 * This file is open source software, licensed to you under the terms
3 * of the Apache License, Version 2.0 (the "License"). See the NOTICE file
4 * distributed with this work for additional information regarding copyright
5 * ownership. You may not use this file except in compliance with the License.
6 *
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing,
12 * software distributed under the License is distributed on an
13 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14 * KIND, either express or implied. See the License for the
15 * specific language governing permissions and limitations
16 * under the License.
17 */
18
19 /*
20 * Copyright (C) 2020 ScyllaDB.
21 */
22
23 #include <chrono>
24
25 #include <seastar/testing/test_case.hh>
26 #include <seastar/testing/thread_test_case.hh>
27 #include <seastar/core/do_with.hh>
28 #include <seastar/core/loop.hh>
29 #include <seastar/core/sleep.hh>
30 #include <seastar/core/rwlock.hh>
31 #include <seastar/core/shared_mutex.hh>
32 #include <seastar/util/alloc_failure_injector.hh>
33 #include <boost/range/irange.hpp>
34
35 using namespace seastar;
36 using namespace std::chrono_literals;
37
38 SEASTAR_THREAD_TEST_CASE(test_rwlock) {
39 rwlock l;
40
41 l.for_write().lock().get();
42 BOOST_REQUIRE(!l.try_write_lock());
43 BOOST_REQUIRE(!l.try_read_lock());
44 l.for_write().unlock();
45
46 l.for_read().lock().get();
47 BOOST_REQUIRE(!l.try_write_lock());
48 BOOST_REQUIRE(l.try_read_lock());
49 l.for_read().lock().get();
50 l.for_read().unlock();
51 l.for_read().unlock();
52 l.for_read().unlock();
53
54 BOOST_REQUIRE(l.try_write_lock());
55 l.for_write().unlock();
56 }
57
58 SEASTAR_TEST_CASE(test_with_lock_mutable) {
59 return do_with(rwlock(), [](rwlock& l) {
60 return with_lock(l.for_read(), [p = std::make_unique<int>(42)] () mutable {});
61 });
62 }
63
64 SEASTAR_TEST_CASE(test_rwlock_exclusive) {
65 return do_with(rwlock(), unsigned(0), [] (rwlock& l, unsigned& counter) {
66 return parallel_for_each(boost::irange(0, 10), [&l, &counter] (int idx) {
67 return with_lock(l.for_write(), [&counter] {
68 BOOST_REQUIRE_EQUAL(counter, 0u);
69 ++counter;
70 return sleep(1ms).then([&counter] {
71 --counter;
72 BOOST_REQUIRE_EQUAL(counter, 0u);
73 });
74 });
75 });
76 });
77 }
78
79 SEASTAR_TEST_CASE(test_rwlock_shared) {
80 return do_with(rwlock(), unsigned(0), unsigned(0), [] (rwlock& l, unsigned& counter, unsigned& max) {
81 return parallel_for_each(boost::irange(0, 10), [&l, &counter, &max] (int idx) {
82 return with_lock(l.for_read(), [&counter, &max] {
83 ++counter;
84 max = std::max(max, counter);
85 return sleep(1ms).then([&counter] {
86 --counter;
87 });
88 });
89 }).finally([&counter, &max] {
90 BOOST_REQUIRE_EQUAL(counter, 0u);
91 BOOST_REQUIRE_NE(max, 0u);
92 });
93 });
94 }
95
96 SEASTAR_THREAD_TEST_CASE(test_rwlock_failed_func) {
97 rwlock l;
98
99 // verify that the rwlock is unlocked when func fails
100 future<> fut = with_lock(l.for_read(), [] {
101 throw std::runtime_error("injected");
102 });
103 BOOST_REQUIRE_THROW(fut.get(), std::runtime_error);
104
105 fut = with_lock(l.for_write(), [] {
106 throw std::runtime_error("injected");
107 });
108 BOOST_REQUIRE_THROW(fut.get(), std::runtime_error);
109
110 BOOST_REQUIRE(l.try_write_lock());
111 l.for_write().unlock();
112 }
113
114 SEASTAR_THREAD_TEST_CASE(test_failed_with_lock) {
115 struct test_lock {
116 future<> lock() noexcept {
117 return make_exception_future<>(std::runtime_error("injected"));
118 }
119 void unlock() noexcept {
120 BOOST_REQUIRE(false);
121 }
122 };
123
124 test_lock l;
125
126 // if l.lock() fails neither the function nor l.unlock()
127 // should be called.
128 BOOST_REQUIRE_THROW(with_lock(l, [] {
129 BOOST_REQUIRE(false);
130 }).get(), std::runtime_error);
131 }
132
133 SEASTAR_THREAD_TEST_CASE(test_shared_mutex) {
134 shared_mutex sm;
135
136 sm.lock().get();
137 BOOST_REQUIRE(!sm.try_lock());
138 BOOST_REQUIRE(!sm.try_lock_shared());
139 sm.unlock();
140
141 sm.lock_shared().get();
142 BOOST_REQUIRE(!sm.try_lock());
143 BOOST_REQUIRE(sm.try_lock_shared());
144 sm.lock_shared().get();
145 sm.unlock_shared();
146 sm.unlock_shared();
147 sm.unlock_shared();
148
149 BOOST_REQUIRE(sm.try_lock());
150 sm.unlock();
151 }
152
153 SEASTAR_TEST_CASE(test_shared_mutex_exclusive) {
154 return do_with(shared_mutex(), unsigned(0), [] (shared_mutex& sm, unsigned& counter) {
155 return parallel_for_each(boost::irange(0, 10), [&sm, &counter] (int idx) {
156 return with_lock(sm, [&counter] {
157 BOOST_REQUIRE_EQUAL(counter, 0u);
158 ++counter;
159 return sleep(1ms).then([&counter] {
160 --counter;
161 BOOST_REQUIRE_EQUAL(counter, 0u);
162 });
163 });
164 });
165 });
166 }
167
168 SEASTAR_TEST_CASE(test_shared_mutex_shared) {
169 return do_with(shared_mutex(), unsigned(0), unsigned(0), [] (shared_mutex& sm, unsigned& counter, unsigned& max) {
170 return parallel_for_each(boost::irange(0, 10), [&sm, &counter, &max] (int idx) {
171 return with_shared(sm, [&counter, &max] {
172 ++counter;
173 max = std::max(max, counter);
174 return sleep(1ms).then([&counter] {
175 --counter;
176 });
177 });
178 }).finally([&counter, &max] {
179 BOOST_REQUIRE_EQUAL(counter, 0u);
180 BOOST_REQUIRE_NE(max, 0u);
181 });
182 });
183 }
184
185 SEASTAR_THREAD_TEST_CASE(test_shared_mutex_failed_func) {
186 shared_mutex sm;
187
188 // verify that the shared_mutex is unlocked when func fails
189 future<> fut = with_shared(sm, [] {
190 throw std::runtime_error("injected");
191 });
192 BOOST_REQUIRE_THROW(fut.get(), std::runtime_error);
193
194 fut = with_lock(sm, [] {
195 throw std::runtime_error("injected");
196 });
197 BOOST_REQUIRE_THROW(fut.get(), std::runtime_error);
198
199 BOOST_REQUIRE(sm.try_lock());
200 sm.unlock();
201 }
202
203 SEASTAR_THREAD_TEST_CASE(test_shared_mutex_throwing_func) {
204 shared_mutex sm;
205 struct X {
206 int x;
207 X(int x_) noexcept : x(x_) {};
208 X(X&& o) : x(o.x) {
209 throw std::runtime_error("X moved");
210 }
211 };
212
213 // verify that the shared_mutex is unlocked when func move fails
214 future<> fut = with_shared(sm, [x = X(0)] {});
215 BOOST_REQUIRE_THROW(fut.get(), std::runtime_error);
216
217 fut = with_lock(sm, [x = X(0)] {});
218 BOOST_REQUIRE_THROW(fut.get(), std::runtime_error);
219
220 BOOST_REQUIRE(sm.try_lock());
221 sm.unlock();
222 }
223
224 SEASTAR_THREAD_TEST_CASE(test_shared_mutex_failed_lock) {
225 #ifdef SEASTAR_ENABLE_ALLOC_FAILURE_INJECTION
226 shared_mutex sm;
227
228 // if l.lock() fails neither the function nor l.unlock()
229 // should be called.
230 sm.lock().get();
231 seastar::memory::local_failure_injector().fail_after(0);
232 BOOST_REQUIRE_THROW(with_shared(sm, [] {
233 BOOST_REQUIRE(false);
234 }).get(), std::bad_alloc);
235
236 seastar::memory::local_failure_injector().fail_after(0);
237 BOOST_REQUIRE_THROW(with_lock(sm, [] {
238 BOOST_REQUIRE(false);
239 }).get(), std::bad_alloc);
240 sm.unlock();
241
242 seastar::memory::local_failure_injector().cancel();
243 #endif // SEASTAR_ENABLE_ALLOC_FAILURE_INJECTION
244 }