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
19 * Copyright 2017 ScyllaDB
27 #include <seastar/util/noncopyable_function.hh>
28 #include <seastar/util/critical_alloc_section.hh>
34 /// Allocation failure injection framework. Allows testing for exception safety.
36 /// To exhaustively inject failure at every allocation point:
41 /// local_failure_injector().fail_after(i++);
42 /// code_under_test();
43 /// local_failure_injector().cancel();
45 /// } catch (const std::bad_alloc&) {
49 class alloc_failure_injector {
50 uint64_t _alloc_count = 0;
51 uint64_t _fail_at = std::numeric_limits<uint64_t>::max();
52 noncopyable_function<void()> _on_alloc_failure = [] { throw std::bad_alloc(); };
57 /// \brief Marks a point in code which should be considered for failure injection.
58 void on_alloc_point() {
59 if (is_critical_alloc_section()) {
62 if (_alloc_count >= _fail_at) {
68 /// Counts encountered allocation points which didn't fail and didn't have failure suppressed.
69 uint64_t alloc_count() const {
73 /// Will cause count-th allocation point from now to fail, counting from 0.
74 void fail_after(uint64_t count) {
75 _fail_at = _alloc_count + count;
79 /// Cancels the failure scheduled by fail_after().
81 _fail_at = std::numeric_limits<uint64_t>::max();
84 /// Returns true iff allocation was failed since last fail_after().
89 /// Runs given function with a custom failure action instead of the default std::bad_alloc throw.
90 void run_with_callback(noncopyable_function<void()> callback, noncopyable_function<void()> to_run);
94 extern thread_local alloc_failure_injector the_alloc_failure_injector;
97 /// \brief Return the shard-local \ref alloc_failure_injector instance.
99 alloc_failure_injector& local_failure_injector() {
100 return the_alloc_failure_injector;
103 #ifdef SEASTAR_ENABLE_ALLOC_FAILURE_INJECTION
105 #ifdef SEASTAR_DEFAULT_ALLOCATOR
106 #error SEASTAR_ENABLE_ALLOC_FAILURE_INJECTION is not supported when using SEASTAR_DEFAULT_ALLOCATOR
112 struct [[deprecated("Use scoped_critical_section instead")]] disable_failure_guard {
113 scoped_critical_alloc_section cs;
116 /// \brief Marks a point in code which should be considered for failure injection.
118 void on_alloc_point() {
119 #ifdef SEASTAR_ENABLE_ALLOC_FAILURE_INJECTION
120 local_failure_injector().on_alloc_point();
124 /// Repeatedly run func with allocation failures
126 /// Initially, allocations start to fail immediately. In each
127 /// subsequent run the failures start one allocation later. This
128 /// returns when func is run and no allocation failures are detected.
129 void with_allocation_failures(noncopyable_function<void()> func);