@@ -12,10 +12,17 @@ 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
+
20
+ /// Bit to define producer taken.
21
+ const BIT_PRODUCER : u8 = 1 << 0 ;
22
+
23
+ /// Bit to define consumer taken.
24
+ const BIT_CONSUMER : u8 = 1 << 1 ;
25
+
19
26
#[ derive( Debug ) ]
20
27
/// A backing structure for a BBQueue. Can be used to create either
21
28
/// a BBQueue or a split Producer/Consumer pair
@@ -49,6 +56,11 @@ pub struct BBBuffer<const N: usize> {
49
56
50
57
/// Have we already split?
51
58
already_split : AtomicBool ,
59
+
60
+ /// Whether we have split the producer and/or consumer parts off.
61
+ ///
62
+ /// See the `BIT_PRODUCER` and `BIT_CONSUMER` bits which define what parts have been split off.
63
+ split_prod_cons : AtomicU8 ,
52
64
}
53
65
54
66
unsafe impl < const A : usize > Sync for BBBuffer < A > { }
@@ -63,7 +75,7 @@ impl<'a, const N: usize> BBBuffer<N> {
63
75
/// is placed at `static` scope within the `.bss` region, the explicit initialization
64
76
/// will be elided (as it is already performed as part of memory initialization)
65
77
///
66
- /// NOTE: If the `thumbv6` feature is selected, this function takes a short critical section
78
+ /// NOTE: If the `thumbv6` feature is selected, this function takes a short critical section
67
79
/// while splitting.
68
80
///
69
81
/// ```rust
@@ -86,7 +98,12 @@ impl<'a, const N: usize> BBBuffer<N> {
86
98
/// # }
87
99
/// ```
88
100
pub fn try_split ( & ' a self ) -> Result < ( Producer < ' a , N > , Consumer < ' a , N > ) > {
89
- if atomic:: swap ( & self . already_split , true , AcqRel ) {
101
+ // Set producer/consumer taken bit, error and reset if one was already set
102
+ let prev = self
103
+ . split_prod_cons
104
+ . fetch_or ( BIT_PRODUCER | BIT_CONSUMER , AcqRel ) ;
105
+ if prev > 0 {
106
+ self . split_prod_cons . store ( prev, Release ) ;
90
107
return Err ( Error :: AlreadySplit ) ;
91
108
}
92
109
@@ -123,14 +140,82 @@ impl<'a, const N: usize> BBBuffer<N> {
123
140
/// is placed at `static` scope within the `.bss` region, the explicit initialization
124
141
/// will be elided (as it is already performed as part of memory initialization)
125
142
///
126
- /// NOTE: If the `thumbv6` feature is selected, this function takes a short critical
143
+ /// NOTE: If the `thumbv6` feature is selected, this function takes a short critical
127
144
/// section while splitting.
128
145
pub fn try_split_framed ( & ' a self ) -> Result < ( FrameProducer < ' a , N > , FrameConsumer < ' a , N > ) > {
129
146
let ( producer, consumer) = self . try_split ( ) ?;
130
147
Ok ( ( FrameProducer { producer } , FrameConsumer { consumer } ) )
131
148
}
132
149
133
- /// Attempt to release the Producer and Consumer
150
+ /// Attempt to take a `Producer` from the `BBBuffer` to gain access to the
151
+ /// buffer. If a producer has already been taken, an error will be returned.
152
+ ///
153
+ /// NOTE: When splitting, the underlying buffer will be explicitly initialized
154
+ /// to zero. This may take a measurable amount of time, depending on the size
155
+ /// of the buffer. This is necessary to prevent undefined behavior. If the buffer
156
+ /// is placed at `static` scope within the `.bss` region, the explicit initialization
157
+ /// will be elided (as it is already performed as part of memory initialization)
158
+ ///
159
+ /// NOTE: If the `thumbv6` feature is selected, this function takes a short critical section
160
+ /// while splitting.
161
+ pub fn try_take_producer ( & ' a self ) -> Result < Producer < ' a , N > > {
162
+ // Set producer taken bit, error if already set
163
+ if self . 0 . split_prod_cons . fetch_or ( BIT_PRODUCER , AcqRel ) & BIT_PRODUCER > 0 {
164
+ return Err ( Error :: AlreadySplit ) ;
165
+ }
166
+
167
+ unsafe {
168
+ // TODO: do we need to zero buffer here, like try_split?
169
+ // // Explicitly zero the data to avoid undefined behavior.
170
+ // // This is required, because we hand out references to the buffers,
171
+ // // which mean that creating them as references is technically UB for now
172
+ // let mu_ptr = self.0.buf.get();
173
+ // (*mu_ptr).as_mut_ptr().write_bytes(0u8, 1);
174
+
175
+ let nn1 = NonNull :: new_unchecked ( self as * const _ as * mut _ ) ;
176
+
177
+ Ok ( Producer {
178
+ bbq : nn1,
179
+ pd : PhantomData ,
180
+ } )
181
+ }
182
+ }
183
+
184
+ /// Attempt to take a `Consumer` from the `BBBuffer` to gain access to the
185
+ /// buffer. If a consumer has already been taken, an error will be returned.
186
+ ///
187
+ /// NOTE: When splitting, the underlying buffer will be explicitly initialized
188
+ /// to zero. This may take a measurable amount of time, depending on the size
189
+ /// of the buffer. This is necessary to prevent undefined behavior. If the buffer
190
+ /// is placed at `static` scope within the `.bss` region, the explicit initialization
191
+ /// will be elided (as it is already performed as part of memory initialization)
192
+ ///
193
+ /// NOTE: If the `thumbv6` feature is selected, this function takes a short critical section
194
+ /// while splitting.
195
+ pub fn try_take_consumer ( & ' a self ) -> Result < Consumer < ' a , N > > {
196
+ // Set producer taken bit, error if already set
197
+ if self . 0 . split_prod_cons . fetch_or ( BIT_CONSUMER , AcqRel ) & BIT_CONSUMER > 0 {
198
+ return Err ( Error :: AlreadySplit ) ;
199
+ }
200
+
201
+ unsafe {
202
+ // TODO: do we need to zero buffer here, like try_split?
203
+ // // Explicitly zero the data to avoid undefined behavior.
204
+ // // This is required, because we hand out references to the buffers,
205
+ // // which mean that creating them as references is technically UB for now
206
+ // let mu_ptr = self.0.buf.get();
207
+ // (*mu_ptr).as_mut_ptr().write_bytes(0u8, 1);
208
+
209
+ let nn1 = NonNull :: new_unchecked ( self as * const _ as * mut _ ) ;
210
+
211
+ Ok ( Consumer {
212
+ bbq : nn1,
213
+ pd : PhantomData ,
214
+ } )
215
+ }
216
+ }
217
+
218
+ /// Attempt to release the `Producer` and `Consumer`
134
219
///
135
220
/// This re-initializes the buffer so it may be split in a different mode at a later
136
221
/// time. There must be no read or write grants active, or an error will be returned.
@@ -204,7 +289,7 @@ impl<'a, const N: usize> BBBuffer<N> {
204
289
self . last . store ( 0 , Release ) ;
205
290
206
291
// Mark the buffer as ready to be split
207
- self . already_split . store ( false , Release ) ;
292
+ self . split_prod_cons . store ( 0 , Release ) ;
208
293
209
294
Ok ( ( ) )
210
295
}
@@ -227,6 +312,148 @@ impl<'a, const N: usize> BBBuffer<N> {
227
312
( FrameProducer { producer } , FrameConsumer { consumer } )
228
313
} )
229
314
}
315
+
316
+ /// Attempt to release the `Producer`.
317
+ ///
318
+ /// This re-initializes the buffer so it may be split in a different mode at a later
319
+ /// time. There must be no read or write grants active, or an error will be returned.
320
+ ///
321
+ /// The `Producer` ust be from THIS `BBBuffer`, or an error will be returned.
322
+ ///
323
+ /// ```rust
324
+ /// # // bbqueue test shim!
325
+ /// # fn bbqtest() {
326
+ /// use bbqueue::{BBBuffer, consts::*};
327
+ ///
328
+ /// // Create and split a new buffer
329
+ /// let buffer: BBBuffer<U6> = BBBuffer::new();
330
+ /// let (prod, cons) = buffer.try_split().unwrap();
331
+ ///
332
+ /// // Not possible to split twice
333
+ /// assert!(buffer.try_split().is_err());
334
+ ///
335
+ /// // Release the producer and consumer
336
+ /// assert!(buffer.try_release(prod, cons).is_ok());
337
+ ///
338
+ /// // Split the buffer in framed mode
339
+ /// let (fprod, fcons) = buffer.try_split_framed().unwrap();
340
+ /// # // bbqueue test shim!
341
+ /// # }
342
+ /// #
343
+ /// # fn main() {
344
+ /// # #[cfg(not(feature = "thumbv6"))]
345
+ /// # bbqtest();
346
+ /// # }
347
+ /// ```
348
+ pub fn try_release_producer (
349
+ & ' a self ,
350
+ prod : Producer < ' a , N > ,
351
+ ) -> CoreResult < ( ) , Producer < ' a , N > > {
352
+ // Note: Re-entrancy is not possible because we require ownership
353
+ // of the producer, which are not cloneable. We also
354
+ // can assume the buffer has been split, because
355
+
356
+ // Is this our producer?
357
+ let our_prod = prod. bbq . as_ptr ( ) as * const Self == self ;
358
+
359
+ if !( our_prod) {
360
+ // Can't release, not our producer
361
+ return Err ( prod) ;
362
+ }
363
+
364
+ let wr_in_progress = self . 0 . write_in_progress . load ( Acquire ) ;
365
+ let rd_in_progress = self . 0 . read_in_progress . load ( Acquire ) ;
366
+
367
+ if wr_in_progress || rd_in_progress {
368
+ // Can't release, active grant(s) in progress
369
+ return Err ( prod) ;
370
+ }
371
+
372
+ // Drop the producer
373
+ drop ( prod) ;
374
+
375
+ // Re-initialize the buffer (not totally needed, but nice to do)
376
+ self . 0 . write . store ( 0 , Release ) ;
377
+ self . 0 . read . store ( 0 , Release ) ;
378
+ self . 0 . reserve . store ( 0 , Release ) ;
379
+ self . 0 . last . store ( 0 , Release ) ;
380
+
381
+ // Mark the buffer as ready to retake producer
382
+ self . 0 . split_prod_cons . fetch_and ( !BIT_PRODUCER , Release ) ;
383
+
384
+ Ok ( ( ) )
385
+ }
386
+
387
+ /// Attempt to release the `Consumer`.
388
+ ///
389
+ /// This re-initializes the buffer so it may be split in a different mode at a later
390
+ /// time. There must be no read or write grants active, or an error will be returned.
391
+ ///
392
+ /// The `Consumer` must be from THIS `BBBuffer`, or an error will be returned.
393
+ ///
394
+ /// ```rust
395
+ /// # // bbqueue test shim!
396
+ /// # fn bbqtest() {
397
+ /// use bbqueue::{BBBuffer, consts::*};
398
+ ///
399
+ /// // Create and split a new buffer
400
+ /// let buffer: BBBuffer<U6> = BBBuffer::new();
401
+ /// let (prod, cons) = buffer.try_split().unwrap();
402
+ ///
403
+ /// // Not possible to split twice
404
+ /// assert!(buffer.try_split().is_err());
405
+ ///
406
+ /// // Release the producer and consumer
407
+ /// assert!(buffer.try_release(prod, cons).is_ok());
408
+ ///
409
+ /// // Split the buffer in framed mode
410
+ /// let (fprod, fcons) = buffer.try_split_framed().unwrap();
411
+ /// # // bbqueue test shim!
412
+ /// # }
413
+ /// #
414
+ /// # fn main() {
415
+ /// # #[cfg(not(feature = "thumbv6"))]
416
+ /// # bbqtest();
417
+ /// # }
418
+ /// ```
419
+ pub fn try_release_consumer (
420
+ & ' a self ,
421
+ cons : Consumer < ' a , N > ,
422
+ ) -> CoreResult < ( ) , Consumer < ' a , N > > {
423
+ // Note: Re-entrancy is not possible because we require ownership
424
+ // of the consumer, which are not cloneable. We also
425
+ // can assume the buffer has been split, because
426
+
427
+ // Is this our consumer?
428
+ let our_cons = cons. bbq . as_ptr ( ) as * const Self == self ;
429
+
430
+ if !( our_cons) {
431
+ // Can't release, not our consumer
432
+ return Err ( cons) ;
433
+ }
434
+
435
+ let wr_in_progress = self . 0 . write_in_progress . load ( Acquire ) ;
436
+ let rd_in_progress = self . 0 . read_in_progress . load ( Acquire ) ;
437
+
438
+ if wr_in_progress || rd_in_progress {
439
+ // Can't release, active grant(s) in progress
440
+ return Err ( cons) ;
441
+ }
442
+
443
+ // Drop the consumer
444
+ drop ( cons) ;
445
+
446
+ // Re-initialize the buffer (not totally needed, but nice to do)
447
+ self . 0 . write . store ( 0 , Release ) ;
448
+ self . 0 . read . store ( 0 , Release ) ;
449
+ self . 0 . reserve . store ( 0 , Release ) ;
450
+ self . 0 . last . store ( 0 , Release ) ;
451
+
452
+ // Mark the buffer as ready to retake consumer
453
+ self . 0 . split_prod_cons . fetch_and ( !BIT_CONSUMER , Release ) ;
454
+
455
+ Ok ( ( ) )
456
+ }
230
457
}
231
458
232
459
impl < const A : usize > BBBuffer < A > {
@@ -280,7 +507,7 @@ impl<const A: usize> BBBuffer<A> {
280
507
write_in_progress : AtomicBool :: new ( false ) ,
281
508
282
509
// We haven't split at the start
283
- already_split : AtomicBool :: new ( false ) ,
510
+ split_prod_cons : AtomicU8 :: new ( 0 ) ,
284
511
}
285
512
}
286
513
}
@@ -744,7 +971,7 @@ impl<'a, const N: usize> GrantW<'a, N> {
744
971
/// If `used` is larger than the given grant, the maximum amount will
745
972
/// be commited
746
973
///
747
- /// NOTE: If the `thumbv6` feature is selected, this function takes a short critical
974
+ /// NOTE: If the `thumbv6` feature is selected, this function takes a short critical
748
975
/// section while committing.
749
976
pub fn commit ( mut self , used : usize ) {
750
977
self . commit_inner ( used) ;
@@ -861,7 +1088,7 @@ impl<'a, const N: usize> GrantR<'a, N> {
861
1088
/// If `used` is larger than the given grant, the full grant will
862
1089
/// be released.
863
1090
///
864
- /// NOTE: If the `thumbv6` feature is selected, this function takes a short critical
1091
+ /// NOTE: If the `thumbv6` feature is selected, this function takes a short critical
865
1092
/// section while releasing.
866
1093
pub fn release ( mut self , used : usize ) {
867
1094
// Saturate the grant release
@@ -969,7 +1196,7 @@ impl<'a, const N: usize> SplitGrantR<'a, N> {
969
1196
/// If `used` is larger than the given grant, the full grant will
970
1197
/// be released.
971
1198
///
972
- /// NOTE: If the `thumbv6` feature is selected, this function takes a short critical
1199
+ /// NOTE: If the `thumbv6` feature is selected, this function takes a short critical
973
1200
/// section while releasing.
974
1201
pub fn release ( mut self , used : usize ) {
975
1202
// Saturate the grant release
0 commit comments