]> git.proxmox.com Git - ceph.git/blob - ceph/src/seastar/include/seastar/util/alloc_failure_injector.hh
import quincy beta 17.1.0
[ceph.git] / ceph / src / seastar / include / seastar / util / alloc_failure_injector.hh
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 * Copyright 2017 ScyllaDB
20 */
21
22 #pragma once
23
24 #include <limits>
25 #include <cstdint>
26 #include <functional>
27 #include <seastar/util/noncopyable_function.hh>
28 #include <seastar/util/critical_alloc_section.hh>
29
30 namespace seastar {
31 namespace memory {
32
33 ///
34 /// Allocation failure injection framework. Allows testing for exception safety.
35 ///
36 /// To exhaustively inject failure at every allocation point:
37 ///
38 /// uint64_t i = 0;
39 /// while (true) {
40 /// try {
41 /// local_failure_injector().fail_after(i++);
42 /// code_under_test();
43 /// local_failure_injector().cancel();
44 /// break;
45 /// } catch (const std::bad_alloc&) {
46 /// // expected
47 /// }
48 /// }
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(); };
53 bool _failed = false;
54 private:
55 void fail();
56 public:
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()) {
60 return;
61 }
62 if (_alloc_count >= _fail_at) {
63 fail();
64 }
65 ++_alloc_count;
66 }
67
68 /// Counts encountered allocation points which didn't fail and didn't have failure suppressed.
69 uint64_t alloc_count() const {
70 return _alloc_count;
71 }
72
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;
76 _failed = false;
77 }
78
79 /// Cancels the failure scheduled by fail_after().
80 void cancel() {
81 _fail_at = std::numeric_limits<uint64_t>::max();
82 }
83
84 /// Returns true iff allocation was failed since last fail_after().
85 bool failed() const {
86 return _failed;
87 }
88
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);
91 };
92
93 /// \cond internal
94 extern thread_local alloc_failure_injector the_alloc_failure_injector;
95 /// \endcond
96
97 /// \brief Return the shard-local \ref alloc_failure_injector instance.
98 inline
99 alloc_failure_injector& local_failure_injector() {
100 return the_alloc_failure_injector;
101 }
102
103 #ifdef SEASTAR_ENABLE_ALLOC_FAILURE_INJECTION
104
105 #ifdef SEASTAR_DEFAULT_ALLOCATOR
106 #error SEASTAR_ENABLE_ALLOC_FAILURE_INJECTION is not supported when using SEASTAR_DEFAULT_ALLOCATOR
107 #endif
108
109 #endif
110
111
112 struct [[deprecated("Use scoped_critical_section instead")]] disable_failure_guard {
113 scoped_critical_alloc_section cs;
114 };
115
116 /// \brief Marks a point in code which should be considered for failure injection.
117 inline
118 void on_alloc_point() {
119 #ifdef SEASTAR_ENABLE_ALLOC_FAILURE_INJECTION
120 local_failure_injector().on_alloc_point();
121 #endif
122 }
123
124 /// Repeatedly run func with allocation failures
125 ///
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);
130
131 }
132 }