86
86
//! represented the same in Rust as it is in the canonical ABI. That means that
87
87
//! sending `list<T>` into a future might require copying the entire list and
88
88
//! changing its layout. Currently this is par-for-the-course with bindings.
89
+ //!
90
+ //! ## Linear (exactly once) Writes
91
+ //!
92
+ //! The component model requires that a writable end of a future must be written
93
+ //! to before closing, otherwise the close operation traps. Ideally usage of
94
+ //! this API shouldn't result in traps so this is modeled in the Rust-level API
95
+ //! to prevent this trap from occurring. Rust does not support linear types
96
+ //! (types that must be used exactly once), instead it only has affine types
97
+ //! (types which must be used at most once), meaning that this requires some
98
+ //! runtime support.
99
+ //!
100
+ //! Specifically the `FutureWriter` structure stores two auxiliary Rust-specific
101
+ //! pieces of information:
102
+ //!
103
+ //! * A `should_write_default_value` boolean - if `true` on destruction then a
104
+ //! value has not yet been written and something must be written.
105
+ //! * A `default: fn() -> T` constructor to lazily create the default value to
106
+ //! be sent in this situation.
107
+ //!
108
+ //! This `default` field is provided by the user when the future is initially
109
+ //! created. Additionally during `Drop` a new Rust-level task will be spawned to
110
+ //! perform the write in the background. That'll keep the component-level task
111
+ //! alive until that write completes but otherwise shouldn't hinder anything
112
+ //! else.
89
113
90
114
use {
91
115
super :: waitable:: { WaitableOp , WaitableOperation } ,
@@ -162,6 +186,7 @@ pub struct FutureVtable<T> {
162
186
/// This function is unsafe as it requires the functions within `vtable` to
163
187
/// correctly uphold the contracts of the component model.
164
188
pub unsafe fn future_new < T > (
189
+ default : fn ( ) -> T ,
165
190
vtable : & ' static FutureVtable < T > ,
166
191
) -> ( FutureWriter < T > , FutureReader < T > ) {
167
192
unsafe {
@@ -170,7 +195,7 @@ pub unsafe fn future_new<T>(
170
195
let writer = ( handles >> 32 ) as u32 ;
171
196
rtdebug ! ( "future.new() = [{writer}, {reader}]" ) ;
172
197
(
173
- FutureWriter :: new ( writer, vtable) ,
198
+ FutureWriter :: new ( writer, default , vtable) ,
174
199
FutureReader :: new ( reader, vtable) ,
175
200
)
176
201
}
@@ -183,6 +208,19 @@ pub unsafe fn future_new<T>(
183
208
pub struct FutureWriter < T : ' static > {
184
209
handle : u32 ,
185
210
vtable : & ' static FutureVtable < T > ,
211
+
212
+ /// Whether or not a value should be written during `drop`.
213
+ ///
214
+ /// This is set to `false` when a value is successfully written or when a
215
+ /// value is written but the future is witnessed as being closed.
216
+ ///
217
+ /// Note that this is set to `true` on construction to ensure that only
218
+ /// location which actually witness a completed write set it to `false`.
219
+ should_write_default_value : bool ,
220
+
221
+ /// Constructor for the default value to write during `drop`, should one
222
+ /// need to be written.
223
+ default : fn ( ) -> T ,
186
224
}
187
225
188
226
impl < T > FutureWriter < T > {
@@ -193,8 +231,13 @@ impl<T> FutureWriter<T> {
193
231
/// This function is unsafe as it requires the functions within `vtable` to
194
232
/// correctly uphold the contracts of the component model.
195
233
#[ doc( hidden) ]
196
- pub unsafe fn new ( handle : u32 , vtable : & ' static FutureVtable < T > ) -> Self {
197
- Self { handle, vtable }
234
+ pub unsafe fn new ( handle : u32 , default : fn ( ) -> T , vtable : & ' static FutureVtable < T > ) -> Self {
235
+ Self {
236
+ handle,
237
+ default,
238
+ should_write_default_value : true ,
239
+ vtable,
240
+ }
198
241
}
199
242
200
243
/// Write the specified `value` to this `future`.
@@ -241,9 +284,33 @@ impl<T> fmt::Debug for FutureWriter<T> {
241
284
242
285
impl < T > Drop for FutureWriter < T > {
243
286
fn drop ( & mut self ) {
244
- unsafe {
245
- rtdebug ! ( "future.close-writable({})" , self . handle) ;
246
- ( self . vtable . close_writable ) ( self . handle ) ;
287
+ // If a value has not yet been written into this writer than that must
288
+ // be done so now. Perform a "clone" of `self` by moving our data into a
289
+ // subtask, but ensure that `should_write_default_value` is set to
290
+ // `false` to avoid infinite loops by accident. Once the task is spawned
291
+ // we're done and the subtask's destructor of the closed-over
292
+ // `FutureWriter` will be responsible for performing the
293
+ // `close-writable` call below.
294
+ //
295
+ // Note, though, that if `should_write_default_value` is `false` then a
296
+ // write has already happened and we can go ahead and just synchronously
297
+ // drop this writer as we would any other handle.
298
+ if self . should_write_default_value {
299
+ let clone = FutureWriter {
300
+ handle : self . handle ,
301
+ default : self . default ,
302
+ should_write_default_value : false ,
303
+ vtable : self . vtable ,
304
+ } ;
305
+ crate :: async_support:: spawn ( async move {
306
+ let value = ( clone. default ) ( ) ;
307
+ let _ = clone. write ( value) . await ;
308
+ } ) ;
309
+ } else {
310
+ unsafe {
311
+ rtdebug ! ( "future.close-writable({})" , self . handle) ;
312
+ ( self . vtable . close_writable ) ( self . handle ) ;
313
+ }
247
314
}
248
315
}
249
316
}
@@ -299,7 +366,7 @@ where
299
366
}
300
367
301
368
fn in_progress_update (
302
- ( writer, cleanup) : Self :: InProgress ,
369
+ ( mut writer, cleanup) : Self :: InProgress ,
303
370
code : u32 ,
304
371
) -> Result < Self :: Result , Self :: InProgress > {
305
372
let ptr = cleanup
@@ -321,6 +388,11 @@ where
321
388
// pass here.
322
389
let value = unsafe { ( writer. vtable . lift ) ( ptr) } ;
323
390
let status = if c == ReturnCode :: Closed ( 0 ) {
391
+ // This writer has been witnessed to be closed, meaning that
392
+ // `writer` is going to get destroyed soon as this return
393
+ // value propagates up the stack. There's no need to write
394
+ // the default value, so set this to `false`.
395
+ writer. should_write_default_value = false ;
324
396
WriteComplete :: Closed ( value)
325
397
} else {
326
398
WriteComplete :: Cancelled ( value)
@@ -338,6 +410,9 @@ where
338
410
// Afterwards the `cleanup` itself is naturally dropped and cleaned
339
411
// up.
340
412
ReturnCode :: Completed ( 1 ) | ReturnCode :: Closed ( 1 ) | ReturnCode :: Cancelled ( 1 ) => {
413
+ // A value was written, so no need to write the default value.
414
+ writer. should_write_default_value = false ;
415
+
341
416
// SAFETY: we're the ones managing `ptr` so we know it's safe to
342
417
// pass here.
343
418
unsafe {
@@ -456,7 +531,7 @@ pub enum FutureWriteCancel<T: 'static> {
456
531
Cancelled ( T , FutureWriter < T > ) ,
457
532
}
458
533
459
- /// Represents the readable end of a Component Model `future`.
534
+ /// Represents the readable end of a Component Model `future<T> `.
460
535
pub struct FutureReader < T : ' static > {
461
536
handle : AtomicU32 ,
462
537
vtable : & ' static FutureVtable < T > ,
@@ -499,12 +574,11 @@ impl<T> FutureReader<T> {
499
574
}
500
575
501
576
impl < T > IntoFuture for FutureReader < T > {
502
- type Output = Option < T > ;
577
+ type Output = T ;
503
578
type IntoFuture = FutureRead < T > ;
504
579
505
580
/// Convert this object into a `Future` which will resolve when a value is
506
- /// written to the writable end of this `future` (yielding a `Some` result)
507
- /// or when the writable end is dropped (yielding a `None` result).
581
+ /// written to the writable end of this `future`.
508
582
fn into_future ( self ) -> Self :: IntoFuture {
509
583
FutureRead {
510
584
op : WaitableOperation :: new ( self ) ,
@@ -536,7 +610,6 @@ struct FutureReadOp<T>(marker::PhantomData<T>);
536
610
537
611
enum ReadComplete < T > {
538
612
Value ( T ) ,
539
- Closed ,
540
613
Cancelled ,
541
614
}
542
615
@@ -547,7 +620,7 @@ where
547
620
type Start = FutureReader < T > ;
548
621
type InProgress = ( FutureReader < T > , Option < Cleanup > ) ;
549
622
type Result = ( ReadComplete < T > , FutureReader < T > ) ;
550
- type Cancel = Result < Option < T > , FutureReader < T > > ;
623
+ type Cancel = Result < T , FutureReader < T > > ;
551
624
552
625
fn start ( reader : Self :: Start ) -> ( u32 , Self :: InProgress ) {
553
626
let ( ptr, cleanup) = Cleanup :: new ( reader. vtable . layout ) ;
@@ -570,13 +643,9 @@ where
570
643
match ReturnCode :: decode ( code) {
571
644
ReturnCode :: Blocked => Err ( ( reader, cleanup) ) ,
572
645
573
- // The read didn't complete, so `cleanup` is still uninitialized, so
574
- // let it fall out of scope.
575
- ReturnCode :: Closed ( 0 ) => Ok ( ( ReadComplete :: Closed , reader) ) ,
576
-
577
- // Like `in_progress_closed` the read operation has finished but
578
- // without a value, so let `cleanup` fall out of scope to clean up
579
- // its allocation.
646
+ // Let `cleanup` fall out of scope to clean up its allocation here,
647
+ // and otherwise tahe reader is plumbed through to possibly restart
648
+ // the read in the future.
580
649
ReturnCode :: Cancelled ( 0 ) => Ok ( ( ReadComplete :: Cancelled , reader) ) ,
581
650
582
651
// The read has completed, so lift the value from the stored memory and
@@ -613,28 +682,27 @@ where
613
682
fn result_into_cancel ( ( value, reader) : Self :: Result ) -> Self :: Cancel {
614
683
match value {
615
684
// The value was actually read, so thread that through here.
616
- ReadComplete :: Value ( value) => Ok ( Some ( value) ) ,
685
+ ReadComplete :: Value ( value) => Ok ( value) ,
617
686
618
687
// The read was successfully cancelled, so thread through the
619
688
// `reader` to possibly restart later on.
620
689
ReadComplete :: Cancelled => Err ( reader) ,
621
-
622
- // The other end was closed, so this can't possibly ever complete
623
- // again, so thread that through.
624
- ReadComplete :: Closed => Ok ( None ) ,
625
690
}
626
691
}
627
692
}
628
693
629
694
impl < T : ' static > Future for FutureRead < T > {
630
- type Output = Option < T > ;
695
+ type Output = T ;
631
696
632
697
fn poll ( self : Pin < & mut Self > , cx : & mut Context < ' _ > ) -> Poll < Self :: Output > {
633
698
self . pin_project ( )
634
699
. poll_complete ( cx)
635
700
. map ( |( result, _reader) | match result {
636
- ReadComplete :: Value ( val) => Some ( val) ,
637
- ReadComplete :: Cancelled | ReadComplete :: Closed => None ,
701
+ ReadComplete :: Value ( val) => val,
702
+ // This is only possible if, after calling `FutureRead::cancel`,
703
+ // the future is polled again. The `cancel` method is documented
704
+ // as "don't do that" so this is left to panic.
705
+ ReadComplete :: Cancelled => panic ! ( "cannot poll after cancelling" ) ,
638
706
} )
639
707
}
640
708
}
@@ -650,21 +718,17 @@ impl<T> FutureRead<T> {
650
718
///
651
719
/// Return values include:
652
720
///
653
- /// * `Ok(Some( value) )` - future completed before this cancellation request
721
+ /// * `Ok(value)` - future completed before this cancellation request
654
722
/// was received.
655
- /// * `Ok(None)` - future closed before this cancellation request was
656
- /// received.
657
723
/// * `Err(reader)` - read operation was cancelled and it can be retried in
658
724
/// the future if desired.
659
725
///
660
- /// Note that if this method is called after the write was already cancelled
661
- /// then `Ok(None)` will be returned.
662
- ///
663
726
/// # Panics
664
727
///
665
728
/// Panics if the operation has already been completed via `Future::poll`,
666
- /// or if this method is called twice.
667
- pub fn cancel ( self : Pin < & mut Self > ) -> Result < Option < T > , FutureReader < T > > {
729
+ /// or if this method is called twice. Additionally if this method completes
730
+ /// then calling `poll` again on `self` will panic.
731
+ pub fn cancel ( self : Pin < & mut Self > ) -> Result < T , FutureReader < T > > {
668
732
self . pin_project ( ) . cancel ( )
669
733
}
670
734
}
0 commit comments