123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107type'astate=|Resolvedof'a|UnresolvedofBroadcast.ttype!'apromise={id:Trace.id;state:'astateAtomic.t;(* Note: we always switch to Resolved before broadcasting *)}type+!'attype-!'autype'aor_exn=('a,exn)resulttletto_public_promise:'apromise->'at=Obj.magicletto_public_resolver:'apromise->'au=Obj.magicletof_public_promise:'at->'apromise=Obj.magicletof_public_resolver:'au->'apromise=Obj.magicletcreate_with_idid=lett={id;state=Atomic.make(Unresolved(Broadcast.create()));}into_public_promiset,to_public_resolvertletcreate?label()=letid=Trace.mint_id()inTrace.create_obj?labelidPromise;create_with_ididletcreate_resolvedx=letid=Trace.mint_id()inTrace.create_objidPromise;to_public_promise{id;state=Atomic.make(Resolvedx)}letawaitt=lett=of_public_promisetinmatchAtomic.gett.statewith|Resolvedx->Trace.gett.id;x|Unresolvedb->Suspend.enter"Promise.await"(functxenqueue->matchBroadcast.suspendb(fun()->enqueue(Ok()))with|None->()(* We got resumed immediately *)|Somerequest->matchAtomic.gett.statewith|Resolved_->(* The promise was resolved as we were suspending.
Resume now if we haven't already done so. *)ifBroadcast.cancelrequestthenenqueue(Ok())|Unresolved_->(* We observed the promise to be still unresolved after registering a waiter.
Therefore any resolution must happen after we were registered and we will be notified. *)Trace.try_gett.id;Cancel.Fiber_context.set_cancel_fnctx(funex->ifBroadcast.cancelrequestthenenqueue(Errorex)(* else already resumed *)));matchAtomic.gett.statewith|Resolvedx->Trace.gett.id;x|Unresolved_->assertfalseletawait_exnt=matchawaittwith|Okx->x|Errorex->raiseexlettry_resolvetv=letrecresolve'tv=matchAtomic.gett.statewith|Resolved_->false|Unresolvedbasprev->ifAtomic.compare_and_sett.stateprev(Resolvedv)then(Trace.putt.id;Broadcast.resume_allb;true)else((* Otherwise, the promise was already resolved. Retry (to get the error). *)resolve'tv)inresolve'(of_public_resolvert)vletresolveux=ifnot(try_resolveux)theninvalid_arg"Can't resolve already-resolved promise"letresolve_okux=resolveu(Okx)letresolve_errorux=resolveu(Errorx)letpeekt=lett=of_public_promisetinmatchAtomic.gett.statewith|Unresolved_->None|Resolvedx->Somexletidt=lett=of_public_promisetint.idletis_resolvedt=Option.is_some(peekt)