123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196openCoreopenAsync_kernelmoduleDurable=structtype'at=|Void|Buildingof'aDeferred.Or_error.t|Builtof'aendtype'at={mutabledurable:'aDurable.t;to_create:unit->'aDeferred.Or_error.t;to_check_broken:'a->bool;mutableis_intact:bool;is_intact_bus:(bool->unit)Bus.Read_write.t;to_rebuild:('a->'aDeferred.Or_error.t)option}letcreate~to_create~is_broken:to_check_broken?to_rebuild()={durable=Void;to_create;to_check_broken;is_intact=false;is_intact_bus=Bus.create_exn[%here]Arity1~on_subscription_after_first_write:Allow_and_send_last_value~on_callback_raise:ignore;to_rebuild};;letis_broken_and_update_mvartdurable=letis_broken=t.to_check_brokendurableinifBool.(t.is_intact=is_broken)thenBus.writet.is_intact_bus(notis_broken);t.is_intact<-notis_broken;is_broken;;letcreate_or_fail~to_create~is_broken?to_rebuild()=lett=create~to_create~is_broken?to_rebuild()int.to_create()>>=?fundur->ifis_broken_and_update_mvartdurthenreturn(Or_error.error_string"Initial durable value is broken.")else(t.durable<-Builtdur;return(Okt));;letget_durablet=letbuildbuilding=letbuilding=Deferred.Or_error.try_with_join~here:[%here](fun()->let%mapresult=building()inassert(matcht.durablewith|Building_->true|_->false);t.durable<-(matchresultwith(* Errors that show up here will also be returned by [get_durable]. We aren't
losing any information *)|Error_->Void|Okdurable->Builtdurable);result)int.durable<-Buildingbuilding;buildinginmatcht.durablewith|Void->buildt.to_create|Buildingdurable->durable|Builtdurable->ifis_broken_and_update_mvartdurablethenbuild(matcht.to_rebuildwith|None->t.to_create|Someto_rebuild->fun()->to_rebuilddurable)elsereturn(Okdurable);;letwith_t~f=get_durablet>>=?fundurable->ifis_broken_and_update_mvartdurablethenreturn(Or_error.error_string"Durable value was broken immediately after being created or rebuilt.")elsefdurable;;letis_intact_bust=Bus.read_onlyt.is_intact_buslet%test_module_=(modulestructletgo()=Async_kernel_scheduler.Expert.run_cycles_until_no_jobs_remain()letcreate_counter=ref0letfix_counter=ref0moduleFragile=structtypet={mutableis_broken:bool}letis_brokent=t.is_brokenletbreakt=t.is_broken<-trueletcreate_()=return(Ok{is_broken=false})letcreate()=create_counter:=!create_counter+1;create_();;letfix_t=fix_counter:=!fix_counter+1;create_();;endletreset()=create_counter:=0;fix_counter:=0;;letcreate~use_fix~now=letto_rebuild=ifuse_fixthenSomeFragile.fixelseNoneinifnowthencreate_or_fail~to_create:Fragile.create~is_broken:Fragile.is_broken?to_rebuild()>>|ok_exnelsereturn(create~to_create:Fragile.create~is_broken:Fragile.is_broken?to_rebuild());;letpoket=ignore(with_t~f:(fun_t->return(Ok())))let%expect_test_=letpass=reffalsein(create~use_fix:false~now:true>>>funt->matcht.durablewith|Built_->pass:=true|_->());go();print_s[%message(!pass:bool)];[%expect{| (!pass true) |}];;letbuild_break_poke~use_fix~now=reset();(create~use_fix~now>>>funt->with_t~f:(funfragile->Fragile.breakfragile;return(Ok()))>>>funresult->Or_error.ok_exnresult;poket;poket;poket);go();;let%expect_test_=build_break_poke~use_fix:true~now:true;print_s[%message(!create_counter:int)(!fix_counter:int)];[%expect{| ((!create_counter 1) (!fix_counter 1)) |}];;let%expect_test_=build_break_poke~use_fix:true~now:false;print_s[%message(!create_counter:int)(!fix_counter:int)];[%expect{| ((!create_counter 1) (!fix_counter 1)) |}];;let%expect_test_=build_break_poke~use_fix:false~now:true;print_s[%message(!create_counter:int)(!fix_counter:int)];[%expect{| ((!create_counter 2) (!fix_counter 0)) |}];;let%expect_test_=build_break_poke~use_fix:false~now:false;print_s[%message(!create_counter:int)(!fix_counter:int)];[%expect{| ((!create_counter 2) (!fix_counter 0)) |}];;end);;