char/avril
(wip) why'd you have to go and make things so complicaaaaated?
git clone https://git.t4t.associates/char/avril
f4a0f5b
main
1pub mod environment; 2pub mod io; 3pub mod messaging; 4 5use std:: future:: Future ; 6use std:: sync:: Arc ; 7use std:: sync:: atomic::{ AtomicBool , Ordering }; 8 9use tokio:: runtime::{ Handle , Runtime }; 10 11/// The Tokio runtime shared by the filesystem and network extensions. 12pub struct Host ( Option < Runtime >); 13 14impl Host { 15pub fn new () -> std:: io:: Result < Self > { 16let runtime = tokio:: runtime:: Builder :: new_multi_thread () 17// A second worker keeps decompression or a large message from stalling other connections. 18. worker_threads ( 2 ) 19. thread_name ( "avril:host" ) 20. enable_all () 21. build () ?; 22Ok ( Self ( Some ( runtime))) 23} 24 25pub fn handle ( & self ) ->Handle { 26self . 0 . as_ref (). unwrap (). handle (). clone () 27} 28} 29 30impl Drop for Host { 31fn drop ( & mut self ) { 32// getaddrinfo may still be running in Tokio's blocking pool after an abort. 33if let Some ( runtime) =self . 0 . take () { 34 runtime. shutdown_background (); 35} 36} 37} 38 39pub fn interrupted ( scope : & avril_core:: v8:: PinScope , flag : & AtomicBool ) ->bool { 40if flag. load ( Ordering :: SeqCst ) { 41// V8 may not observe a termination request until this native call returns. 42 scope. terminate_execution (); 43true 44} else { 45 scope. is_execution_terminating () 46} 47} 48 49struct WakeThread ( std:: thread:: Thread ); 50 51impl std:: task:: Wake for WakeThread { 52fn wake ( self : Arc < Self >) { 53self . 0 . unpark (); 54} 55 56fn wake_by_ref ( self : & Arc < Self >) { 57self . 0 . unpark (); 58} 59} 60 61// Worker and REPL loops may already be inside a futures executor. Poll only 62// this op's native work, without entering an executor or pumping JavaScript. 63pub fn wait < F : Future >( future : F , interrupted : impl Fn () ->bool ) ->Option < F :: Output > { 64let waker = std:: task:: Waker :: from ( Arc :: new ( WakeThread ( std:: thread:: current ()))); 65let mut context = std:: task:: Context :: from_waker ( & waker); 66// A synchronous wait cannot yield to the outer task to replenish its budget. 67let mut pending = std:: pin:: pin!( tokio:: task:: unconstrained ( future)); 68loop { 69if interrupted () { 70return None ; 71} 72if let std:: task:: Poll :: Ready ( result) = pending. as_mut (). poll ( & mut context) { 73return Some ( result); 74} 75 std:: thread:: park_timeout ( std:: time:: Duration :: from_millis ( 10 )); 76} 77} 78 79# [ cfg ( test )] 80mod tests{ 81use super ::{ Host , wait}; 82use std:: time::{ Duration , Instant }; 83 84# [ test ] 85fn synchronous_wait_drains_tokio_io_inside_an_async_task () { 86let host =Host :: new (). unwrap (); 87let ( sender, mut receiver) = tokio:: sync:: mpsc:: channel ( 1_000 ); 88for valuein 0 ..1_000 { 89 sender. try_send ( value). unwrap (); 90} 91drop ( sender); 92 93 host. handle (). block_on ( async { 94let deadline =Instant :: now () +Duration :: from_secs ( 2 ); 95let result =wait ( 96async { 97let mut values =Vec :: new (); 98while let Some ( value) = receiver. recv (). await { 99 values. push ( value); 100} 101 values 102}, 103 ||Instant :: now () >= deadline, 104); 105assert_eq! ( result, Some (( 0 ..1_000 ). collect ())); 106}); 107} 108}