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.
7 * You may obtain a copy of the License at
9 * http://www.apache.org/licenses/LICENSE-2.0
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
20 * Copyright (C) 2020 ScyllaDB.
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>
35 using namespace seastar
;
36 using namespace std::chrono_literals
;
38 SEASTAR_THREAD_TEST_CASE(test_rwlock
) {
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();
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();
54 BOOST_REQUIRE(l
.try_write_lock());
55 l
.for_write().unlock();
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 {});
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);
70 return sleep(1ms
).then([&counter
] {
72 BOOST_REQUIRE_EQUAL(counter
, 0u);
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
] {
84 max
= std::max(max
, counter
);
85 return sleep(1ms
).then([&counter
] {
89 }).finally([&counter
, &max
] {
90 BOOST_REQUIRE_EQUAL(counter
, 0u);
91 BOOST_REQUIRE_NE(max
, 0u);
96 SEASTAR_THREAD_TEST_CASE(test_rwlock_failed_func
) {
99 // verify that the rwlock is unlocked when func fails
100 future
<> fut
= with_lock(l
.for_read(), [] {
101 throw std::runtime_error("injected");
103 BOOST_REQUIRE_THROW(fut
.get(), std::runtime_error
);
105 fut
= with_lock(l
.for_write(), [] {
106 throw std::runtime_error("injected");
108 BOOST_REQUIRE_THROW(fut
.get(), std::runtime_error
);
110 BOOST_REQUIRE(l
.try_write_lock());
111 l
.for_write().unlock();
114 SEASTAR_THREAD_TEST_CASE(test_failed_with_lock
) {
116 future
<> lock() noexcept
{
117 return make_exception_future
<>(std::runtime_error("injected"));
119 void unlock() noexcept
{
120 BOOST_REQUIRE(false);
126 // if l.lock() fails neither the function nor l.unlock()
128 BOOST_REQUIRE_THROW(with_lock(l
, [] {
129 BOOST_REQUIRE(false);
130 }).get(), std::runtime_error
);
133 SEASTAR_THREAD_TEST_CASE(test_shared_mutex
) {
137 BOOST_REQUIRE(!sm
.try_lock());
138 BOOST_REQUIRE(!sm
.try_lock_shared());
141 sm
.lock_shared().get();
142 BOOST_REQUIRE(!sm
.try_lock());
143 BOOST_REQUIRE(sm
.try_lock_shared());
144 sm
.lock_shared().get();
149 BOOST_REQUIRE(sm
.try_lock());
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);
159 return sleep(1ms
).then([&counter
] {
161 BOOST_REQUIRE_EQUAL(counter
, 0u);
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
] {
173 max
= std::max(max
, counter
);
174 return sleep(1ms
).then([&counter
] {
178 }).finally([&counter
, &max
] {
179 BOOST_REQUIRE_EQUAL(counter
, 0u);
180 BOOST_REQUIRE_NE(max
, 0u);
185 SEASTAR_THREAD_TEST_CASE(test_shared_mutex_failed_func
) {
188 // verify that the shared_mutex is unlocked when func fails
189 future
<> fut
= with_shared(sm
, [] {
190 throw std::runtime_error("injected");
192 BOOST_REQUIRE_THROW(fut
.get(), std::runtime_error
);
194 fut
= with_lock(sm
, [] {
195 throw std::runtime_error("injected");
197 BOOST_REQUIRE_THROW(fut
.get(), std::runtime_error
);
199 BOOST_REQUIRE(sm
.try_lock());
203 SEASTAR_THREAD_TEST_CASE(test_shared_mutex_throwing_func
) {
207 X(int x_
) noexcept
: x(x_
) {};
209 throw std::runtime_error("X moved");
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
);
217 fut
= with_lock(sm
, [x
= X(0)] {});
218 BOOST_REQUIRE_THROW(fut
.get(), std::runtime_error
);
220 BOOST_REQUIRE(sm
.try_lock());
224 SEASTAR_THREAD_TEST_CASE(test_shared_mutex_failed_lock
) {
225 #ifdef SEASTAR_ENABLE_ALLOC_FAILURE_INJECTION
228 // if l.lock() fails neither the function nor l.unlock()
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
);
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
);
242 seastar::memory::local_failure_injector().cancel();
243 #endif // SEASTAR_ENABLE_ALLOC_FAILURE_INJECTION