Between OTP 21 and OTP 22, enif_consume_timeslice/2 gained an early return on dirty schedulers:
if (sched < 0)
return 0; /* no-op on dirty scheduler */
Before, it worked identically on normal and dirty. The documented well-behaved-NIF pattern (call enif_consume_timeslice, on non-zero call enif_schedule_nif) is now silently broken for dirty NIFs, the return branch never fires. Also, reductions are no longer credited for CPU time spent on dirty schedulers.
Concrete case: an on-device LLM inference NIF. When the number of concurrent sessions exceeds the dirty CPU scheduler count, queued sessions must wait for a full forward pass to complete before making any progress, since the pre-emption granularity is one whole NIF call. Pre-OTP-22, the C side could yield mid-forward-pass via enif_consume_timeslice + enif_schedule_nif, so all sessions would advance concurrently at roughly (scheduler_count / session_count) of full speed.
Was pre-OTP-22 behavior considered incorrect? And is elapsed-based enif_schedule_nif with DIRTY_JOB_* flags (ignoring enif_consume_timeslice’s return) the intended replacement now?
Zab.