Browse Source

Add an `is_active` function, and remove panic on not using

Bradlee Speice 5 months ago
parent
commit
8838070a1b
4 changed files with 94 additions and 29 deletions
  1. 1
    0
      .gitignore
  2. 89
    18
      src/lib.rs
  3. 4
    0
      tests/inactive.rs
  4. 0
    11
      tests/unused_panic.rs

+ 1
- 0
.gitignore View File

@@ -1,3 +1,4 @@
1 1
 /target
2 2
 **/*.rs.bk
3 3
 Cargo.lock
4
+*.swp

+ 89
- 18
src/lib.rs View File

@@ -17,11 +17,17 @@
17 17
 //! Actually making use of QADAPT is straight-forward. To set up the allocator,
18 18
 //! place the following snippet in either your program binaries (main.rs) or tests:
19 19
 //!
20
-//! ```rust,ignore
20
+//! ```rust,no_run
21 21
 //! use qadapt::QADAPT;
22 22
 //!
23 23
 //! #[global_allocator]
24 24
 //! static Q: QADAPT = QADAPT;
25
+//!
26
+//! fn main() {
27
+//!     if cfg!(debug_assertions) {
28
+//!         assert!(qadapt::is_active());
29
+//!     }
30
+//! }
25 31
 //! ```
26 32
 //!
27 33
 //! After that, there are two ways of telling QADAPT that it should trigger a panic:
@@ -29,6 +35,11 @@
29 35
 //! 1. Annotate functions with the `#[no_alloc]` proc macro:
30 36
 //! ```rust,no_run
31 37
 //! use qadapt::no_alloc;
38
+//! use qadapt::QADAPT;
39
+//! use std::panic::catch_unwind;
40
+//!
41
+//! #[global_allocator]
42
+//! static Q: QADAPT = QADAPT;
32 43
 //!
33 44
 //! // This function is fine, there are no allocations here
34 45
 //! #[no_alloc]
@@ -44,15 +55,21 @@
44 55
 //!
45 56
 //! fn main() {
46 57
 //!     do_math();
47
-//!     does_panic();
58
+//!
59
+//!     let err = catch_unwind(|| does_panic());
60
+//!     assert!(err.is_err());
48 61
 //! }
49 62
 //! ```
50 63
 //!
51 64
 //! 2. Evaluate expressions with the `assert_no_alloc!` macro
52
-//! ```rust,no_run
65
+//! ```rust
53 66
 //! use qadapt::assert_no_alloc;
67
+//! use qadapt::QADAPT;
68
+//!
69
+//! #[global_allocator]
70
+//! static Q: QADAPT = QADAPT;
54 71
 //!
55
-//! fn do_work() {
72
+//! fn main() {
56 73
 //!     // This code is allowed to trigger an allocation
57 74
 //!     let b = Box::new(8);
58 75
 //!     
@@ -60,6 +77,7 @@
60 77
 //!     let x = assert_no_alloc!(*b + 2);
61 78
 //!     assert_eq!(x, 10);
62 79
 //! }
80
+//! ```
63 81
 #![deny(missing_docs)]
64 82
 
65 83
 // thread_id is necessary because `std::thread::current()` panics if we have not yet
@@ -84,11 +102,17 @@ thread_local! {
84 102
 /// To make use of the allocator, include this code block in your program
85 103
 /// binaries/tests:
86 104
 ///
87
-/// ```rust,ignore
105
+/// ```rust,no_run
88 106
 /// use qadapt::QADAPT;
89 107
 ///
90 108
 /// #[global_allocator]
91 109
 /// static Q: QADAPT = QADAPT;
110
+///
111
+/// fn main() {
112
+///     if cfg!(debug_assertions) {
113
+///         assert!(qadapt::is_active());
114
+///     }
115
+/// }
92 116
 /// ```
93 117
 pub struct QADAPT;
94 118
 
@@ -99,9 +123,13 @@ static SYSTEM_ALLOC: System = System;
99 123
 ///
100 124
 /// **Example**:
101 125
 ///
102
-/// ```rust,no_run
126
+/// ```rust
103 127
 /// use qadapt::enter_protected;
104 128
 /// use qadapt::exit_protected;
129
+/// use qadapt::QADAPT;
130
+///
131
+/// #[global_allocator]
132
+/// static Q: QADAPT = QADAPT;
105 133
 ///
106 134
 /// fn main() {
107 135
 ///     // Force an allocation by using a Box
@@ -119,14 +147,10 @@ static SYSTEM_ALLOC: System = System;
119 147
 pub fn enter_protected() {
120 148
     #[cfg(debug_assertions)]
121 149
     {
122
-        if thread::panicking() {
150
+        if thread::panicking() || !is_active() {
123 151
             return;
124 152
         }
125 153
 
126
-        if !*IS_ACTIVE.read() {
127
-            panic!("QADAPT not initialized when using allocation guards; please verify `#[global_allocator]` is set!");
128
-        }
129
-
130 154
         PROTECTION_LEVEL
131 155
             .try_with(|v| {
132 156
                 *v.write() += 1;
@@ -140,9 +164,13 @@ pub fn enter_protected() {
140 164
 ///
141 165
 /// **Example**:
142 166
 ///
143
-/// ```rust,no_run
167
+/// ```rust
144 168
 /// use qadapt::enter_protected;
145 169
 /// use qadapt::exit_protected;
170
+/// use qadapt::QADAPT;
171
+///
172
+/// #[global_allocator]
173
+/// static Q: QADAPT = QADAPT;
146 174
 ///
147 175
 /// fn main() {
148 176
 ///     // Force an allocation by using a Box
@@ -160,7 +188,7 @@ pub fn enter_protected() {
160 188
 pub fn exit_protected() {
161 189
     #[cfg(debug_assertions)]
162 190
     {
163
-        if thread::panicking() {
191
+        if thread::panicking() || !is_active() {
164 192
             return;
165 193
         }
166 194
 
@@ -183,8 +211,12 @@ pub fn exit_protected() {
183 211
 ///
184 212
 /// **Example**:
185 213
 ///
186
-/// ```rust,no_run
214
+/// ```rust
187 215
 /// use qadapt::assert_no_alloc;
216
+/// use qadapt::QADAPT;
217
+///
218
+/// #[global_allocator]
219
+/// static Q: QADAPT = QADAPT;
188 220
 ///
189 221
 /// fn main() {
190 222
 ///     assert_no_alloc!(2 + 2);
@@ -198,6 +230,11 @@ pub fn exit_protected() {
198 230
 ///
199 231
 /// ```rust,no_run
200 232
 /// use qadapt::assert_no_alloc;
233
+/// use qadapt::QADAPT;
234
+/// use std::panic::catch_unwind;
235
+///
236
+/// #[global_allocator]
237
+/// static Q: QADAPT = QADAPT;
201 238
 ///
202 239
 /// fn early_return() -> usize {
203 240
 ///     assert_no_alloc!(return 8);
@@ -206,10 +243,12 @@ pub fn exit_protected() {
206 243
 /// fn main() {
207 244
 ///     let x = early_return();
208 245
 ///     
209
-///     // This triggers a panic - `Box::new` forces an allocation,
210
-///     // and QADAPT still thinks we're in a protected region because
211
-///     // of a return in the `early_return()` function
212
-///     let b = Box::new(x);
246
+///     // Even though only the `early_return` function contains
247
+///     // QADAPT allocation guards, this triggers a panic:
248
+///     // `Box::new` forces an allocation, and QADAPT still thinks
249
+///     // we're in a protected region because of the return in  `early_return()`
250
+///     let res = catch_unwind(|| Box::new(x));
251
+///     assert!(res.is_err());
213 252
 /// }
214 253
 #[macro_export]
215 254
 macro_rules! assert_no_alloc {
@@ -231,8 +270,12 @@ static INTERNAL_ALLOCATION: RwLock<usize> = RwLock::new(usize::max_value());
231 270
 /// ```rust,no_run
232 271
 /// use qadapt::enter_protected;
233 272
 /// use qadapt::exit_protected;
273
+/// use qadapt::QADAPT;
234 274
 /// use qadapt::protection_level;
235 275
 ///
276
+/// #[global_allocator]
277
+/// static Q: QADAPT = QADAPT;
278
+///
236 279
 /// fn main() {
237 280
 ///     enter_protected();
238 281
 ///     // We're now in an allocation-protected code region
@@ -254,6 +297,34 @@ pub fn protection_level() -> usize {
254 297
     }
255 298
 }
256 299
 
300
+/// Determine whether qadapt is running as the current global allocator. Useful for
301
+/// double-checking that you will in fact panic if allocations happen in guarded code.
302
+///
303
+/// **Note**: when running in `release` profile, `is_active()` will always return false.
304
+///
305
+/// **Example**:
306
+///
307
+/// ```rust,no_run
308
+/// use qadapt::is_active;
309
+/// use qadapt::QADAPT;
310
+///
311
+/// #[global_allocator]
312
+/// static Q: QADAPT = QADAPT;
313
+///
314
+/// pub fn main() {
315
+///     if cfg!(debug_assertions) {
316
+///         assert!(is_active());
317
+///     }
318
+/// }
319
+/// ```
320
+pub fn is_active() -> bool {
321
+    if cfg!(debug_assertions) {
322
+        *IS_ACTIVE.read()
323
+    } else {
324
+        false
325
+    }
326
+}
327
+
257 328
 fn claim_internal_alloc() {
258 329
     loop {
259 330
         match INTERNAL_ALLOCATION.write() {

+ 4
- 0
tests/inactive.rs View File

@@ -0,0 +1,4 @@
1
+#[test]
2
+fn is_inactive() {
3
+    assert!(!qadapt::is_active());
4
+}

+ 0
- 11
tests/unused_panic.rs View File

@@ -1,11 +0,0 @@
1
-use qadapt::enter_protected;
2
-
3
-#[test]
4
-#[should_panic]
5
-fn guard_without_initialization() {
6
-    if cfg!(debug_assertions) {
7
-        enter_protected();
8
-    } else {
9
-        panic!("Intentional")
10
-    }
11
-}

Loading…
Cancel
Save