1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192openAperoopenTimeopenTime_64bitopenTimestampopenClockmoduleHLC=structmoduletypeS=sigmoduleTime:TimemoduleTimestamp:Timestamp.Stypetvalcreate:?csize:int->?delta:Time.t->Uuid.t->tvalnew_timestamp:t->Timestamp.tLwt.t(** [new_timestamp ()] updates the HLC with the local time and returns a new [Timestamp]
which is greater than any previously returned timestamp *)valupdate_with_timestamp:Timestamp.t->t->(unit,error)Result.tLwt.t(** [update_with_timestamp t] checks if the timestamp [t] (that should come from an incoming message)
doesn't exceeds the local time above the specified {! Config.delta }.
If not, the HLC is updated with this timestamps and will further create timestamps that are
greater thant [t] and than any previously returned timestamp. *)endmoduleMake(Clk:ClockwithtypeTime.t=Time_64bit.t)=structmoduleTime=Clk.TimemoduleTimestamp=Timestamp.Make(Time)typet={id:Uuid.t;cmask:Time.t;lmask:Time.t;delta:Time.t;last_time:Time.tGuard.t}letcreate?(csize=8)?(delta=Time_64bit.of_seconds0.1)id=letcmask=letopenInt64insub(shift_left1Lcsize)1Linletlmask=Int64.lognot@@cmaskinletlast_time=Guard.create0Lin{id;cmask;lmask;delta;last_time}letget_ltimeself=Int64.logandtimeself.lmaskletget_ctimeself=Int64.logandtimeself.cmaskletmaxt1t2=letopenTime.Infixinift1>t2thent1elset2letmax3t1t2t3=maxt1t2|>maxt3letnew_timestampself=letopenInt64inletpt=get_l(Clk.now())selfinGuard.guardedself.last_time@@funtime->letl'=get_ltimeselfinletl=maxl'ptinletc=if(Int64.equalll')thensucc(get_ctimeself)else0Linletnew_time=logorlcinGuard.return(Timestamp.createself.idnew_time)new_timeletupdate_with_timestamptimestampself=letopenInt64inletnow=Clk.now()inletmsg_time=Timestamp.get_timetimestampinif(submsg_timenow)>self.deltathenletsource=Timestamp.get_sourcetimestampinleterror_msg=Printf.sprintf"[HLC] incoming timestamp from %s exceeding delta %Ld is rejected: %Ld vs. now: %Ld"(Uuid.to_stringsource)self.deltamsg_timenowinlet_=Logs.warn(funm->m"%s"error_msg)inLwt.return@@Result.fail(`OutOfRange(`Msgerror_msg))elseletpt=get_lnowselfinletlm=get_lmsg_timeselfinletcm=get_cmsg_timeselfinGuard.guardedself.last_time@@funtime->letl'=get_ltimeselfinletl=max3l'msg_timeptinletc=if(Int64.equalll')&&(Int64.equallmsg_time)thensucc(max(get_ctimeself)cm)elseif(Int64.equalll')thensucc(get_ctimeself)elseif(Int64.equalllm)thensucccmelse0Linletnew_time=logorlcinGuard.return(Result.return())new_timeendend