char/avril
(wip) why'd you have to go and make things so complicaaaaated?
git clone https://git.t4t.associates/char/avril
b7bd857
main
1use std:: cell:: RefCell ; 2use std:: collections:: HashMap ; 3use std:: convert:: Infallible ; 4use std:: io; 5use std:: rc:: Rc ; 6use std:: sync:: atomic::{ AtomicBool , Ordering }; 7use std:: sync::{ Arc , Mutex }; 8use std:: thread:: JoinHandle ; 9 10use avril_caps::{ Capabilities , Category , Pledge }; 11use avril_core::{ Extension , Op , v8}; 12use avril_host:: environment:: Environment ; 13use avril_host:: messaging::{ Broker , Endpoint }; 14use futures:: channel:: oneshot; 15use futures:: task:: AtomicWaker ; 16use tokio:: sync:: Notify ; 17use tokio_util:: sync:: CancellationToken ; 18 19const MAX_ID : u64 =9_007_199_254_740_991 ; 20 21pub struct Request { 22pub specifier : String , 23pub referrer : String , 24pub name : String , 25pub caps : Arc < Capabilities >, 26pub environment : Environment , 27} 28 29/// Runs on the child isolate thread. The host owns loading and runtime assembly. 30pub trait Launch : Send +Sync { 31fn run ( 32& self , 33request : Request , 34control : Arc < Control >, 35inbox : Arc < Endpoint >, 36) ->Result <(), Box < dyn std:: error:: Error >>; 37} 38 39# [ derive ( Default )] 40pub struct Control { 41stopped : AtomicBool , 42terminated : Arc < AtomicBool >, 43processes : CancellationToken , 44isolate : Mutex < Option < v8:: IsolateHandle >>, 45notify : Notify , 46waker : AtomicWaker , 47} 48 49impl Control { 50pub fn is_stopped ( & self ) ->bool { 51self . stopped . load ( Ordering :: SeqCst ) 52} 53 54pub fn termination_flag ( & self ) ->Arc < AtomicBool > { 55self . terminated . clone () 56} 57 58pub fn process_cancellation ( & self ) ->CancellationToken { 59self . processes . clone () 60} 61 62pub fn set_isolate ( & self , isolate : Option < v8:: IsolateHandle >) { 63let mut handle =self . isolate . lock (). unwrap (); 64* handle = isolate; 65if self . is_stopped () 66 &&let Some ( handle) =&* handle 67{ 68 handle. terminate_execution (); 69} 70} 71 72pub fn register_waker ( & self , waker : & std:: task:: Waker ) { 73self . waker . register ( waker); 74} 75 76fn stop ( & self , terminate : bool ) { 77self . stopped . store ( true , Ordering :: SeqCst ); 78self . processes . cancel (); 79if terminate{ 80self . terminated . store ( true , Ordering :: SeqCst ); 81if let Some ( isolate) =&* self . isolate . lock (). unwrap () { 82 isolate. terminate_execution (); 83} 84} 85self . notify . notify_waiters (); 86self . waker . wake (); 87} 88 89pub async fn cancelled ( & self ) { 90loop { 91let notified =self . notify . notified (); 92 tokio:: pin!( notified); 93 notified. as_mut (). enable (); 94if self . stopped . load ( Ordering :: SeqCst ) { 95return ; 96} 97 notified. await ; 98} 99} 100} 101 102pub struct WorkerScope { 103pub control : Arc < Control >, 104pub inbox : Arc < Endpoint >, 105pub url : String , 106pub name : String , 107} 108 109struct Child { 110control : Arc < Control >, 111inbox : Arc < Endpoint >, 112outbox : Arc < Endpoint >, 113thread : Option < JoinHandle <()>>, 114completion : Option < oneshot:: Receiver < Option < String >>>, 115} 116 117impl Child { 118fn terminate ( & self ) { 119self . control . stop ( true ); 120self . inbox . close (); 121} 122} 123 124impl Drop for Child { 125fn drop ( & mut self ) { 126self . terminate (); 127if let Some ( thread) =self . thread . take () { 128let _ = thread. join (); 129} 130} 131} 132 133/// One serialized capability pledge row inherited from the parent scope. 134type PledgeRow =( String , Option < Vec < String >>, Vec < String >); 135 136/// `op_worker_create` arguments: specifier, referrer, worker name, pledges. 137type CreateArgs =( String , String , String , Vec < PledgeRow >); 138 139pub fn init ( 140entry_url : impl Fn () ->String +' static , 141caps : Arc < Capabilities >, 142broker : Arc < Broker >, 143launch : Arc < dyn Launch >, 144environment : Rc < RefCell < Environment >>, 145worker : Option < WorkerScope >, 146) ->Extension { 147let children =Rc :: new ( RefCell :: new ( HashMap ::< u64 , Child >:: new ())); 148let next =Rc :: new ( std:: cell:: Cell :: new ( 0u64 )); 149let metadata = worker 150. as_ref () 151. map ( |scope|( scope. inbox . id (), scope. url . clone (), scope. name . clone ())); 152Extension { 153name : "ext:workers" , 154dependencies : & [ "ext:base" , "ext:url" , "ext:web" ], 155ops : vec! [ 156Op :: new ( "op_worker_metadata" , move |(): () |{ 157Ok ::< _ , Infallible >( metadata. clone ()) 158}), 159Op :: new ( "op_worker_create" , { 160let children = children. clone (); 161move |( specifier, referrer, name, restrictions): CreateArgs |{ 162let environment = environment. borrow (). clone (); 163let caps =Capabilities :: inherit ( caps. clone ()); 164let launch = launch. clone (); 165let restrictions = restrictions 166. into_iter () 167. map ( |( category, allow, deny) |{ 168 category 169. parse ::< Category >() 170. map ( |category|( category, allow, deny)) 171}) 172. collect ::< Result < Pledge , _ >>() 173. map_err ( io:: Error :: other) ?; 174 caps. pledge ( restrictions). map_err ( io:: Error :: other) ?; 175let ( parent, inbox) = broker. pair () ?; 176let control =Arc :: new ( Control :: default ()); 177let ( done, completion) = oneshot:: channel (); 178let child_control = control. clone (); 179let child_inbox = inbox. clone (); 180let referrer =if referrer. is_empty () { 181 caps. cwd () 182. join ( "__worker__.js" ) 183. to_string_lossy () 184. into_owned () 185} else { 186 referrer 187}; 188let id = next. get (); 189if id >MAX_ID { 190return Err ( io:: Error :: other ( "Worker ID space exhausted" )); 191} 192 next. set ( id +1 ); 193let thread = std:: thread:: Builder :: new () 194. name ( format! ( "avril:worker:{id}" )) 195. spawn ( move ||{ 196let result = launch. run ( 197Request { 198 specifier, 199 referrer, 200 name, 201 caps, 202 environment, 203}, 204 child_control. clone (), 205 child_inbox. clone (), 206); 207 child_inbox. close (); 208let error =if child_control. stopped . load ( Ordering :: SeqCst ) { 209None 210} else { 211 result. err (). map ( |error| error. to_string ()) 212}; 213let _ = done. send ( error); 214}) ?; 215 children. borrow_mut (). insert ( 216 id, 217Child { 218 control, 219 inbox, 220outbox : parent. clone (), 221thread : Some ( thread), 222completion : Some ( completion), 223}, 224); 225Ok ::< _ , io:: Error >(( id, parent. id ())) 226} 227}), 228Op :: asynchronous ( "op_worker_wait" , { 229let children = children. clone (); 230move |id : u64 |{ 231let completion = children 232. borrow_mut () 233. get_mut ( & id) 234. and_then ( |child| child. completion . take ()); 235let outbox = children. borrow (). get ( & id). map ( |child| child. outbox . clone ()); 236let children = children. clone (); 237async move { 238let result =match completion{ 239Some ( completion) => completion 240. await 241. unwrap_or ( Some ( "Worker thread stopped unexpectedly" . into ())), 242None =>None , 243}; 244if let Some ( outbox) = outbox{ 245 outbox. drained (). await ; 246} 247 children. borrow_mut (). remove ( & id); 248Ok ::< _ , Infallible >( result) 249} 250} 251}), 252Op :: new ( "op_worker_terminate" , move |id : u64 |{ 253if let Some ( child) = children. borrow (). get ( & id) { 254 child. terminate (); 255} 256Ok ::< _ , Infallible >(()) 257}), 258Op :: new ( "op_worker_close" , move |(): () |{ 259if let Some ( scope) =& worker{ 260 scope. control . stop ( false ); 261 scope. inbox . close (); 262} 263Ok ::< _ , Infallible >(()) 264}), 265Op :: new ( "op_worker_base" , move |(): () |{ 266// The entry URL is the worker API's base, not the constructor's call site. 267Ok ::< _ , Infallible >( entry_url ()) 268}), 269], 270modules : & [( 271"ext:workers/main.js" , 272include_str! ( concat! ( env! ( "OUT_DIR" ), "/main.js" )), 273)], 274entry_point : Some ( "ext:workers/main.js" ), 275} 276}