Does process.call in gleam/erlang/process ever cancel the callee, or only stop the caller waiting?
0 reputation · 29 Sept 2022, 14:52 UTC
0 reputation · 29 Sept 2022, 14:52 UTC
I'm designing a Gleam service on the BEAM target that makes blocking requests to worker processes using process.call from gleam/erlang/process with a timeout in milliseconds. My understanding from the module documentation is that when the timeout elapses, the caller gets a timeout outcome instead of blocking forever — but the callee keeps running and may still deliver a late reply.
That raises a design question I can't resolve from the docs alone: if I reuse the same subject for subsequent calls, a stale reply from an earlier timed-out call could sit in my mailbox and be misread as the answer to the next call. The documented alternatives for real cancellation seem to be process.kill or monitoring/linking, which are explicit and abrupt rather than timeout-driven.
My constraints: I want graceful degradation on slow workers, no cross-contamination between sequential calls, and ideally no orphaned workers burning resources after their caller has given up.
Specifically:
33775 reputation · 29 Sept 2022, 16:01 UTC
process.call never cancels the callee. A timeout only stops the caller from waiting: the call returns a timeout outcome, while the worker process keeps running and will still send its reply whenever it finishes. That late reply lands in the caller's mailbox like any other message. This is standard BEAM send-and-receive-with-timeout behavior, and it has been stable across Erlang/OTP and recent gleam/erlang releases — there is no version pin that changes it.
Two facts follow from this. First, reusing one subject across sequential calls is risky: a reply from a timed-out call can be consumed as the answer to the next call. Second, the fix is correlation, not flushing.
The intended pattern is to give each call a unique identity and only accept the matching reply:
process.new_subject()), orA fresh subject per call is the simplest and is cheap on the BEAM — subjects are just aliases over your mailbox. Selective receive with a unique tag works equally well if you prefer one long-lived subject, but be aware that unmatched stale messages accumulate in the mailbox until something receives and discards them; a periodic flush of messages older than the current reference keeps memory bounded.
There is no supported mechanism where the timeout itself signals the callee. Cancellation is always explicit. Your realistic options, from gentlest to most abrupt:
stop message on a control subject it selects on. This is the clean option and lets the worker release resources gracefully.DOWN notification so you know it actually exited.process.kill: abrupt and can leave the worker's internal state (or anything it was writing to) inconsistent. Reserve it for workers that ignore shutdown messages.For your constraints — graceful degradation, no cross-contamination, no orphaned workers — the combination that fits is: fresh subject per call for isolation, plus a monitor-and-stop-message on timeout so workers don't burn resources after the caller gives up.
You can confirm all of this in a small test: spawn a worker that sleeps longer than your timeout, call it with process.call, and assert that (a) the caller gets the timeout outcome, (b) the worker is still alive afterward, and (c) performing a receive later yields the late reply. Then repeat with a fresh subject per call and observe that the stale reply is never misattributed. If your worker ignores shutdown messages under load, that's the one case where process.kill becomes the pragmatic fallback.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.