tricu

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

io-lifecycle-probe.mjs (4361B)


      1 // Lifecycle building blocks with a real DOM read/provider and native pure work.
      2 // This does NOT execute a v2 language program or bypass the production v2 gate.
      3 export async function run(_control,backend){
      4  const assert=(value,message)=>{if(!value)throw Error(message);};
      5  const base=new URL('../',document.querySelector('script[type=module][src]').src);
      6  const {ioResources:{lifecycle},ioHostWork:{HostWork},runtimeSession}=await import(new URL('dist/probe-api.js',base));
      7  const session=await runtimeSession.createRuntimeSession(backend,false),checks=[];
      8  const {tasks,resources}=lifecycle(1n,'owner-stopped','cycle',[null,null,null]),work=new HostWork(tasks);
      9  const input=document.createElement('input');input.id='lifecycle-test-input';document.body.append(input);
     10  const original=resources.publish(resources.beginAcquire(0),'reader',input);
     11  const {task:read,handles}=resources.spawn(0,true,true,[original]),other=tasks.spawn(0,true,false);
     12  let stale=false;try{resources.lookup(0,original);}catch(error){stale=error.message==='stale-handle';}assert(stale,'old alias survived transfer');
     13  let acknowledge,aborts=0,events=0,signal;
     14  const cleanup=new Promise(resolve=>{acknowledge=resolve;});
     15  const listener=()=>{events++;};input.addEventListener('input',listener);
     16  const release=async id=>{work.release(id,()=>resources.releaseScope(id,r=>r.backend.remove(),e=>e,'cleanup-failed'));await work.wait();const reply=work.take();assert(reply?.outcome.ok,'release failed');return tasks.settle(id,reply.outcome.value);};
     17  const finish=async id=>{tasks.bodyDone(id,{kind:'completed',value:null});assert(tasks.beginRelease(id),'release blocked');return release(id);};
     18  try{
     19   const fixture=(await(await fetch('/runtime-fixtures.json')).json())['pure-number'];
     20   await session.load(new Uint8Array(fixture.bytes),fixture.hash);
     21   work.start(read.key,async abort=>{
     22    signal=abort;const lease=resources.beginLease(read.key,handles[0],'reader','read');
     23    try{await new Promise(resolve=>abort.addEventListener('abort',()=>{aborts++;input.removeEventListener('input',listener);resolve();},{once:true}));
     24    await cleanup;return 'late read value';}finally{resources.endLease(lease.token);}
     25   });
     26   const forbidden=()=>{throw Error('pure work invoked IO hook');};
     27   work.start(other.key,()=>session.run('root',[],'number','pure',{before:forbidden,after:forbidden,read:forbidden,write:forbidden,running:forbidden}));
     28   await work.wait();const reply=work.take();assert(reply.outcome.ok&&reply.outcome.value.value==='42','native sibling result');await finish(other.key);
     29   assert(!signal.aborted&&tasks.state(read.key).hostCalls===1,'read was detached');
     30   input.dispatchEvent(new Event('input'));assert(events===1,'listener not live');
     31   checks.push('pending DOM read does not block sibling native pure evaluation');
     32   tasks.cancel(0,read,'cancel read');work.cancelStopped();work.cancelStopped();
     33   tasks.bodyDone(read.key,{kind:'completed',value:null});
     34   assert(aborts===1&&signal.aborted,'abort delivered more than once');
     35   input.dispatchEvent(new Event('input'));assert(events===1,'cancelled listener still active');
     36   assert(!tasks.beginRelease(read.key)&&input.isConnected,'cancellation request was treated as completion');
     37   const wait=work.wait();acknowledge();await wait;
     38   assert(input.isConnected&&work.take()===null,'resource closed before scope release or late reply escaped');
     39   assert(resources.counts.leases===0,'read lease survived acknowledgement');
     40   assert(tasks.beginRelease(read.key),'acknowledged read did not release');await release(read.key);
     41   assert(!input.isConnected,'owned DOM resource escaped physical release');
     42   tasks.claimJoin(0,read);assert(tasks.pollJoin(0).kind==='cancelled','cancelled join outcome');await finish(0);
     43   assert(tasks.counts.scopes===0&&tasks.counts.tasks===0&&tasks.counts.hosts===0,'lifecycle retained history');
     44   assert(work.counts.pending===0&&work.counts.replies===0,'host work retained history');
     45   assert(resources.counts.resources===0&&resources.counts.leases===0&&resources.counts.admissions===0,'registry retained history');
     46   checks.push('transferred alias invalidation; leased DOM read; one abort; physical acknowledgement and resource release before cancelled join');
     47  }finally{acknowledge?.();input.removeEventListener('input',listener);input.remove();session.dispose();}
     48  return {backend,checks};
     49 }