123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192(**************************************************************************)(* *)(* OCaml *)(* *)(* KC Sivaramakrishnan, Indian Institute of Technology, Madras *)(* *)(* Copyright 2021 Indian Institute of Technology, Madras *)(* *)(* All rights reserved. This file is distributed under the terms of *)(* the GNU Lesser General Public License version 2.1, with the *)(* special exception on linking described in the file LICENSE. *)(* *)(**************************************************************************)externalregister_named_value:string->'a->unit="caml_register_named_value"type'at=..externalperform:'at->'a="%perform"exceptionOut_of_fibers=Out_of_fiberstypeexn+=Unhandled:'at->exnexceptionContinuation_already_resumedlet()=letprinter=function|Unhandledx->letmsg=Printf.sprintf"Stdlib.Effect.Unhandled(%s)"(Printexc.string_of_extension_constructor@@Obj.reprx)inSomemsg|_->Nonein(* need magic because jkind doesn't know [t] crosses portability and
contention *)Printexc.Safe.register_printer(Obj.magic_portableprinter)(* Register the exceptions so that the runtime can access it *)type_t+=Should_not_see_this__:unittlet_=Callback.Safe.register_exception"Effect.Unhandled"(UnhandledShould_not_see_this__)let_=Callback.Safe.register_exception"Effect.Continuation_already_resumed"Continuation_already_resumedtype_t+=Preemption:unittlet()=register_named_value"Effect.Preemption"Preemption(* A paused fiber, awaiting an 'a, which terminates with an 'x,
equipped with a handler that produces a 'b *)type(-'a,'x,+'b)cont:valuemodnon_float(* A last_fiber is a tagged pointer, so does not keep the fiber alive.
It must never be the sole reference to the fiber, and is only used to cache
the final fiber in the linked list formed by [cont.fiber->parent]. *)typelast_fiber[@@immediate]externalcont_set_last_fiber:_cont->last_fiber->unit="%setfield1"externalresume:('a,_,'b)cont->('c->'a)->'c->'b="%resume"type('a,'x,'b)effc='at->('a,'x,'b)cont->last_fiber->'bexternalwith_stack:('x->'b)->(exn->'b)->('a.('a,'x,'b)effc)->('d->'x)->'d->'b="%with_stack"externalupdate_cont_handler_noexc:('a,'x,_)cont->('x->'b)->(exn->'b)->('a2.('a2,'x,'b)effc)->('a,'x,'b)cont="caml_continuation_update_handler_noexc"(* Retrieve the stack from a [cont]inuation, update its handlers, and run
[f x] using it. *)letwith_handlercontvaluecexnc(effc:'a.('a,_,_)effc)fx=resume(* FIXME: There's a race condition here - if multiple threads call
[with_handler] on the same continuation at once, they could be
interleaved, causing a segfault rather
than an exception. *)(update_cont_handler_noexccontvaluecexnceffc)fxmoduleDeep=structtype('a,'b)continuation=|Cont:('a,'x,'b)cont->('a,'b)continuation[@@unboxed]letcontinue(Contk)v=resumek(funx->x)vletdiscontinue(Contk)e=resumek(fune->raisee)eletdiscontinue_with_backtrace(Contk)ebt=resumek(fune->Printexc.raise_with_backtraceebt)etype('a,'b)handler={retc:'a->'b;exnc:exn->'b;effc:'c.'ct->(('c,'b)continuation->'b)option}externalreperform:'at->('a,_,'b)cont->last_fiber->'b="%reperform"letmatch_withcomparghandler=leteffceffklast_fiber=matchhandler.effceffwith|Somef->cont_set_last_fiberklast_fiber;f(Contk)|None->reperformeffklast_fiberinwith_stackhandler.retchandler.exnceffccompargtype'aeffect_handler={effc:'b.'bt->(('b,'a)continuation->'a)option}lettry_withcomparghandler=leteffc'effklast_fiber=matchhandler.effceffwith|Somef->cont_set_last_fiberklast_fiber;f(Contk)|None->reperformeffklast_fiberinwith_stack(funx->x)(fune->raisee)effc'compargexternalget_callstack:('a,'b)continuation->int->Printexc.raw_backtrace="caml_get_continuation_callstack"endmoduleShallow=structtype('a,'b)continuation=|Cont:('a,'b,'x)cont->('a,'b)continuation[@@unboxed]letfiber:typeab.(a->b)->(a,b)continuation=funf->letmoduleM=structtype_t+=Initial_setup__:atendinletexceptionEof(a,b)continuationinletf'()=f(performM.Initial_setup__)inleterror_=failwith"impossible"inleteffc(typea2)(eff:a2t)(k:(a2,b,_)cont)last_fiber=matcheffwith|M.Initial_setup__->cont_set_last_fiberklast_fiber;raise_notrace(E(Contk))(* We need to handle [Preemption] here since it's triggered automatically
on a timer, and might arrive while we're setting up the fiber *)|Preemption->cont_set_last_fiberklast_fiber;resumek(funx->x)()|_->error()inmatchwith_stackerrorerroreffcf'()with|exceptionEk->k|_->error()type('a,'b)handler={retc:'a->'b;exnc:exn->'b;effc:'c.'ct->(('c,'a)continuation->'b)option}externalreperform:'at->('a,'b,_)cont->last_fiber->'c="%reperform"letcontinue_gen(Contk)resume_funvhandler=leteffceffklast_fiber=matchhandler.effceffwith|Somef->cont_set_last_fiberklast_fiber;f(Contk)|None->reperformeffklast_fiberinwith_handlerkhandler.retchandler.exnceffcresume_funvletcontinue_withkvhandler=continue_genk(funx->x)vhandlerletdiscontinue_withkvhandler=continue_genk(fune->raisee)vhandlerletdiscontinue_with_backtracekvbthandler=continue_genk(fune->Printexc.raise_with_backtraceebt)vhandlerexternalget_callstack:('a,'b)continuation->int->Printexc.raw_backtrace="caml_get_continuation_callstack"end