char/avril
(wip) why'd you have to go and make things so complicaaaaated?
git clone https://git.t4t.associates/char/avril
f20b64f
main
1use std:: cell:: RefCell ; 2use std:: convert:: Infallible ; 3use std:: future:: Future ; 4use std:: io::{ self , Read , Write }; 5use std:: rc:: Rc ; 6use std:: sync:: Arc ; 7use std:: sync:: atomic:: AtomicBool ; 8use std:: sync:: mpsc::{ self , Sender , SyncSender }; 9 10use avril_core:: serde_v8::{ JsBuffer , ToJsBuffer }; 11use avril_core::{ Extension , Op }; 12use futures:: channel:: oneshot; 13use futures:: future::{ Either , select}; 14 15enum WriteReply { 16Async ( oneshot:: Sender < io:: Result <()>>), 17Blocking ( SyncSender < io:: Result <()>>), 18} 19 20struct WriteRequest { 21bytes : Option < Vec < u8 >>, 22reply : WriteReply , 23} 24 25struct Output ( Sender < WriteRequest >); 26 27async fn response < T >( reply : oneshot:: Receiver < io:: Result < T >>) -> io:: Result < T > { 28 reply 29. await 30. map_err ( |_| io:: Error :: other ( "Stdio worker stopped" )) ? 31} 32 33impl Output { 34fn new ( name : & str , mut writer : impl Write +Send +' static ) -> io:: Result < Self > { 35let ( sender, requests) = mpsc:: channel ::< WriteRequest >(); 36 std:: thread:: Builder :: new () 37. name ( name. into ()) 38. spawn ( move ||{ 39let mut failure: Option <( io:: ErrorKind , String )> =None ; 40for requestin requests. iter () { 41let result =if let Some (( kind, message)) =& failure{ 42Err ( io:: Error :: new ( * kind, message. clone ())) 43} else { 44( ||{ 45if let Some ( bytes) = request. bytes { 46 writer. write_all ( & bytes) ?; 47} 48 writer. flush () 49})() 50}; 51if let Err ( error) =& result{ 52 failure =Some (( error. kind (), error. to_string ())); 53} 54match request. reply { 55WriteReply :: Async ( reply) =>{ 56let _ = reply. send ( result); 57} 58WriteReply :: Blocking ( reply) =>{ 59let _ = reply. send ( result); 60} 61} 62} 63}) ?; 64Ok ( Self ( sender)) 65} 66 67fn submit ( & self , bytes : Option < Vec < u8 >>) -> io:: Result < oneshot:: Receiver < io:: Result <()>>> { 68let ( reply, receiver) = oneshot:: channel (); 69// Stream writes are serialized by the sink; raw ops must not block the isolate on capacity. 70self . 0 71. send ( WriteRequest { 72 bytes, 73reply : WriteReply :: Async ( reply), 74}) 75. map_err ( |_| io:: Error :: other ( "Stdio worker stopped" )) ?; 76Ok ( receiver) 77} 78 79fn blocking_write ( & self , bytes : Option < Vec < u8 >>) -> io:: Result <()> { 80let ( reply, receiver) = mpsc:: sync_channel ( 1 ); 81self . 0 82. send ( WriteRequest { 83 bytes, 84reply : WriteReply :: Blocking ( reply), 85}) 86. map_err ( |_| io:: Error :: other ( "Stdio worker stopped" )) ?; 87 receiver 88. recv () 89. map_err ( |_| io:: Error :: other ( "Stdio worker stopped" )) ? 90} 91 92fn flush ( & self ) -> io:: Result <()> { 93self . blocking_write ( None ) 94} 95} 96 97struct ReadRequest { 98limit : usize , 99reply : oneshot:: Sender < io:: Result < Vec < u8 >>>, 100} 101 102enum InputState { 103Idle , 104Reading ( oneshot:: Sender <()>), 105Canceled , 106} 107 108struct PendingRead ( Rc < Stdio >); 109 110impl Drop for PendingRead { 111fn drop ( & mut self ) { 112if matches! ( * self . 0 . input . borrow (), InputState :: Reading ( _)) { 113self . 0 . cancel_read (); 114} 115} 116} 117 118pub struct Stdio { 119stdin : SyncSender < ReadRequest >, 120stdin_available : bool , 121stdout : Output , 122stderr : Output , 123input : RefCell < InputState >, 124interrupted : Arc < AtomicBool >, 125} 126 127impl Stdio { 128pub fn new () -> io:: Result < Rc < Self >> { 129Self :: new_with_stdin ( true , Arc :: default ()) 130} 131 132pub fn new_for_repl () -> io:: Result < Rc < Self >> { 133Self :: new_with_stdin ( false , Arc :: default ()) 134} 135 136pub fn new_for_worker ( interrupted : Arc < AtomicBool >) -> io:: Result < Rc < Self >> { 137Self :: new_with_stdin ( false , interrupted) 138} 139 140pub fn interrupt_flag ( & self ) ->Arc < AtomicBool > { 141self . interrupted . clone () 142} 143 144fn new_with_stdin ( stdin_available : bool , interrupted : Arc < AtomicBool >) -> io:: Result < Rc < Self >> { 145let ( stdin, requests) = mpsc:: sync_channel ::< ReadRequest >( 1 ); 146if stdin_available{ 147// An outstanding OS read cannot be interrupted portably; never join this worker. 148 std:: thread:: Builder :: new () 149. name ( "avril:stdin" . into ()) 150. spawn ( move ||{ 151for requestin requests{ 152let result =( ||{ 153let mut bytes =vec! [ 0 ; request. limit ]; 154let mut stdin = io:: stdin (). lock (); 155let count =loop { 156match stdin. read ( & mut bytes) { 157Err ( error) if error. kind () == io:: ErrorKind :: Interrupted =>{ 158continue ; 159} 160 result =>break result?, 161} 162}; 163 bytes. truncate ( count); 164Ok ( bytes) 165})(); 166let _ = request. reply . send ( result); 167} 168}) ?; 169} else { 170drop ( requests); 171} 172Ok ( Rc :: new ( Self { 173 stdin, 174 stdin_available, 175stdout : Output :: new ( "avril:stdout" , io:: stdout ()) ?, 176stderr : Output :: new ( "avril:stderr" , io:: stderr ()) ?, 177input : RefCell :: new ( InputState :: Idle ), 178 interrupted, 179})) 180} 181 182fn read ( 183self : & Rc < Self >, 184limit : usize , 185) ->impl Future < Output = io:: Result < Option < ToJsBuffer >>> +use <>{ 186let started =( ||{ 187if !self . stdin_available { 188return Err ( io:: Error :: new ( 189 io:: ErrorKind :: Unsupported , 190"Guest stdin in the REPL is not yet implemented; the REPL owns standard input" , 191)); 192} 193if limit ==0 { 194return Ok ( None ); 195} 196let ( reply, receiver) = oneshot:: channel (); 197let ( cancel, canceled) = oneshot:: channel (); 198{ 199let mut input =self . input . borrow_mut (); 200match &* input{ 201InputState :: Reading ( _) =>{ 202return Err ( io:: Error :: new ( 203 io:: ErrorKind :: WouldBlock , 204"A stdin read is already pending" , 205)); 206} 207InputState :: Canceled =>return Ok ( None ), 208InputState :: Idle =>* input =InputState :: Reading ( cancel), 209} 210} 211let pending =PendingRead ( self . clone ()); 212// Only one read can be submitted; cancellation permanently closes the input. 213self . stdin 214. send ( ReadRequest { 215limit : limit. min ( 64 * 1024 ), 216 reply, 217}) 218. map_err ( |_| io:: Error :: other ( "Stdin worker stopped" )) ?; 219Ok ( Some (( pending, receiver, canceled))) 220})(); 221async move { 222let Some (( pending, receiver, canceled)) = started?else { 223return Ok ( if limit ==0 { 224Some ( ToJsBuffer :: from ( Vec :: new ())) 225} else { 226None 227}); 228}; 229let result =match select ( receiver, canceled). await { 230Either :: Left (( result, _)) => result 231. map_err ( |_| io:: Error :: other ( "Stdin worker stopped" )) 232. and_then ( |result| result) 233. map ( |bytes|{ 234if bytes. is_empty () { 235None 236} else { 237Some ( ToJsBuffer :: from ( bytes)) 238} 239}), 240Either :: Right ( _) =>Ok ( None ), 241}; 242{ 243let mut input = pending. 0 . input . borrow_mut (); 244if matches! ( * input, InputState :: Reading ( _)) { 245* input =InputState :: Idle ; 246} 247} 248 result 249} 250} 251 252fn cancel_read ( & self ) { 253if let InputState :: Reading ( sender) =self . input . replace ( InputState :: Canceled ) { 254let _ = sender. send (()); 255} 256} 257 258fn output ( & self , stderr : bool ) ->& Output { 259if stderr{ & self . stderr } else { & self . stdout } 260} 261 262pub fn flush ( & self ) -> io:: Result <()> { 263let stdout =self . stdout . flush (); 264let stderr =self . stderr . flush (); 265 stdout. and ( stderr) 266} 267 268pub fn flush_interruptible ( & self , interrupted : impl Fn () ->bool ) -> io:: Result <()> { 269let stdout =self . stdout . submit ( None ); 270let stderr =self . stderr . submit ( None ); 271 avril_host:: wait ( 272async { 273let stdout =response ( stdout?). await ; 274let stderr =response ( stderr?). await ; 275 stdout. and ( stderr) 276}, 277 interrupted, 278) 279. unwrap_or ( Ok (())) 280} 281 282pub fn ( & self , stderr : bool , text : String ) -> io:: Result <()> { 283self . output ( stderr). blocking_write ( Some ( text. into_bytes ())) 284} 285 286pub fn extension ( self : & Rc < Self >) ->Extension { 287let read =self . clone (); 288let read_sync =self . clone (); 289let write_sync =self . clone (); 290let cancel =self . clone (); 291let write =self . clone (); 292let flush =self . clone (); 293Extension { 294name : "ext:stdio" , 295dependencies : & [ "ext:webstreams" , "ext:file" ], 296ops : vec! [ 297Op :: asynchronous ( "op_stdio_read" , move |limit : usize | read. read ( limit)), 298Op :: with_scope ( "op_stdio_read_sync" , move |scope, limit : usize |{ 299let pending = read_sync. read ( limit); 300Ok ::< _ , Infallible >( 301 avril_host:: wait ( pending, ||{ 302 avril_host:: interrupted ( scope, & read_sync. interrupted ) 303}) 304. map ( avril_host:: io:: response), 305) 306}), 307Op :: with_scope ( 308"op_stdio_write_sync" , 309move |scope, ( stderr, bytes): ( bool , JsBuffer ) |{ 310let count = bytes. len (); 311let pending = write_sync. output ( stderr). submit ( Some ( bytes. to_vec ())); 312let result = avril_host:: wait ( 313async { response ( pending?). await . map ( |() | count) }, 314 || avril_host:: interrupted ( scope, & write_sync. interrupted ), 315); 316Ok ::< _ , Infallible >( result. map ( avril_host:: io:: response)) 317}, 318), 319Op :: new ( "op_stdio_cancel_read" , move |_: () |{ 320 cancel. cancel_read (); 321Ok ::< _ , io:: Error >(()) 322}), 323Op :: asynchronous ( 324"op_stdio_write" , 325move |( stderr, bytes): ( bool , JsBuffer ) |{ 326let pending = write. output ( stderr). submit ( Some ( bytes. to_vec ())); 327async move { response ( pending?). await } 328}, 329), 330Op :: asynchronous ( "op_stdio_flush" , move |stderr : bool |{ 331let pending = flush. output ( stderr). submit ( None ); 332async move { response ( pending?). await } 333}), 334], 335modules : & [ 336( 337"ext:stdio/main.js" , 338include_str! ( concat! ( env! ( "OUT_DIR" ), "/main.js" )), 339), 340( 341"ext:stdio/sync.js" , 342include_str! ( concat! ( env! ( "OUT_DIR" ), "/sync.js" )), 343), 344], 345entry_point : Some ( "ext:stdio/main.js" ), 346} 347} 348} 349 350# [ cfg ( test )] 351mod tests{ 352use super :: * ; 353 354fn input () ->( Rc < Stdio >, mpsc:: Receiver < ReadRequest >) { 355let ( stdin, requests) = mpsc:: sync_channel ( 1 ); 356let stdio =Rc :: new ( Stdio { 357 stdin, 358stdin_available : true , 359stdout : Output :: new ( "test:stdout" , io:: sink ()). unwrap (), 360stderr : Output :: new ( "test:stderr" , io:: sink ()). unwrap (), 361input : RefCell :: new ( InputState :: Idle ), 362interrupted : Arc :: default (), 363}); 364( stdio, requests) 365} 366 367# [ test ] 368fn reads_reserve_input_before_polling_and_empty_reads_do_not_consume_it () { 369let ( stdio, requests) =input (); 370let first = stdio. read ( 2 ); 371let request = requests. try_recv (). unwrap (); 372assert_eq! ( request. limit , 2 ); 373let second = avril_host:: wait ( stdio. read ( 1 ), ||false ). unwrap (); 374assert_eq! ( second. err (). unwrap (). kind (), io:: ErrorKind :: WouldBlock ); 375let empty = avril_host:: wait ( stdio. read ( 0 ), ||false ). unwrap (). unwrap (); 376assert! ( empty. is_some ()); 377assert! ( requests. try_recv (). is_err ()); 378 request. reply . send ( Ok ( vec! [ 65 , 66 ])). unwrap (); 379assert! ( 380 avril_host:: wait ( first, ||false ) 381. unwrap () 382. unwrap () 383. is_some () 384); 385let next = stdio. read ( 1 ); 386let request = requests. try_recv (). unwrap (); 387 request. reply . send ( Ok ( Vec :: new ())). unwrap (); 388assert! ( avril_host:: wait ( next, ||false ). unwrap (). unwrap (). is_none ()); 389} 390 391# [ test ] 392fn an_interrupted_read_does_not_leave_a_second_reader_on_the_native_queue () { 393let ( stdio, requests) =input (); 394let first = stdio. read ( 1 ); 395let request = requests. try_recv (). unwrap (); 396assert! ( avril_host:: wait ( first, ||true ). is_none ()); 397assert! ( 398 avril_host:: wait ( stdio. read ( 1 ), ||false ) 399. unwrap () 400. unwrap () 401. is_none () 402); 403assert! ( requests. try_recv (). is_err ()); 404assert! ( request. reply . send ( Ok ( vec! [ 65 ])). is_err ()); 405} 406}