tricu

A lazy functional language based on Tree Calculus
Log | Files | Refs | README | LICENSE

browser-client.mjs (1999B)


      1 // Single-flight Worker ownership. Termination rejects the active request and
      2 // discards the entire runtime; no attempt to interrupt/reuse a poisoned RTS.
      3 export class CompilerWorker {
      4   constructor(){
      5     this.worker=new Worker(new URL('./worker.mjs',import.meta.url),{type:'module'});
      6     this.lifetime=new AbortController();this.signal=this.lifetime.signal;
      7     this.next=0;this.pending=null;this.disposed=false;
      8     this.worker.onmessage=({data})=>{
      9       const pending=this.pending;if(!pending||pending.id!==data.id)return;
     10       clearTimeout(pending.timer);this.pending=null;
     11       if(data.ok){
     12         data.text=new TextDecoder('utf-8',{fatal:true}).decode(data.bytes);
     13         data.roundtripMs=performance.now()-pending.start;
     14         pending.resolve(data);
     15       }else{
     16         pending.reject(Error(data.error));
     17         if(data.fatal)this.dispose('Fatal runtime error');
     18       }
     19     };
     20     this.worker.onerror=event=>{event.preventDefault();this.dispose(event.message||'Worker failed');};
     21   }
     22   request(message,timeout=null){
     23     if(this.disposed)return Promise.reject(Error('Disposed Worker'));
     24     if(this.pending)return Promise.reject(Error('Worker is busy'));
     25     if(timeout!==null&&timeout!==Infinity&&(!Number.isFinite(timeout)||timeout<=0))return Promise.reject(Error('Invalid request deadline'));
     26     const id=++this.next,start=performance.now();
     27     return new Promise((resolve,reject)=>{
     28       const timer=timeout===null||timeout===Infinity?undefined:setTimeout(()=>this.dispose('Worker deadline exceeded'),timeout);
     29       this.pending={id,resolve,reject,timer,start};
     30       try{this.worker.postMessage({...message,id});}
     31       catch(error){this.dispose('Request transport failed: '+error.message);}
     32     });
     33   }
     34   dispose(reason='Worker terminated'){
     35     if(this.disposed)return;
     36     this.disposed=true;this.worker.terminate();this.lifetime.abort(Error(reason));
     37     if(this.pending){clearTimeout(this.pending.timer);this.pending.reject(Error(reason));this.pending=null;}
     38   }
     39 }