123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101moduleA=Ambient_context_atomic.Atomictypekey=intlet[@inline]get_key_():key=Thread.id(Thread.self())moduleKey_map_=Map.Make(structtypet=keyletcompare:t->t->int=compareend)type'at='arefKey_map_.tA.t(** The TLS variable is made of a global atomic reference
(which has very low contention: it's modified only when a
thread is started/stopped).
Inside that atomic variable, is a map from thread ID to a mutable [ref]
holding the actual data. Because this [ref] is only ever accessed
by the thread with this given ID, it's safe to modify. *)letcreate():_t=A.makeKey_map_.emptylet[@inline]get_exn(self:_t)=letm=A.getselfinletkey=get_key_()in!(Key_map_.findkeym)let[@inline]getself=trySome(get_exnself)withNot_found->None(* remove reference for the key *)letremove_ref_selfkey:unit=whileletm=A.getselfinletm'=Key_map_.removekeyminnot(A.compare_and_setselfmm')doThread.yield()doneletset_ref_selfkey(r:_ref):unit=whileletm=A.getselfinletm'=Key_map_.addkeyrminnot(A.compare_and_setselfmm')doThread.yield()done(* get or associate a reference to [key], and return it.
Also return a function to remove the reference if we just created it. *)letget_or_create_ref_(self:_t)key~v:_ref*_option=tryletr=Key_map_.findkey(A.getself)inletold=!rinr:=v;r,SomeoldwithNot_found->letr=refvinset_ref_selfkeyr;r,Noneletset(self:_t)v:unit=letkey=get_key_()inlet_,_=get_or_create_ref_selfkey~vin()letremove(self:_t):unit=letkey=get_key_()inremove_ref_selfkeyletget_or_create~create(self:'at):'a=letkey=get_key_()intryletr=Key_map_.findkey(A.getself)in!rwithNot_found->Gc.finalise(fun_->remove_ref_selfkey)(Thread.self());letv=create()inletr=refvinset_ref_selfkeyr;vletwith_selfvf=letkey=get_key_()inletr,old=get_or_create_ref_selfkey~vinletrestore_():unit=matcholdwith|None->remove_ref_selfkey|Someold->r:=oldintryletres=foldinrestore_();reswithe->restore_();raisee