123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130# 1 "clib/memprof_coq.std.ml"letis_interrupted_=false[@@inline]moduleResource_bind=structlet(let&)fscope=f~scopeend(* We do our own Mutex_aux for OCaml 5.x *)moduleMutex_aux=Mutex_auxmoduleThread_map_core=structopenResource_bindmoduleIMap=Map.Make(structtypet=intletcompare=Stdlib.compareend)type'at={mutex:Mutex.t;mutablemap:'aIMap.t}letcreate()={mutex=Mutex.create();map=IMap.empty}letcurrent_thread()=Thread.id(Thread.self())letgets=(* Concurrent threads do not alter the value for the current
thread, so we do not need a lock. *)IMap.find_opt(current_thread())s.map(* For set and clear we need a lock *)letsetsv=let&()=Mutex_aux.with_locks.mutexinletnew_map=matchvwith|None->IMap.remove(current_thread())s.map|Somev->IMap.add(current_thread())vs.mapins.map<-new_maplet_clears=let&()=Mutex_aux.with_locks.mutexins.map<-IMap.emptyendmoduleMasking=structmoduleT=Thread_map_coretypemask={mutableon:bool}letmask_tls:maskT.t=T.create()(* whether the current thread is masked *)letcreate_mask()=letr={on=false}inT.setmask_tls(Somer);rletdelete_mask()=T.setmask_tlsNoneletis_blocked()=matchT.getmask_tlswith|None->false|Somer->r.onletassert_blocked()=assert(is_blocked())(* The current goal is only to protect from those asynchronous
exceptions raised after dutifully checking that [is_blocked ()]
evaluates to false, and that expect the asynchronous callback to be
called again shortly thereafter (e.g. memprof callbacks). There is
currently no mechanism to delay asynchronous callbacks, so this
strategy cannot work for other kinds of asynchronous callbacks. *)letwith_resource~acquirearg~scope~(release:_->unit)=letmask,delete_after=matchT.getmask_tlswith|None->create_mask(),true|Somer->r,falseinletold_mask=mask.oninletremove_mask()=(* remove the mask flag from the TLS to avoid it growing
uncontrollably when there are lots of threads. *)ifdelete_afterthendelete_mask()elsemask.on<-old_maskinletrelease_and_unmaskrx=matchreleaserwith|()->remove_mask();x|exceptione->remove_mask();raiseeinmask.on<-true;letr=tryacquireargwith|e->mask.on<-old_mask;raiseeinmatchmask.on<-old_mask;scoperwith|(* BEGIN ATOMIC *)y->(mask.on<-true;(* END ATOMIC *)release_and_unmaskry)|(* BEGIN ATOMIC *)exceptione->(mask.on<-true;(* END ATOMIC *)matchPrintexc.get_raw_backtrace()with|bt->(lete=release_and_unmaskreinPrintexc.raise_with_backtraceebt)|exceptionOut_of_memory->raise(release_and_unmaskre))endmoduleThread_map=structincludeThread_map_coreletwith_valuetls~value~scope=letold_value=gettlsin(* FIXME: needs proper masking here as there is a race between
resources and asynchronous exceptions. For now, it is
exception-safe only for exceptions arising from Memprof_callbacks. *)Masking.with_resource~acquire:(fun()->settls(Somevalue))()~scope~release:(fun()->settlsold_value)end