]>
Commit | Line | Data |
---|---|---|
11fdf7f2 TL |
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> | |
f67539c2 | 28 | #include <seastar/util/critical_alloc_section.hh> |
11fdf7f2 TL |
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 | /// | |
f67539c2 TL |
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 | /// } | |
11fdf7f2 TL |
49 | class alloc_failure_injector { |
50 | uint64_t _alloc_count; | |
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; | |
11fdf7f2 TL |
54 | private: |
55 | void fail(); | |
56 | public: | |
f67539c2 | 57 | /// \brief Marks a point in code which should be considered for failure injection. |
11fdf7f2 | 58 | void on_alloc_point() { |
f67539c2 | 59 | if (is_critical_alloc_section()) { |
11fdf7f2 TL |
60 | return; |
61 | } | |
62 | if (_alloc_count >= _fail_at) { | |
63 | fail(); | |
64 | } | |
65 | ++_alloc_count; | |
66 | } | |
67 | ||
f67539c2 | 68 | /// Counts encountered allocation points which didn't fail and didn't have failure suppressed. |
11fdf7f2 TL |
69 | uint64_t alloc_count() const { |
70 | return _alloc_count; | |
71 | } | |
72 | ||
f67539c2 | 73 | /// Will cause count-th allocation point from now to fail, counting from 0. |
11fdf7f2 TL |
74 | void fail_after(uint64_t count) { |
75 | _fail_at = _alloc_count + count; | |
76 | _failed = false; | |
77 | } | |
78 | ||
f67539c2 | 79 | /// Cancels the failure scheduled by fail_after(). |
11fdf7f2 TL |
80 | void cancel() { |
81 | _fail_at = std::numeric_limits<uint64_t>::max(); | |
82 | } | |
83 | ||
f67539c2 | 84 | /// Returns true iff allocation was failed since last fail_after(). |
11fdf7f2 TL |
85 | bool failed() const { |
86 | return _failed; | |
87 | } | |
88 | ||
f67539c2 | 89 | /// Runs given function with a custom failure action instead of the default std::bad_alloc throw. |
11fdf7f2 TL |
90 | void run_with_callback(noncopyable_function<void()> callback, noncopyable_function<void()> to_run); |
91 | }; | |
92 | ||
f67539c2 | 93 | /// \cond internal |
11fdf7f2 | 94 | extern thread_local alloc_failure_injector the_alloc_failure_injector; |
f67539c2 | 95 | /// \endcond |
11fdf7f2 | 96 | |
f67539c2 | 97 | /// \brief Return the shard-local \ref alloc_failure_injector instance. |
11fdf7f2 TL |
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 | ||
f67539c2 TL |
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 | |
11fdf7f2 | 110 | |
11fdf7f2 | 111 | |
f67539c2 TL |
112 | struct [[deprecated("Use scoped_critical_section instead")]] disable_failure_guard { |
113 | scoped_critical_alloc_section cs; | |
11fdf7f2 TL |
114 | }; |
115 | ||
f67539c2 | 116 | /// \brief Marks a point in code which should be considered for failure injection. |
11fdf7f2 TL |
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 | ||
f67539c2 TL |
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 | ||
11fdf7f2 TL |
131 | } |
132 | } |