forked from scylladb/seastar
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathalloc_failure_injector.hh
126 lines (107 loc) · 3.35 KB
/
alloc_failure_injector.hh
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
/*
* This file is open source software, licensed to you under the terms
* of the Apache License, Version 2.0 (the "License"). See the NOTICE file
* distributed with this work for additional information regarding copyright
* ownership. You may not use this file except in compliance with the License.
*
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
/*
* Copyright 2017 ScyllaDB
*/
#pragma once
#include <limits>
#include <cstdint>
#include <functional>
#include "noncopyable_function.hh"
namespace seastar {
namespace memory {
///
/// Allocation failure injection framework. Allows testing for exception safety.
///
/// To exhaustively inject failure at every allocation point:
///
/// uint64_t i = 0;
/// while (true) {
/// try {
/// local_failure_injector().fail_after(i++);
/// code_under_test();
/// local_failure_injector().cancel();
/// break;
/// } catch (const std::bad_alloc&) {
/// // expected
/// }
/// }
///
class alloc_failure_injector {
uint64_t _alloc_count;
uint64_t _fail_at = std::numeric_limits<uint64_t>::max();
noncopyable_function<void()> _on_alloc_failure = [] { throw std::bad_alloc(); };
bool _failed;
uint64_t _suppressed = 0;
friend class disable_failure_guard;
private:
void fail();
public:
void on_alloc_point() {
if (_suppressed) {
return;
}
if (_alloc_count >= _fail_at) {
fail();
}
++_alloc_count;
}
// Counts encountered allocation points which didn't fail and didn't have failure suppressed
uint64_t alloc_count() const {
return _alloc_count;
}
// Will cause count-th allocation point from now to fail, counting from 0
void fail_after(uint64_t count) {
_fail_at = _alloc_count + count;
_failed = false;
}
// Cancels the failure scheduled by fail_after()
void cancel() {
_fail_at = std::numeric_limits<uint64_t>::max();
}
// Returns true iff allocation was failed since last fail_after()
bool failed() const {
return _failed;
}
// Runs given function with a custom failure action instead of the default std::bad_alloc throw.
void run_with_callback(noncopyable_function<void()> callback, noncopyable_function<void()> to_run);
};
extern thread_local alloc_failure_injector the_alloc_failure_injector;
inline
alloc_failure_injector& local_failure_injector() {
return the_alloc_failure_injector;
}
#ifdef SEASTAR_ENABLE_ALLOC_FAILURE_INJECTION
struct disable_failure_guard {
disable_failure_guard() { ++local_failure_injector()._suppressed; }
~disable_failure_guard() { --local_failure_injector()._suppressed; }
};
#else
struct disable_failure_guard {
~disable_failure_guard() {}
};
#endif
// Marks a point in code which should be considered for failure injection
inline
void on_alloc_point() {
#ifdef SEASTAR_ENABLE_ALLOC_FAILURE_INJECTION
local_failure_injector().on_alloc_point();
#endif
}
}
}