@@ -12,13 +12,19 @@ use core::{
12
12
result:: Result as CoreResult ,
13
13
slice:: from_raw_parts_mut,
14
14
sync:: atomic:: {
15
- AtomicBool , AtomicUsize ,
15
+ AtomicBool , AtomicU8 , AtomicUsize ,
16
16
Ordering :: { AcqRel , Acquire , Release } ,
17
17
} ,
18
18
} ;
19
19
pub use generic_array:: typenum:: consts;
20
20
use generic_array:: { ArrayLength , GenericArray } ;
21
21
22
+ /// Bit to define producer taken.
23
+ const BIT_PRODUCER : u8 = 1 << 0 ;
24
+
25
+ /// Bit to define consumer taken.
26
+ const BIT_CONSUMER : u8 = 1 << 1 ;
27
+
22
28
/// A backing structure for a BBQueue. Can be used to create either
23
29
/// a BBQueue or a split Producer/Consumer pair
24
30
pub struct BBBuffer < N : ArrayLength < u8 > > (
41
47
/// is placed at `static` scope within the `.bss` region, the explicit initialization
42
48
/// will be elided (as it is already performed as part of memory initialization)
43
49
///
44
- /// NOTE: If the `thumbv6` feature is selected, this function takes a short critical section
50
+ /// NOTE: If the `thumbv6` feature is selected, this function takes a short critical section
45
51
/// while splitting.
46
52
///
47
53
/// ```rust
64
70
/// # }
65
71
/// ```
66
72
pub fn try_split ( & ' a self ) -> Result < ( Producer < ' a , N > , Consumer < ' a , N > ) > {
67
- if atomic:: swap ( & self . 0 . already_split , true , AcqRel ) {
73
+ // Set producer/consumer taken bit, error and reset if one was already set
74
+ let prev = self
75
+ . 0
76
+ . split_prod_cons
77
+ . fetch_or ( BIT_PRODUCER | BIT_CONSUMER , AcqRel ) ;
78
+ if prev > 0 {
79
+ self . 0 . split_prod_cons . store ( prev, Release ) ;
68
80
return Err ( Error :: AlreadySplit ) ;
69
81
}
70
82
@@ -101,14 +113,82 @@ where
101
113
/// is placed at `static` scope within the `.bss` region, the explicit initialization
102
114
/// will be elided (as it is already performed as part of memory initialization)
103
115
///
104
- /// NOTE: If the `thumbv6` feature is selected, this function takes a short critical
116
+ /// NOTE: If the `thumbv6` feature is selected, this function takes a short critical
105
117
/// section while splitting.
106
118
pub fn try_split_framed ( & ' a self ) -> Result < ( FrameProducer < ' a , N > , FrameConsumer < ' a , N > ) > {
107
119
let ( producer, consumer) = self . try_split ( ) ?;
108
120
Ok ( ( FrameProducer { producer } , FrameConsumer { consumer } ) )
109
121
}
110
122
111
- /// Attempt to release the Producer and Consumer
123
+ /// Attempt to take a `Producer` from the `BBBuffer` to gain access to the
124
+ /// buffer. If a producer has already been taken, an error will be returned.
125
+ ///
126
+ /// NOTE: When splitting, the underlying buffer will be explicitly initialized
127
+ /// to zero. This may take a measurable amount of time, depending on the size
128
+ /// of the buffer. This is necessary to prevent undefined behavior. If the buffer
129
+ /// is placed at `static` scope within the `.bss` region, the explicit initialization
130
+ /// will be elided (as it is already performed as part of memory initialization)
131
+ ///
132
+ /// NOTE: If the `thumbv6` feature is selected, this function takes a short critical section
133
+ /// while splitting.
134
+ pub fn try_take_producer ( & ' a self ) -> Result < Producer < ' a , N > > {
135
+ // Set producer taken bit, error if already set
136
+ if self . 0 . split_prod_cons . fetch_or ( BIT_PRODUCER , AcqRel ) & BIT_PRODUCER > 0 {
137
+ return Err ( Error :: AlreadySplit ) ;
138
+ }
139
+
140
+ unsafe {
141
+ // TODO: do we need to zero buffer here, like try_split?
142
+ // // Explicitly zero the data to avoid undefined behavior.
143
+ // // This is required, because we hand out references to the buffers,
144
+ // // which mean that creating them as references is technically UB for now
145
+ // let mu_ptr = self.0.buf.get();
146
+ // (*mu_ptr).as_mut_ptr().write_bytes(0u8, 1);
147
+
148
+ let nn1 = NonNull :: new_unchecked ( self as * const _ as * mut _ ) ;
149
+
150
+ Ok ( Producer {
151
+ bbq : nn1,
152
+ pd : PhantomData ,
153
+ } )
154
+ }
155
+ }
156
+
157
+ /// Attempt to take a `Consumer` from the `BBBuffer` to gain access to the
158
+ /// buffer. If a consumer has already been taken, an error will be returned.
159
+ ///
160
+ /// NOTE: When splitting, the underlying buffer will be explicitly initialized
161
+ /// to zero. This may take a measurable amount of time, depending on the size
162
+ /// of the buffer. This is necessary to prevent undefined behavior. If the buffer
163
+ /// is placed at `static` scope within the `.bss` region, the explicit initialization
164
+ /// will be elided (as it is already performed as part of memory initialization)
165
+ ///
166
+ /// NOTE: If the `thumbv6` feature is selected, this function takes a short critical section
167
+ /// while splitting.
168
+ pub fn try_take_consumer ( & ' a self ) -> Result < Consumer < ' a , N > > {
169
+ // Set producer taken bit, error if already set
170
+ if self . 0 . split_prod_cons . fetch_or ( BIT_CONSUMER , AcqRel ) & BIT_CONSUMER > 0 {
171
+ return Err ( Error :: AlreadySplit ) ;
172
+ }
173
+
174
+ unsafe {
175
+ // TODO: do we need to zero buffer here, like try_split?
176
+ // // Explicitly zero the data to avoid undefined behavior.
177
+ // // This is required, because we hand out references to the buffers,
178
+ // // which mean that creating them as references is technically UB for now
179
+ // let mu_ptr = self.0.buf.get();
180
+ // (*mu_ptr).as_mut_ptr().write_bytes(0u8, 1);
181
+
182
+ let nn1 = NonNull :: new_unchecked ( self as * const _ as * mut _ ) ;
183
+
184
+ Ok ( Consumer {
185
+ bbq : nn1,
186
+ pd : PhantomData ,
187
+ } )
188
+ }
189
+ }
190
+
191
+ /// Attempt to release the `Producer` and `Consumer`
112
192
///
113
193
/// This re-initializes the buffer so it may be split in a different mode at a later
114
194
/// time. There must be no read or write grants active, or an error will be returned.
@@ -178,7 +258,7 @@ where
178
258
self . 0 . last . store ( 0 , Release ) ;
179
259
180
260
// Mark the buffer as ready to be split
181
- self . 0 . already_split . store ( false , Release ) ;
261
+ self . 0 . split_prod_cons . store ( 0 , Release ) ;
182
262
183
263
Ok ( ( ) )
184
264
}
@@ -201,6 +281,148 @@ where
201
281
( FrameProducer { producer } , FrameConsumer { consumer } )
202
282
} )
203
283
}
284
+
285
+ /// Attempt to release the `Producer`.
286
+ ///
287
+ /// This re-initializes the buffer so it may be split in a different mode at a later
288
+ /// time. There must be no read or write grants active, or an error will be returned.
289
+ ///
290
+ /// The `Producer` ust be from THIS `BBBuffer`, or an error will be returned.
291
+ ///
292
+ /// ```rust
293
+ /// # // bbqueue test shim!
294
+ /// # fn bbqtest() {
295
+ /// use bbqueue::{BBBuffer, consts::*};
296
+ ///
297
+ /// // Create and split a new buffer
298
+ /// let buffer: BBBuffer<U6> = BBBuffer::new();
299
+ /// let (prod, cons) = buffer.try_split().unwrap();
300
+ ///
301
+ /// // Not possible to split twice
302
+ /// assert!(buffer.try_split().is_err());
303
+ ///
304
+ /// // Release the producer and consumer
305
+ /// assert!(buffer.try_release(prod, cons).is_ok());
306
+ ///
307
+ /// // Split the buffer in framed mode
308
+ /// let (fprod, fcons) = buffer.try_split_framed().unwrap();
309
+ /// # // bbqueue test shim!
310
+ /// # }
311
+ /// #
312
+ /// # fn main() {
313
+ /// # #[cfg(not(feature = "thumbv6"))]
314
+ /// # bbqtest();
315
+ /// # }
316
+ /// ```
317
+ pub fn try_release_producer (
318
+ & ' a self ,
319
+ prod : Producer < ' a , N > ,
320
+ ) -> CoreResult < ( ) , Producer < ' a , N > > {
321
+ // Note: Re-entrancy is not possible because we require ownership
322
+ // of the producer, which are not cloneable. We also
323
+ // can assume the buffer has been split, because
324
+
325
+ // Is this our producer?
326
+ let our_prod = prod. bbq . as_ptr ( ) as * const Self == self ;
327
+
328
+ if !( our_prod) {
329
+ // Can't release, not our producer
330
+ return Err ( prod) ;
331
+ }
332
+
333
+ let wr_in_progress = self . 0 . write_in_progress . load ( Acquire ) ;
334
+ let rd_in_progress = self . 0 . read_in_progress . load ( Acquire ) ;
335
+
336
+ if wr_in_progress || rd_in_progress {
337
+ // Can't release, active grant(s) in progress
338
+ return Err ( prod) ;
339
+ }
340
+
341
+ // Drop the producer
342
+ drop ( prod) ;
343
+
344
+ // Re-initialize the buffer (not totally needed, but nice to do)
345
+ self . 0 . write . store ( 0 , Release ) ;
346
+ self . 0 . read . store ( 0 , Release ) ;
347
+ self . 0 . reserve . store ( 0 , Release ) ;
348
+ self . 0 . last . store ( 0 , Release ) ;
349
+
350
+ // Mark the buffer as ready to retake producer
351
+ self . 0 . split_prod_cons . fetch_and ( !BIT_PRODUCER , Release ) ;
352
+
353
+ Ok ( ( ) )
354
+ }
355
+
356
+ /// Attempt to release the `Consumer`.
357
+ ///
358
+ /// This re-initializes the buffer so it may be split in a different mode at a later
359
+ /// time. There must be no read or write grants active, or an error will be returned.
360
+ ///
361
+ /// The `Consumer` must be from THIS `BBBuffer`, or an error will be returned.
362
+ ///
363
+ /// ```rust
364
+ /// # // bbqueue test shim!
365
+ /// # fn bbqtest() {
366
+ /// use bbqueue::{BBBuffer, consts::*};
367
+ ///
368
+ /// // Create and split a new buffer
369
+ /// let buffer: BBBuffer<U6> = BBBuffer::new();
370
+ /// let (prod, cons) = buffer.try_split().unwrap();
371
+ ///
372
+ /// // Not possible to split twice
373
+ /// assert!(buffer.try_split().is_err());
374
+ ///
375
+ /// // Release the producer and consumer
376
+ /// assert!(buffer.try_release(prod, cons).is_ok());
377
+ ///
378
+ /// // Split the buffer in framed mode
379
+ /// let (fprod, fcons) = buffer.try_split_framed().unwrap();
380
+ /// # // bbqueue test shim!
381
+ /// # }
382
+ /// #
383
+ /// # fn main() {
384
+ /// # #[cfg(not(feature = "thumbv6"))]
385
+ /// # bbqtest();
386
+ /// # }
387
+ /// ```
388
+ pub fn try_release_consumer (
389
+ & ' a self ,
390
+ cons : Consumer < ' a , N > ,
391
+ ) -> CoreResult < ( ) , Consumer < ' a , N > > {
392
+ // Note: Re-entrancy is not possible because we require ownership
393
+ // of the consumer, which are not cloneable. We also
394
+ // can assume the buffer has been split, because
395
+
396
+ // Is this our consumer?
397
+ let our_cons = cons. bbq . as_ptr ( ) as * const Self == self ;
398
+
399
+ if !( our_cons) {
400
+ // Can't release, not our consumer
401
+ return Err ( cons) ;
402
+ }
403
+
404
+ let wr_in_progress = self . 0 . write_in_progress . load ( Acquire ) ;
405
+ let rd_in_progress = self . 0 . read_in_progress . load ( Acquire ) ;
406
+
407
+ if wr_in_progress || rd_in_progress {
408
+ // Can't release, active grant(s) in progress
409
+ return Err ( cons) ;
410
+ }
411
+
412
+ // Drop the consumer
413
+ drop ( cons) ;
414
+
415
+ // Re-initialize the buffer (not totally needed, but nice to do)
416
+ self . 0 . write . store ( 0 , Release ) ;
417
+ self . 0 . read . store ( 0 , Release ) ;
418
+ self . 0 . reserve . store ( 0 , Release ) ;
419
+ self . 0 . last . store ( 0 , Release ) ;
420
+
421
+ // Mark the buffer as ready to retake consumer
422
+ self . 0 . split_prod_cons . fetch_and ( !BIT_CONSUMER , Release ) ;
423
+
424
+ Ok ( ( ) )
425
+ }
204
426
}
205
427
206
428
/// `const-fn` version BBBuffer
@@ -237,8 +459,10 @@ pub struct ConstBBBuffer<A> {
237
459
/// Is there an active write grant?
238
460
write_in_progress : AtomicBool ,
239
461
240
- /// Have we already split?
241
- already_split : AtomicBool ,
462
+ /// Whether we have split the producer and/or consumer parts off.
463
+ ///
464
+ /// See the `BIT_PRODUCER` and `BIT_CONSUMER` bits which define what parts have been split off.
465
+ split_prod_cons : AtomicU8 ,
242
466
}
243
467
244
468
impl < A > ConstBBBuffer < A > {
@@ -292,7 +516,7 @@ impl<A> ConstBBBuffer<A> {
292
516
write_in_progress : AtomicBool :: new ( false ) ,
293
517
294
518
/// We haven't split at the start
295
- already_split : AtomicBool :: new ( false ) ,
519
+ split_prod_cons : AtomicU8 :: new ( 0 ) ,
296
520
}
297
521
}
298
522
}
@@ -820,7 +1044,7 @@ where
820
1044
/// If `used` is larger than the given grant, the maximum amount will
821
1045
/// be commited
822
1046
///
823
- /// NOTE: If the `thumbv6` feature is selected, this function takes a short critical
1047
+ /// NOTE: If the `thumbv6` feature is selected, this function takes a short critical
824
1048
/// section while committing.
825
1049
pub fn commit ( mut self , used : usize ) {
826
1050
self . commit_inner ( used) ;
@@ -947,7 +1171,7 @@ where
947
1171
/// If `used` is larger than the given grant, the full grant will
948
1172
/// be released.
949
1173
///
950
- /// NOTE: If the `thumbv6` feature is selected, this function takes a short critical
1174
+ /// NOTE: If the `thumbv6` feature is selected, this function takes a short critical
951
1175
/// section while releasing.
952
1176
pub fn release ( mut self , used : usize ) {
953
1177
// Saturate the grant release
@@ -1065,7 +1289,7 @@ where
1065
1289
/// If `used` is larger than the given grant, the full grant will
1066
1290
/// be released.
1067
1291
///
1068
- /// NOTE: If the `thumbv6` feature is selected, this function takes a short critical
1292
+ /// NOTE: If the `thumbv6` feature is selected, this function takes a short critical
1069
1293
/// section while releasing.
1070
1294
pub fn release ( mut self , used : usize ) {
1071
1295
// Saturate the grant release
0 commit comments