<?php
declare(strict_types=1);namespace Aen\RateLab;
final class Concurrency {
 public static function run(string $backend='files',bool $locked=true):array {
  $namespace='aen_race_'.bin2hex(random_bytes(6));$at=microtime(true)+2;$jobs=[];$n=8;
  try{
   for($i=0;$i<$n;$i++){
    $p=proc_open([PHP_BINARY,dirname(__DIR__).'/bin/worker.php',$backend,$namespace,(string)$at,$locked?'1':'0'],[0=>['pipe','r'],1=>['pipe','w'],2=>['pipe','w']],$pipes);
    if(!is_resource($p))throw new \RuntimeException('Cannot start PHP worker');fclose($pipes[0]);$jobs[]=[$p,$pipes];
   }
   $events=[];foreach($jobs as [$p,$pipes]){$out=stream_get_contents($pipes[1]);$err=stream_get_contents($pipes[2]);fclose($pipes[1]);fclose($pipes[2]);$exit=proc_close($p);if($exit!==0)throw new \RuntimeException('Worker failed: '.$err);$events[]=json_decode($out,true,16,JSON_THROW_ON_ERROR);}
   usort($events,fn($a,$b)=>$a['finished_at']<=>$b['finished_at']);
   foreach($events as &$e){$e['t_ms']=round(($e['finished_at']-$at)*1000,2);$e['user']='shared';}unset($e);
   $accepted=count(array_filter($events,fn($e)=>$e['accepted']));
   return ['backend'=>$backend,'locks'=>$locked,'workers'=>$n,'limit'=>3,'accepted'=>$accepted,'rejected'=>$n-$accepted,'artificial_storage_delay_ms'=>120,'unique_processes'=>count(array_unique(array_column($events,'pid'))),'events'=>$events];
  }finally{foreach($jobs as [$p,$pipes])if(is_resource($p)){proc_terminate($p);foreach($pipes as $pipe)if(is_resource($pipe))fclose($pipe);proc_close($p);}}
 }
}
