Legend:
Page
Library
Module
Module type
Parameter
Class
Class type
Source
Source file moonpool_dpool.ml
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142(* shared with {!Moonpool.Bb_queue}/{!Moonpool.Lock}; we can't depend on
[moonpool] itself (layering would be circular), so these live in
[moonpool.private] instead. *)moduleBb_queue=Bb_queue_moduleLock=Lock_typedomain=Domain_.ttypeevent=|Runof(unit->unit)(** Run this function *)|Decr(** Decrease count *)(* State for a domain worker. It should not do too much except for starting
new threads for pools. *)typeworker_state={q:eventBb_queue.t;th_count:intAtomic.t;(** Number of threads on this *)}typepool=(worker_stateoption*Domain_.toption)Lock.tarrayletmax_number_of_domains_=max1(Domain_.recommended_number())(* lazily created pool (for fork safety; safe to fork until the first domain
pool is created). *)letdomains_:pooloptionLock.t=Lock.createNone(** main work loop for a domain worker.
A domain worker does two things:
- run functions it's asked to (mainly, to start new threads inside it)
- decrease the refcount when one of these threads stops. The thread will
notify the domain that it's exiting, so the domain can know how many
threads are still using it. If all threads exit, the domain polls a bit
(in case new threads are created really shortly after, which happens with
a [Pool.with_] or [Pool.create() … Pool.shutdown()] in a tight loop), and
if nothing happens it tries to stop to free resources. *)letwork_(domains:pool)idx(st:worker_state):unit=Signals_.ignore_signals_();letmain_loop()=letcontinue=reftrueinwhile!continuedomatchBb_queue.popst.qwith|Runf->(tryf()withexn->(* that invariant is just too important *)Printf.eprintf"moonpool.dpool: fatal error, callback raised with %s\n%!"(Printexc.to_stringexn);exit1)|Decr->ifAtomic.fetch_and_addst.th_count(-1)=1then(continue:=false;(* wait a bit, we might be needed again in a short amount of time *)tryfor_n_attempt=1to50doThread.delay0.001;ifAtomic.getst.th_count>0then((* needed again! *)continue:=true;raiseExit)donewithExit->())doneinwhilemain_loop();(* exit: try to remove ourselves from [domains]. If that fails, keep living. *)letis_alive=Lock.update_mapdomains.(idx)(function|None,_->assertfalse|Some_st',dom->assert(st==_st');ifAtomic.getst.th_count>0then(* still alive! *)(Somest,dom),trueelse(None,dom),false)inis_alivedo()done;()letinit_domains_():pool=Lock.update_mapdomains_@@function|Somedomains->Somedomains,domains|None->assert(Domain_.is_main_domain());letdomains=Array.initmax_number_of_domains_(fun_->Lock.create(None,None))inletw={th_count=Atomic.make1;q=Bb_queue.create()}indomains.(0)<-Lock.create(Somew,None);ignore(Thread.create(fun()->work_domains0w)():Thread.t);Somedomains,domainslet[@inline]max_number_of_domains():int=max_number_of_domains_letrun_on(i:int)(f:unit->unit):unit=assert(i<max_number_of_domains_);letdomains=init_domains_()inletw:worker_state=Lock.update_mapdomains.(i)(function|(Somew,_)asst->Atomic.incrw.th_count;st,w|None,dying_dom->(* join previous dying domain, to free its resources, if any *)Option.iterDomain_.joindying_dom;letw={th_count=Atomic.make1;q=Bb_queue.create()}inletworker:domain=Domain_.spawn(fun()->work_domainsiw)in(Somew,Someworker),w)inBb_queue.pushw.q(Runf)letdecr_on(i:int):unit=assert(i<max_number_of_domains_);letdomains=matchLock.getdomains_with|Somedomains->domains|None->assertfalseinmatchLock.getdomains.(i)with|Somest,_->Bb_queue.pushst.qDecr|None,_->()letrun_on_and_wait(i:int)(f:unit->'a):'a=letq=Bb_queue.create()inrun_oni(fun()->letx=f()inBb_queue.pushqx);Bb_queue.popq