@@ -6,7 +6,8 @@ use crate::p3::bindings::filesystem::types::{
66} ;
77use crate :: p3:: filesystem:: { FilesystemError , FilesystemResult , preopens} ;
88use crate :: p3:: {
9- DEFAULT_BUFFER_CAPACITY , FutureOneshotProducer , FutureReadyProducer , StreamEmptyProducer ,
9+ DEFAULT_BUFFER_CAPACITY , FallibleIteratorProducer , FutureOneshotProducer , FutureReadyProducer ,
10+ StreamEmptyProducer ,
1011} ;
1112use crate :: { DirPerms , FilePerms } ;
1213use anyhow:: { Context as _, anyhow} ;
@@ -22,7 +23,7 @@ use tokio::task::{JoinHandle, spawn_blocking};
2223use wasmtime:: StoreContextMut ;
2324use wasmtime:: component:: {
2425 Accessor , Destination , FutureReader , Resource , ResourceTable , Source , StreamConsumer ,
25- StreamProducer , StreamReader , StreamResult , VecBuffer ,
26+ StreamProducer , StreamReader , StreamResult ,
2627} ;
2728
2829fn get_descriptor < ' a > (
@@ -291,150 +292,94 @@ fn map_dir_entry(
291292 }
292293}
293294
294- struct BlockingDirectoryStreamProducer {
295- dir : Arc < cap_std:: fs:: Dir > ,
295+ struct ReadDirStream {
296+ rx : mpsc:: Receiver < DirectoryEntry > ,
297+ task : JoinHandle < Result < ( ) , ErrorCode > > ,
296298 result : Option < oneshot:: Sender < Result < ( ) , ErrorCode > > > ,
297299}
298300
299- impl Drop for BlockingDirectoryStreamProducer {
300- fn drop ( & mut self ) {
301- self . close ( Ok ( ( ) ) )
302- }
303- }
304-
305- impl BlockingDirectoryStreamProducer {
306- fn close ( & mut self , res : Result < ( ) , ErrorCode > ) {
307- if let Some ( tx) = self . result . take ( ) {
308- _ = tx. send ( res) ;
309- }
310- }
311- }
312-
313- impl < D > StreamProducer < D > for BlockingDirectoryStreamProducer {
314- type Item = DirectoryEntry ;
315- type Buffer = VecBuffer < DirectoryEntry > ;
316-
317- fn poll_produce < ' a > (
318- mut self : Pin < & mut Self > ,
319- _: & mut Context < ' _ > ,
320- _: StoreContextMut < ' a , D > ,
321- mut dst : Destination < ' a , Self :: Item , Self :: Buffer > ,
322- _finish : bool ,
323- ) -> Poll < wasmtime:: Result < StreamResult > > {
324- let entries = match self . dir . entries ( ) {
325- Ok ( entries) => entries,
326- Err ( err) => {
327- self . close ( Err ( err. into ( ) ) ) ;
328- return Poll :: Ready ( Ok ( StreamResult :: Dropped ) ) ;
329- }
330- } ;
331- let res = match entries
332- . filter_map ( |entry| map_dir_entry ( entry) . transpose ( ) )
333- . collect :: < Result < Vec < _ > , _ > > ( )
334- {
335- Ok ( entries) => {
336- dst. set_buffer ( entries. into ( ) ) ;
337- Ok ( ( ) )
338- }
339- Err ( err) => Err ( err) ,
340- } ;
341- self . close ( res) ;
342- Poll :: Ready ( Ok ( StreamResult :: Dropped ) )
343- }
344- }
345-
346- struct NonblockingDirectoryStreamProducer ( DirStreamState ) ;
347-
348- enum DirStreamState {
349- Init {
301+ impl ReadDirStream {
302+ fn new (
350303 dir : Arc < cap_std:: fs:: Dir > ,
351304 result : oneshot:: Sender < Result < ( ) , ErrorCode > > ,
352- } ,
353- InProgress {
354- rx : mpsc:: Receiver < DirectoryEntry > ,
355- task : JoinHandle < Result < ( ) , ErrorCode > > ,
356- result : oneshot:: Sender < Result < ( ) , ErrorCode > > ,
357- } ,
358- Closed ,
359- }
360-
361- impl Drop for NonblockingDirectoryStreamProducer {
362- fn drop ( & mut self ) {
363- self . close ( Ok ( ( ) ) )
305+ ) -> ReadDirStream {
306+ let ( tx, rx) = mpsc:: channel ( 1 ) ;
307+ ReadDirStream {
308+ task : spawn_blocking ( move || {
309+ let entries = dir. entries ( ) ?;
310+ for entry in entries {
311+ if let Some ( entry) = map_dir_entry ( entry) ? {
312+ if let Err ( _) = tx. blocking_send ( entry) {
313+ break ;
314+ }
315+ }
316+ }
317+ Ok ( ( ) )
318+ } ) ,
319+ rx,
320+ result : Some ( result) ,
321+ }
364322 }
365- }
366323
367- impl NonblockingDirectoryStreamProducer {
368324 fn close ( & mut self , res : Result < ( ) , ErrorCode > ) {
369- if let DirStreamState :: Init { result, .. } | DirStreamState :: InProgress { result, .. } =
370- mem:: replace ( & mut self . 0 , DirStreamState :: Closed )
371- {
372- _ = result. send ( res) ;
373- }
325+ self . rx . close ( ) ;
326+ self . task . abort ( ) ;
327+ let _ = self . result . take ( ) . unwrap ( ) . send ( res) ;
374328 }
375329}
376330
377- impl < D > StreamProducer < D > for NonblockingDirectoryStreamProducer {
331+ impl < D > StreamProducer < D > for ReadDirStream {
378332 type Item = DirectoryEntry ;
379333 type Buffer = Option < DirectoryEntry > ;
380334
381335 fn poll_produce < ' a > (
382336 mut self : Pin < & mut Self > ,
383337 cx : & mut Context < ' _ > ,
384- store : StoreContextMut < ' a , D > ,
338+ mut store : StoreContextMut < ' a , D > ,
385339 mut dst : Destination < ' a , Self :: Item , Self :: Buffer > ,
386340 finish : bool ,
387341 ) -> Poll < wasmtime:: Result < StreamResult > > {
388- match mem:: replace ( & mut self . 0 , DirStreamState :: Closed ) {
389- DirStreamState :: Init { .. } if finish => Poll :: Ready ( Ok ( StreamResult :: Cancelled ) ) ,
390- DirStreamState :: Init { dir, result } => {
391- let ( entry_tx, entry_rx) = mpsc:: channel ( 1 ) ;
392- let task = spawn_blocking ( move || {
393- let entries = dir. entries ( ) ?;
394- for entry in entries {
395- if let Some ( entry) = map_dir_entry ( entry) ? {
396- if let Err ( _) = entry_tx. blocking_send ( entry) {
397- break ;
398- }
399- }
400- }
401- Ok ( ( ) )
402- } ) ;
403- self . 0 = DirStreamState :: InProgress {
404- rx : entry_rx,
405- task,
406- result,
407- } ;
408- self . poll_produce ( cx, store, dst, finish)
342+ // If this is a 0-length read then `mpsc::Receiver` does not expose an
343+ // API to wait for an item to be available without taking it out of the
344+ // channel. In lieu of that just say that we're complete and ready for a
345+ // read.
346+ if dst. remaining ( & mut store) == Some ( 0 ) {
347+ return Poll :: Ready ( Ok ( StreamResult :: Completed ) ) ;
348+ }
349+
350+ match self . rx . poll_recv ( cx) {
351+ // If an item is on the channel then send that along and say that
352+ // the read is now complete with one item being yielded.
353+ Poll :: Ready ( Some ( item) ) => {
354+ dst. set_buffer ( Some ( item) ) ;
355+ Poll :: Ready ( Ok ( StreamResult :: Completed ) )
409356 }
410- DirStreamState :: InProgress {
411- mut rx,
412- mut task,
413- result,
414- } => {
415- let Poll :: Ready ( res) = rx. poll_recv ( cx) else {
416- self . 0 = DirStreamState :: InProgress { rx, task, result } ;
417- if finish {
418- return Poll :: Ready ( Ok ( StreamResult :: Cancelled ) ) ;
419- }
420- return Poll :: Pending ;
421- } ;
422- match res {
423- Some ( entry) => {
424- self . 0 = DirStreamState :: InProgress { rx, task, result } ;
425- dst. set_buffer ( Some ( entry) ) ;
426- Poll :: Ready ( Ok ( StreamResult :: Completed ) )
427- }
428- None => {
429- let res = ready ! ( Pin :: new( & mut task) . poll( cx) )
430- . context ( "failed to join I/O task" ) ?;
431- self . 0 = DirStreamState :: InProgress { rx, task, result } ;
432- self . close ( res) ;
433- Poll :: Ready ( Ok ( StreamResult :: Dropped ) )
434- }
435- }
357+
358+ // If there's nothing left on the channel then that means that an
359+ // error occurred or the iterator is done. In both cases an
360+ // un-cancellable wait for the spawned task is entered and we await
361+ // its completion. Upon completion there our own stream is closed
362+ // with the result (sending an error code on our oneshot) and then
363+ // the stream is reported as dropped.
364+ Poll :: Ready ( None ) => {
365+ let result = ready ! ( Pin :: new( & mut self . task) . poll( cx) )
366+ . expect ( "spawned task should not panic" ) ;
367+ self . close ( result) ;
368+ Poll :: Ready ( Ok ( StreamResult :: Dropped ) )
436369 }
437- DirStreamState :: Closed => Poll :: Ready ( Ok ( StreamResult :: Dropped ) ) ,
370+
371+ // If an item isn't ready yet then cancel this outstanding request
372+ // if `finish` is set, otherwise propagate the `Pending` status.
373+ Poll :: Pending if finish => Poll :: Ready ( Ok ( StreamResult :: Cancelled ) ) ,
374+ Poll :: Pending => Poll :: Pending ,
375+ }
376+ }
377+ }
378+
379+ impl Drop for ReadDirStream {
380+ fn drop ( & mut self ) {
381+ if self . result . is_some ( ) {
382+ self . close ( Ok ( ( ) ) ) ;
438383 }
439384 }
440385}
@@ -848,23 +793,22 @@ impl types::HostDescriptorWithStore for WasiFilesystem {
848793 let dir = Arc :: clone ( dir. as_dir ( ) ) ;
849794 let ( result_tx, result_rx) = oneshot:: channel ( ) ;
850795 let stream = if allow_blocking_current_thread {
851- StreamReader :: new (
852- instance,
853- & mut store,
854- BlockingDirectoryStreamProducer {
855- dir,
856- result : Some ( result_tx) ,
857- } ,
858- )
796+ match dir. entries ( ) {
797+ Ok ( readdir) => StreamReader :: new (
798+ instance,
799+ & mut store,
800+ FallibleIteratorProducer :: new (
801+ readdir. filter_map ( |e| map_dir_entry ( e) . transpose ( ) ) ,
802+ result_tx,
803+ ) ,
804+ ) ,
805+ Err ( e) => {
806+ result_tx. send ( Err ( e. into ( ) ) ) . unwrap ( ) ;
807+ StreamReader :: new ( instance, & mut store, StreamEmptyProducer :: default ( ) )
808+ }
809+ }
859810 } else {
860- StreamReader :: new (
861- instance,
862- & mut store,
863- NonblockingDirectoryStreamProducer ( DirStreamState :: Init {
864- dir,
865- result : result_tx,
866- } ) ,
867- )
811+ StreamReader :: new ( instance, & mut store, ReadDirStream :: new ( dir, result_tx) )
868812 } ;
869813 Ok ( (
870814 stream,
0 commit comments