123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116(* Unit tests are in ../../lib_test/shutdown_tests.ml *)openCoreopenImportletdebug=Debug.shutdownlettodo=ref[]letat_shutdownf=letbacktrace=Backtrace.get()inifdebugthenDebug.log"at_shutdown"backtrace[%sexp_of:Backtrace.t];todo:=(backtrace,f)::!todo;;letshutting_down_ref=ref`Noletdefault_force_ref=ref(fun()->Clock.after(sec10.))letdefault_force()=!default_force_refletset_default_forceforce=default_force_ref:=forceletshutting_down()=!shutting_down_ref(* Be careful to ensure [shutdown] doesn't raise just because
stderr is closed *)letignore_exnf=tryf()with|_->();;letexit_reliablystatus=match(exitstatus:Nothing.t)with|exceptionexn->ignore_exn(fun()->Core.Debug.eprints"Caml.exit raised"exn[%sexp_of:Exn.t]);Core.Unix.exit_immediately(ifstatus=0then1elsestatus)|_->.;;letshutdown?forcestatus=ifdebugthenignore_exn(fun()->Debug.log"shutdown"status[%sexp_of:int]);match!shutting_down_refwith|`Yesstatus'->ifstatus<>0&&status'<>0&&status<>status'thenraise_s[%message"shutdown with inconsistent status"(status:int)(status':int)]elseifstatus'=0&&status<>0thenshutting_down_ref:=`Yesstatus|`No->shutting_down_ref:=`Yesstatus;upon(Deferred.all(List.map!todo~f:(fun(backtrace,f)->let%mapresult=Monitor.try_with_or_errorfin(matchresultwith|Ok()->()|Errorerror->ignore_exn(fun()->Core.Debug.eprints"at_shutdown function raised"(error,backtrace)[%sexp_of:Error.t*Backtrace.t]));ifdebugthenignore_exn(fun()->Debug.log"one at_shutdown function finished"backtrace[%sexp_of:Backtrace.t]);result)))(funresults->matchshutting_down()with|`No->assertfalse|`Yesstatus->letstatus=matchOr_error.combine_errors_unitresultswith|Ok()->status|Error_->ifstatus=0then1elsestatusinexit_reliablystatus);letforce=matchforcewith|None->!default_force_ref()|Somef->finuponforce(fun()->ignore_exn(fun()->Debug.log_string"Shutdown forced.");exit_reliably1);;letshutdown_on_unhandled_exn()=Monitor.detach_and_iter_errorsMonitor.main~f:(funexn->tryDebug.log"shutting down due to unhandled exception"exn[%sexp_of:exn];shutdown1with|_->());;letexit?forcestatus=shutdown?forcestatus;Deferred.never();;letdon't_finish_before=letproceed_with_shutdown=Ivar.create()inletnum_waiting=ref0inletcheck()=if!num_waiting=0thenIvar.fillproceed_with_shutdown()inat_shutdown(fun()->check();Ivar.readproceed_with_shutdown);fund->incrnum_waiting;upond(fun()->decrnum_waiting;matchshutting_down()with|`No->()|`Yes_->check());;