Action.PrimitivePrimitive actions.
After an action invocation as been created it is first polled and if that doesn't yield a synchronization, it is blocked. A primitive action needs to define what happens during these two phases by providing corresponding functions.
Debug tip. Heisen synchronization hangs usually happen during the block phase. Unconditionally returning None in the poll phase helps with reproducing the hang more often.
type 'a poll = continue:('a Result.t -> unit -> unit) -> (unit -> unit) optionThe type for optimisitically polling a primitive action for sychronization.
If the polling phase is able to provide a result r for the invocation it should apply continue r and return the resulting thunk. If it fails it must return None and not touch or store continue. Always returning None is a legitimate implementation of the poll phase, see poll_is_none. For an invocation, this function is always guaranteed to be called before block does.
Warning. The function must not raise. If it's meaningful for your action to do so you should continue with a Result.t.Exn value. If an unexpected exception occurs the current implementation throws it in continue but do not rely on this as it may change in the future.
val poll_is_none : 'a pollpoll_is_none is a polling function that always returns None.
type 'a block = blocked:'a Blocked.t -> unitThe type for blocking on a primitive action.
This function is only invoked if the poll phase returned None. It assumes that the entity blocked on the invocation has been suspended and that it can be resumed by unblocking the blocked value.
The function must try to synchronize again like it did in the poll phase before storing the blocked somewhere until it can unblocked by another entity. If while doing so you discover that the state of blocked is already Blocked.state.Synced, you can just throw the value away and do nothing. In fact if you store blocked in data structures and never get to synchronize it you should make sure to periodically check if it reach that state and garbage collect it (usually when you are adding or removing blocked values from your data structures).
There are two reasons why blocked value may synchronize without you being in charge:
Action.choose invocation. The state of the blocked value represents that choice which is shared among all invocations of the choice. If another invocation synchronizes the choice, the state becomes Blocked.state.Synced and you must not synchronize the blocked value (that would invoke the Action.invoke continuation twice and break the semantics of Action.choose).Ports do. As soon as blocked is published for others to see, other action invocations may be trying to synchronize it concurrently.Warning. The function must not raise. If it is meaningful for your action to do so you should unblock blocked with a Result.t.Exn value. If an unexpected exception occurs the current implementation unblocks blocked with it but do not rely on this it may change in the future.
make ~meta ~poll ~block is a primitive action whose invocation is first polled with poll and if that does not yield a synchronization blocked with block.
type 'a t = 'a primitiveThe type for bare primitive actions.