40
40
unused_results,
41
41
clippy:: pedantic
42
42
) ] // from https://github.com/rust-unofficial/patterns/blob/master/anti_patterns/deny-warnings.md
43
- #![ allow( ) ]
43
+ #![ allow(
44
+ clippy:: result_unit_err,
45
+ clippy:: let_underscore_untyped,
46
+ clippy:: missing_errors_doc
47
+ ) ]
44
48
45
49
#[ cfg( feature = "nightly" ) ]
46
50
use std:: alloc:: { Alloc , AllocErr , CannotReallocInPlace } ;
@@ -54,6 +58,10 @@ pub struct Cap<H> {
54
58
allocator : H ,
55
59
remaining : AtomicUsize ,
56
60
limit : AtomicUsize ,
61
+ #[ cfg( feature = "stats" ) ]
62
+ total_allocated : AtomicUsize ,
63
+ #[ cfg( feature = "stats" ) ]
64
+ max_allocated : AtomicUsize ,
57
65
}
58
66
59
67
impl < H > Cap < H > {
@@ -65,6 +73,10 @@ impl<H> Cap<H> {
65
73
allocator,
66
74
remaining : AtomicUsize :: new ( limit) ,
67
75
limit : AtomicUsize :: new ( limit) ,
76
+ #[ cfg( feature = "stats" ) ]
77
+ total_allocated : AtomicUsize :: new ( 0 ) ,
78
+ #[ cfg( feature = "stats" ) ]
79
+ max_allocated : AtomicUsize :: new ( 0 ) ,
68
80
}
69
81
}
70
82
@@ -101,16 +113,16 @@ impl<H> Cap<H> {
101
113
}
102
114
if self
103
115
. limit
104
- . compare_and_swap ( limit_old, limit, Ordering :: Relaxed )
105
- != limit_old
116
+ . compare_exchange ( limit_old, limit, Ordering :: Relaxed , Ordering :: Relaxed )
117
+ . is_err ( )
106
118
{
107
119
continue ;
108
120
}
109
121
} else {
110
122
if self
111
123
. limit
112
- . compare_and_swap ( limit_old, limit, Ordering :: Relaxed )
113
- != limit_old
124
+ . compare_exchange ( limit_old, limit, Ordering :: Relaxed , Ordering :: Relaxed )
125
+ . is_err ( )
114
126
{
115
127
continue ;
116
128
}
@@ -134,6 +146,34 @@ impl<H> Cap<H> {
134
146
}
135
147
}
136
148
}
149
+
150
+ /// Get total amount of allocated memory. This includes already deallocated memory.
151
+ #[ cfg( feature = "stats" ) ]
152
+ pub fn total_allocated ( & self ) -> usize {
153
+ self . total_allocated . load ( Ordering :: Relaxed )
154
+ }
155
+
156
+ /// Get maximum amount of memory that was allocated at any point in time.
157
+ #[ cfg( feature = "stats" ) ]
158
+ pub fn max_allocated ( & self ) -> usize {
159
+ self . max_allocated . load ( Ordering :: Relaxed )
160
+ }
161
+
162
+ fn update_stats ( & self , size : usize ) {
163
+ #[ cfg( feature = "stats" ) ]
164
+ {
165
+ let _ = self . total_allocated . fetch_add ( size, Ordering :: Relaxed ) ;
166
+ // If max_allocated is less than currently allocated, then it will be updated to limit - remaining.
167
+ // Otherwise, it will remain unchanged.
168
+ let _ = self
169
+ . max_allocated
170
+ . fetch_max ( self . allocated ( ) , Ordering :: Relaxed ) ;
171
+ }
172
+ #[ cfg( not( feature = "stats" ) ) ]
173
+ {
174
+ let _ = ( self , size) ;
175
+ }
176
+ }
137
177
}
138
178
139
179
unsafe impl < H > GlobalAlloc for Cap < H >
@@ -149,6 +189,8 @@ where
149
189
} ;
150
190
if res. is_null ( ) {
151
191
let _ = self . remaining . fetch_add ( size, Ordering :: Release ) ;
192
+ } else {
193
+ self . update_stats ( size) ;
152
194
}
153
195
res
154
196
}
@@ -166,13 +208,15 @@ where
166
208
} ;
167
209
if res. is_null ( ) {
168
210
let _ = self . remaining . fetch_add ( size, Ordering :: Release ) ;
211
+ } else {
212
+ self . update_stats ( size) ;
169
213
}
170
214
res
171
215
}
172
216
unsafe fn realloc ( & self , ptr : * mut u8 , old_l : Layout , new_s : usize ) -> * mut u8 {
173
217
let new_l = Layout :: from_size_align_unchecked ( new_s, old_l. align ( ) ) ;
174
218
let ( old_size, new_size) = ( old_l. size ( ) , new_l. size ( ) ) ;
175
- if new_size > old_size {
219
+ let res = if new_size > old_size {
176
220
let res = if self
177
221
. remaining
178
222
. fetch_sub ( new_size - old_size, Ordering :: Acquire )
@@ -195,8 +239,13 @@ where
195
239
. remaining
196
240
. fetch_add ( old_size - new_size, Ordering :: Release ) ;
197
241
}
242
+ // Although this might just deaalocate, I will still update the stats as if it allocates to be on "the safe side"
198
243
res
244
+ } ;
245
+ if !res. is_null ( ) {
246
+ self . update_stats ( new_size) ;
199
247
}
248
+ res
200
249
}
201
250
}
202
251
@@ -214,6 +263,8 @@ where
214
263
} ;
215
264
if res. is_err ( ) {
216
265
let _ = self . remaining . fetch_add ( size, Ordering :: Release ) ;
266
+ } else {
267
+ self . update_stats ( size) ;
217
268
}
218
269
res
219
270
}
@@ -233,7 +284,7 @@ where
233
284
self . allocator . usable_size ( & old_l) . 1 ,
234
285
self . allocator . usable_size ( & new_l) . 1 ,
235
286
) ;
236
- if new_size > old_size {
287
+ let res = if new_size > old_size {
237
288
let res = if self
238
289
. remaining
239
290
. fetch_sub ( new_size - old_size, Ordering :: Acquire )
@@ -257,7 +308,11 @@ where
257
308
. fetch_add ( old_size - new_size, Ordering :: Release ) ;
258
309
}
259
310
res
311
+ } ;
312
+ if res. is_ok ( ) {
313
+ self . update_stats ( new_size) ;
260
314
}
315
+ res
261
316
}
262
317
unsafe fn alloc_zeroed ( & mut self , l : Layout ) -> Result < ptr:: NonNull < u8 > , AllocErr > {
263
318
let size = self . allocator . usable_size ( & l) . 1 ;
@@ -268,6 +323,8 @@ where
268
323
} ;
269
324
if res. is_err ( ) {
270
325
let _ = self . remaining . fetch_add ( size, Ordering :: Release ) ;
326
+ } else {
327
+ self . update_stats ( size) ;
271
328
}
272
329
res
273
330
}
@@ -292,6 +349,8 @@ where
292
349
let _ = self
293
350
. remaining
294
351
. fetch_add ( new_size - old_size, Ordering :: Release ) ;
352
+ } else {
353
+ self . update_stats ( new_size - old_size) ;
295
354
}
296
355
res
297
356
}
@@ -357,26 +416,63 @@ mod tests {
357
416
. into_iter ( )
358
417
. for_each ( |thread| thread. join ( ) . unwrap ( ) ) ;
359
418
let allocated2 = A . allocated ( ) ;
419
+ #[ cfg( feature = "stats" ) ]
420
+ let total_allocated = A . total_allocated ( ) ;
360
421
if cfg ! ( all( test, feature = "nightly" ) ) {
361
422
assert_eq ! ( allocated, allocated2) ;
423
+ #[ cfg( feature = "stats" ) ]
424
+ assert ! ( total_allocated >= allocated) ;
362
425
}
363
426
}
427
+ #[ cfg( feature = "stats" ) ]
428
+ assert ! ( A . max_allocated( ) < A . total_allocated( ) ) ;
429
+ }
430
+
431
+ #[ cfg( all( test, not( feature = "nightly" ) ) ) ]
432
+ #[ test]
433
+ fn limit ( ) {
434
+ #[ cfg( feature = "stats" ) ]
435
+ let initial = A . allocated ( ) ;
436
+ let allocate_amount = 30 * 1024 * 1024 ;
437
+ A . set_limit ( A . allocated ( ) + allocate_amount) . unwrap ( ) ;
438
+ for _ in 0 ..10 {
439
+ let mut vec = Vec :: < u8 > :: with_capacity ( 0 ) ;
440
+ if let Err ( _e) = vec. try_reserve_exact ( allocate_amount + 1 ) {
441
+ } else {
442
+ A . set_limit ( usize:: max_value ( ) ) . unwrap ( ) ;
443
+ panic ! ( "{}" , A . remaining( ) ) ;
444
+ } ;
445
+ assert_eq ! ( vec. try_reserve_exact( allocate_amount) , Ok ( ( ) ) ) ;
446
+ let mut vec2 = Vec :: < u8 > :: with_capacity ( 0 ) ;
447
+ assert ! ( vec2. try_reserve_exact( 1 ) . is_err( ) ) ;
448
+ }
449
+ // Might have additional allocations of errors and what not along the way.
450
+ #[ cfg( feature = "stats" ) ]
451
+ {
452
+ assert ! ( A . total_allocated( ) >= initial + 10 * allocate_amount) ;
453
+ assert_eq ! ( A . max_allocated( ) , initial + allocate_amount) ;
454
+ }
364
455
}
365
456
366
457
#[ cfg( all( test, feature = "nightly" ) ) ]
367
458
#[ test]
368
459
fn limit ( ) {
369
- A . set_limit ( A . allocated ( ) + 30 * 1024 * 1024 ) . unwrap ( ) ;
460
+ let allocate_amount = 30 * 1024 * 1024 ;
461
+ A . set_limit ( A . allocated ( ) + allocate_amount) . unwrap ( ) ;
370
462
for _ in 0 ..10 {
371
463
let mut vec = Vec :: < u8 > :: with_capacity ( 0 ) ;
372
464
if let Err ( TryReserveError :: AllocError { .. } ) =
373
- vec. try_reserve_exact ( 30 * 1024 * 1024 + 1 )
465
+ vec. try_reserve_exact ( allocate_amount + 1 )
374
466
{
375
467
} else {
376
468
A . set_limit ( usize:: max_value ( ) ) . unwrap ( ) ;
377
469
panic ! ( "{}" , A . remaining( ) )
378
470
} ;
379
- assert_eq ! ( vec. try_reserve_exact( 30 * 1024 * 1024 ) , Ok ( ( ) ) ) ;
471
+ assert_eq ! ( vec. try_reserve_exact( allocate_amount) , Ok ( ( ) ) ) ;
472
+ let mut vec2 = Vec :: < u8 > :: with_capacity ( 0 ) ;
473
+ assert ! ( vec2. try_reserve_exact( 1 ) . is_err( ) ) ;
380
474
}
475
+ assert_eq ! ( A . total_allocated( ) , 10 * allocate_amount) ;
476
+ assert_eq ! ( A . max_allocated( ) , allocate_amount)
381
477
}
382
478
}
0 commit comments