tricu

An interpreted language for exploring Tree Calculus
Log | Files | Refs | README | LICENSE

browser-client.mjs (1890B)


      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.next=0;this.pending=null;this.disposed=false;
      7     this.worker.onmessage=({data})=>{
      8       const pending=this.pending;if(!pending||pending.id!==data.id)return;
      9       clearTimeout(pending.timer);this.pending=null;
     10       if(data.ok){
     11         data.text=new TextDecoder('utf-8',{fatal:true}).decode(data.bytes);
     12         data.roundtripMs=performance.now()-pending.start;
     13         pending.resolve(data);
     14       }else{
     15         pending.reject(Error(data.error));
     16         if(data.fatal)this.dispose('Fatal runtime error');
     17       }
     18     };
     19     this.worker.onerror=event=>{event.preventDefault();this.dispose(event.message||'Worker failed');};
     20   }
     21   request(message,timeout=null){
     22     if(this.disposed)return Promise.reject(Error('Disposed Worker'));
     23     if(this.pending)return Promise.reject(Error('Worker is busy'));
     24     if(timeout!==null&&timeout!==Infinity&&(!Number.isFinite(timeout)||timeout<=0))return Promise.reject(Error('Invalid request deadline'));
     25     const id=++this.next,start=performance.now();
     26     return new Promise((resolve,reject)=>{
     27       const timer=timeout===null||timeout===Infinity?undefined:setTimeout(()=>this.dispose('Worker deadline exceeded'),timeout);
     28       this.pending={id,resolve,reject,timer,start};
     29       try{this.worker.postMessage({...message,id});}
     30       catch(error){this.dispose('Request transport failed: '+error.message);}
     31     });
     32   }
     33   dispose(reason='Worker terminated'){
     34     if(this.disposed)return;
     35     this.disposed=true;this.worker.terminate();
     36     if(this.pending){clearTimeout(this.pending.timer);this.pending.reject(Error(reason));this.pending=null;}
     37   }
     38 }