@ -60,7 +60,13 @@ type 'a boss_message =
 
			
		
	
		
			
				
					  |  GoHome   (* *  all tasks done, prepare for teardown  *) 
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					(* *  convenience function to send data down pipes without forgetting to flush  *)  
			
		
	
		
			
				
					let  marshal_to_pipe  fd  x  =  Marshal . to_channel  fd  x  []  ;  Out_channel . flush  fd  
			
		
	
		
			
				
					let  marshal_to_pipe  fd  x  =  
			
		
	
		
			
				
					  PerfEvent . log  ( fun  logger  -> 
 
			
		
	
		
			
				
					      PerfEvent . log_begin_event  logger  ~ categories : [ " sys " ]  ~ name : " send to pipe "  ()  )  ; 
 
			
		
	
		
			
				
					  Marshal . to_channel  fd  x  []  ; 
 
			
		
	
		
			
				
					  Out_channel . flush  fd  ; 
 
			
		
	
		
			
				
					  PerfEvent . ( log  ( fun  logger  ->  log_end_event  logger  () ) ) 
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					(* *  like [Unix.read] but reads until [len] bytes have been read  *)  
			
		
	
		
			
				
					let  rec  really_read  ? ( pos  =  0 )  ~ len  fd  ~ buf  =  
			
		
	
	
		
			
				
					
						
						
						
							
								 
						
					 
				
				@ -73,13 +79,45 @@ let rec really_read ?(pos = 0) ~len fd ~buf =
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					(* *  return [true] if the [file_descr] is ready for reading after at most [timeout] has  
			
		
	
		
			
				
					     elapsed  * ) 
 
			
		
	
		
			
				
					let  wait_for_updates  file_descr  timeout  =  
			
		
	
		
			
				
					let  wait_for_updates  pool  buffer  =  
			
		
	
		
			
				
					  let  file_descr  =  pool . children_updates  in 
 
			
		
	
		
			
				
					  let  timeout  =  if  TaskBar . is_interactive  pool . task_bar  then  refresh_timeout  else  ` Never  in 
 
			
		
	
		
			
				
					  (*  Use select ( 2 )  so that we can both wait on the pipe of children updates and wait for a 
 
			
		
	
		
			
				
					     timeout .  The  timeout  is  for  giving  a  chance  to  the  taskbar  of  refreshing  from  time  to  time .  * ) 
 
			
		
	
		
			
				
					  let  { Unix . Select_fds . read =  read_fds }  = 
 
			
		
	
		
			
				
					    Unix . select  ~ read : [ file_descr ]  ~ write : []  ~ except : []  ~ timeout  () 
 
			
		
	
		
			
				
					  in 
 
			
		
	
		
			
				
					  match  read_fds  with  _  ::  _  ::  _  ->  assert  false  |  []  ->  false  |  [ _ file_descr ]  ->  true 
 
			
		
	
		
			
				
					  match  read_fds  with 
 
			
		
	
		
			
				
					  |  _  ::  _  ::  _  -> 
 
			
		
	
		
			
				
					      assert  false 
 
			
		
	
		
			
				
					  |  []  -> 
 
			
		
	
		
			
				
					      (*  not ready  *)  None 
 
			
		
	
		
			
				
					  |  [ _ file_descr ]  -> 
 
			
		
	
		
			
				
					      (*  Read one OCaml value at a time. This is done by first reading the header of the marshalled 
 
			
		
	
		
			
				
					       value  ( fixed  size ) ,  then  get  the  total  size  of  the  data  from  that  header ,  then  request  a 
 
			
		
	
		
			
				
					       read  of  the  full  OCaml  value . 
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					       This  way  the  buffer  is  used  for  only  one  OCaml  value  at  a  time .  This  is  simpler  ( values  do 
 
			
		
	
		
			
				
					       not  overlap  across  the  end  of  a  read  and  the  beginning  of  another )  and  means  we  do  not  need 
 
			
		
	
		
			
				
					       a  large  buffer  as  long  as  messages  are  never  bigger  than  the  buffer . 
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					       This  works  somewhat  like  [ Marshal . from_channel ]  but  uses  the  file  descriptor  directly 
 
			
		
	
		
			
				
					       instead  of  an  [ in_channel ] .  Do  * not *  read  from  the  pipe  via  an  [ in_channel ]  as  they  read 
 
			
		
	
		
			
				
					       as  much  as  possible  eagerly .  This  can  empty  the  pipe  without  us  having  a  way  to  tell  that 
 
			
		
	
		
			
				
					       there  is  more  to  read  anymore  since  the  [ select ]  call  will  return  that  there  is  nothing  to 
 
			
		
	
		
			
				
					       read .  * ) 
 
			
		
	
		
			
				
					      really_read  pool . children_updates  ~ buf : buffer  ~ len : Marshal . header_size  ; 
 
			
		
	
		
			
				
					      let  data_size  =  Marshal . data_size  buffer  0  in 
 
			
		
	
		
			
				
					      really_read  pool . children_updates  ~ buf : buffer  ~ pos : Marshal . header_size  ~ len : data_size  ; 
 
			
		
	
		
			
				
					      Some  ( Marshal . from_bytes  buffer  0 ) 
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					let  wait_for_updates  pool  buffer  =  
			
		
	
		
			
				
					  PerfEvent . log  ( fun  logger  -> 
 
			
		
	
		
			
				
					      PerfEvent . log_begin_event  logger  ~ categories : [ " sys " ]  ~ name : " wait for event "  ()  )  ; 
 
			
		
	
		
			
				
					  let  update  =  wait_for_updates  pool  buffer  in 
 
			
		
	
		
			
				
					  PerfEvent . ( log  ( fun  logger  ->  log_end_event  logger  () ) )  ; 
 
			
		
	
		
			
				
					  update 
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					let  killall  pool  ~ slot  status  =  
			
		
	
	
		
			
				
					
						
							
								 
						
						
							
								 
						
						
					 
				
				@ -106,43 +144,26 @@ let process_updates pool buffer =
 
			
		
	
		
			
				
					  has_dead_child  pool 
 
			
		
	
		
			
				
					  | >  Option . iter  ~ f : ( fun  ( slot ,  status )  -> 
 
			
		
	
		
			
				
					         killall  pool  ~ slot  ( Unix . Exit_or_signal . to_string_hum  status )  )  ; 
 
			
		
	
		
			
				
					  let  timeout  =  if  TaskBar . is_interactive  pool . task_bar  then  refresh_timeout  else  ` Never  in 
 
			
		
	
		
			
				
					  if  wait_for_updates  pool . children_updates  timeout  then  ( 
 
			
		
	
		
			
				
					    (*  Read one OCaml value at a time. This is done by first reading the header of the marshalled 
 
			
		
	
		
			
				
					       value  ( fixed  size ) ,  then  get  the  total  size  of  the  data  from  that  header ,  then  request  a 
 
			
		
	
		
			
				
					       read  of  the  full  OCaml  value . 
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					       This  way  the  buffer  is  used  for  only  one  OCaml  value  at  a  time .  This  is  simpler  ( values  do 
 
			
		
	
		
			
				
					       not  overlap  across  the  end  of  a  read  and  the  beginning  of  another )  and  means  we  do  not  need 
 
			
		
	
		
			
				
					       a  large  buffer  as  long  as  messages  are  never  bigger  than  the  buffer . 
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					       This  works  somewhat  like  [ Marshal . from_channel ]  but  uses  the  file  descriptor  directly 
 
			
		
	
		
			
				
					       instead  of  an  [ in_channel ] .  Do  * not *  read  from  the  pipe  via  an  [ in_channel ]  as  they  read 
 
			
		
	
		
			
				
					       as  much  as  possible  eagerly .  This  can  empty  the  pipe  without  us  having  a  way  to  tell  that 
 
			
		
	
		
			
				
					       there  is  more  to  read  anymore  since  the  [ select ]  call  will  return  that  there  is  nothing  to 
 
			
		
	
		
			
				
					       read .  * ) 
 
			
		
	
		
			
				
					    really_read  pool . children_updates  ~ buf : buffer  ~ len : Marshal . header_size  ; 
 
			
		
	
		
			
				
					    let  data_size  =  Marshal . data_size  buffer  0  in 
 
			
		
	
		
			
				
					    really_read  pool . children_updates  ~ buf : buffer  ~ pos : Marshal . header_size  ~ len : data_size  ; 
 
			
		
	
		
			
				
					    let  update  =  Marshal . from_bytes  buffer  0  in 
 
			
		
	
		
			
				
					    match  update  with 
 
			
		
	
		
			
				
					    |  UpdateStatus  ( slot ,  t ,  status )  -> 
 
			
		
	
		
			
				
					        TaskBar . update_status  pool . task_bar  ~ slot  t  status 
 
			
		
	
		
			
				
					    |  Crash  slot  -> 
 
			
		
	
		
			
				
					        let  { pid }  =  pool . slots . ( slot )  in 
 
			
		
	
		
			
				
					        (*  clean crash, give the child process a chance to cleanup  *) 
 
			
		
	
		
			
				
					        Unix . wait  ( ` Pid  pid )  | >  ignore  ; 
 
			
		
	
		
			
				
					        killall  pool  ~ slot  " see backtrace above " 
 
			
		
	
		
			
				
					    |  Ready  slot  ->  ( 
 
			
		
	
		
			
				
					        TaskBar . tasks_done_add  pool . task_bar  1  ; 
 
			
		
	
		
			
				
					        match  pool . tasks  with 
 
			
		
	
		
			
				
					        |  []  -> 
 
			
		
	
		
			
				
					            TaskBar . update_status  pool . task_bar  ~ slot  ( Mtime_clock . now  () )  " idle "  ; 
 
			
		
	
		
			
				
					            pool . idle_children  <-  pool . idle_children  +  1 
 
			
		
	
		
			
				
					        |  x  ::  tasks  -> 
 
			
		
	
		
			
				
					            pool . tasks  <-  tasks  ; 
 
			
		
	
		
			
				
					            let  { down_pipe }  =  pool . slots . ( slot )  in 
 
			
		
	
		
			
				
					            marshal_to_pipe  down_pipe  ( Do  x )  )  ) 
 
			
		
	
		
			
				
					  match  wait_for_updates  pool  buffer  with 
 
			
		
	
		
			
				
					  |  Some  ( UpdateStatus  ( slot ,  t ,  status ) )  -> 
 
			
		
	
		
			
				
					      TaskBar . update_status  pool . task_bar  ~ slot  t  status 
 
			
		
	
		
			
				
					  |  Some  ( Crash  slot )  -> 
 
			
		
	
		
			
				
					      let  { pid }  =  pool . slots . ( slot )  in 
 
			
		
	
		
			
				
					      (*  clean crash, give the child process a chance to cleanup  *) 
 
			
		
	
		
			
				
					      Unix . wait  ( ` Pid  pid )  | >  ignore  ; 
 
			
		
	
		
			
				
					      killall  pool  ~ slot  " see backtrace above " 
 
			
		
	
		
			
				
					  |  Some  ( Ready  slot )  ->  ( 
 
			
		
	
		
			
				
					      TaskBar . tasks_done_add  pool . task_bar  1  ; 
 
			
		
	
		
			
				
					      match  pool . tasks  with 
 
			
		
	
		
			
				
					      |  []  -> 
 
			
		
	
		
			
				
					          TaskBar . update_status  pool . task_bar  ~ slot  ( Mtime_clock . now  () )  " idle "  ; 
 
			
		
	
		
			
				
					          pool . idle_children  <-  pool . idle_children  +  1 
 
			
		
	
		
			
				
					      |  x  ::  tasks  -> 
 
			
		
	
		
			
				
					          pool . tasks  <-  tasks  ; 
 
			
		
	
		
			
				
					          let  { down_pipe }  =  pool . slots . ( slot )  in 
 
			
		
	
		
			
				
					          marshal_to_pipe  down_pipe  ( Do  x )  ) 
 
			
		
	
		
			
				
					  |  None  -> 
 
			
		
	
		
			
				
					      () 
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					(* *  terminate all worker processes  *)  
			
		
	
	
		
			
				
					
						
							
								 
						
						
							
								 
						
						
					 
				
				@ -220,7 +241,13 @@ let fork_child ~child_prelude ~slot (updates_r, updates_w) ~f =
 
			
		
	
		
			
				
					      in 
 
			
		
	
		
			
				
					      ProcessPoolState . update_status  :=  update_status  ; 
 
			
		
	
		
			
				
					      let  orders_ic  =  Unix . in_channel_of_descr  to_child_r  in 
 
			
		
	
		
			
				
					      let  receive_from_parent  ()  =  Marshal . from_channel  orders_ic  in 
 
			
		
	
		
			
				
					      let  receive_from_parent  ()  = 
 
			
		
	
		
			
				
					        PerfEvent . log  ( fun  logger  -> 
 
			
		
	
		
			
				
					            PerfEvent . log_begin_event  logger  ~ categories : [ " sys " ]  ~ name : " receive from pipe "  ()  )  ; 
 
			
		
	
		
			
				
					        let  x  =  Marshal . from_channel  orders_ic  in 
 
			
		
	
		
			
				
					        PerfEvent . ( log  ( fun  logger  ->  log_end_event  logger  () ) )  ; 
 
			
		
	
		
			
				
					        x 
 
			
		
	
		
			
				
					      in 
 
			
		
	
		
			
				
					      child_loop  ~ slot  send_to_parent  receive_from_parent  ~ f  ; 
 
			
		
	
		
			
				
					      Out_channel . close  updates_oc  ; 
 
			
		
	
		
			
				
					      In_channel . close  orders_ic  ;