assert_no_alloc_patched/
lib.rs1#![doc = include_str!("../README.md")]
25
26use std::alloc::{System,GlobalAlloc,Layout};
27use std::cell::Cell;
28
29#[cfg(all(feature = "disable_release", feature = "warn_release"))]
31compile_error!("disable_release cannot be active at the same time with warn_release");
32
33
34#[cfg(not(all(feature = "disable_release", not(debug_assertions))))] thread_local! {
36 static ALLOC_FORBID_COUNT: Cell<u32> = Cell::new(0);
37 static ALLOC_PERMIT_COUNT: Cell<u32> = Cell::new(0);
38
39 #[cfg(any( all(feature="warn_debug", debug_assertions), all(feature="warn_release", not(debug_assertions)) ))]
40 static ALLOC_VIOLATION_COUNT: Cell<u32> = Cell::new(0);
41}
42
43#[cfg(all(feature = "disable_release", not(debug_assertions)))] pub fn assert_no_alloc<T, F: FnOnce() -> T> (func: F) -> T { func()
46}
47
48#[cfg(all(feature = "disable_release", not(debug_assertions)))] pub fn permit_alloc<T, F: FnOnce() -> T> (func: F) -> T { func()
51}
52
53#[cfg(not(all(feature = "disable_release", not(debug_assertions))))] pub fn assert_no_alloc<T, F: FnOnce() -> T> (func: F) -> T {
61 struct Guard;
64 impl Guard {
65 fn new() -> Guard {
66 ALLOC_FORBID_COUNT.with(|c| c.set(c.get()+1));
67 Guard
68 }
69 }
70 impl Drop for Guard {
71 fn drop(&mut self) {
72 ALLOC_FORBID_COUNT.with(|c| c.set(c.get()-1));
73 }
74 }
75
76
77 #[cfg(any( all(feature="warn_debug", debug_assertions), all(feature="warn_release", not(debug_assertions)) ))] let old_violation_count = violation_count();
79
80
81 let guard = Guard::new(); let ret = func();
83 std::mem::drop(guard); #[cfg(any( all(feature="warn_debug", debug_assertions), all(feature="warn_release", not(debug_assertions)) ))] if violation_count() > old_violation_count {
88 eprintln!("Tried to (de)allocate memory in a thread that forbids allocator calls!");
89 }
90
91 return ret;
92}
93
94#[cfg(not(all(feature = "disable_release", not(debug_assertions))))] pub fn permit_alloc<T, F: FnOnce() -> T> (func: F) -> T {
98 struct Guard;
100 impl Guard {
101 fn new() -> Guard {
102 ALLOC_PERMIT_COUNT.with(|c| c.set(c.get()+1));
103 Guard
104 }
105 }
106 impl Drop for Guard {
107 fn drop(&mut self) {
108 ALLOC_PERMIT_COUNT.with(|c| c.set(c.get()-1));
109 }
110 }
111
112 let guard = Guard::new(); let ret = func();
114 std::mem::drop(guard); return ret;
117}
118
119#[cfg(any( all(feature="warn_debug", debug_assertions), all(feature="warn_release", not(debug_assertions)) ))] pub fn violation_count() -> u32 {
124 ALLOC_VIOLATION_COUNT.with(|c| c.get())
125}
126
127#[cfg(any( all(feature="warn_debug", debug_assertions), all(feature="warn_release", not(debug_assertions)) ))] pub fn reset_violation_count() {
132 ALLOC_VIOLATION_COUNT.with(|c| c.set(0));
133}
134
135
136
137
138#[cfg(not(all(feature = "disable_release", not(debug_assertions))))] pub struct AllocDisabler;
150
151#[cfg(not(all(feature = "disable_release", not(debug_assertions))))] impl AllocDisabler {
153 fn check(&self, layout: Layout) {
154 let forbid_count = ALLOC_FORBID_COUNT.with(|f| f.get());
155 let permit_count = ALLOC_PERMIT_COUNT.with(|p| p.get());
156 if forbid_count > 0 && permit_count == 0 {
157 #[cfg(any( all(feature="warn_debug", debug_assertions), all(feature="warn_release", not(debug_assertions)) ))] ALLOC_VIOLATION_COUNT.with(|c| c.set(c.get()+1));
159
160 #[cfg(any( all(not(feature="warn_debug"), debug_assertions), all(not(feature="warn_release"), not(debug_assertions)) ))] {
162 #[cfg(all(feature = "log", feature = "backtrace"))]
163 permit_alloc(|| log::error!("Memory allocation of {} bytes failed from:\n{:?}", layout.size(), backtrace::Backtrace::new()));
164 #[cfg(all(feature = "log", not(feature = "backtrace")))]
165 permit_alloc(|| log::error!("Memory allocation of {} bytes failed", layout.size()));
166
167 #[cfg(all(not(feature = "log"), feature = "backtrace"))]
168 permit_alloc(|| eprintln!("Allocation failure from:\n{:?}", backtrace::Backtrace::new()));
169
170 std::alloc::handle_alloc_error(layout);
173 }
174 }
175 }
176}
177
178#[cfg(not(all(feature = "disable_release", not(debug_assertions))))] unsafe impl GlobalAlloc for AllocDisabler {
180 unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
181 self.check(layout);
182 System.alloc(layout)
183 }
184
185 unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
186 System.dealloc(ptr, layout)
187 }
188}
189
190pub struct PermitDrop<T>(Option<T>);
207
208impl<T> PermitDrop<T> {
209 pub fn new(t: T) -> PermitDrop<T> {
210 permit_alloc(|| {
211 PermitDrop(Some(t))
212 })
213 }
214}
215
216impl<T> std::ops::Deref for PermitDrop<T> {
217 type Target = T;
218 fn deref(&self) -> &T { self.0.as_ref().unwrap() }
219}
220
221impl<T> std::ops::DerefMut for PermitDrop<T> {
222 fn deref_mut(&mut self) -> &mut T { self.0.as_mut().unwrap() }
223}
224
225impl<I: Iterator> Iterator for PermitDrop<I> {
226 type Item = I::Item;
227 fn next(&mut self) -> Option<Self::Item> {
228 (**self).next()
229 }
230}
231
232
233impl<T> Drop for PermitDrop<T> {
234 fn drop(&mut self) {
235 let mut tmp = None;
236 std::mem::swap(&mut tmp, &mut self.0);
237 permit_alloc(|| {
238 std::mem::drop(tmp);
239 });
240 }
241}