erlang: send + selective receive via shift/reset (+13 tests)
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Has been cancelled
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Has been cancelled
This commit is contained in:
@@ -88,6 +88,26 @@
|
||||
(range h (len items)))
|
||||
out)))
|
||||
|
||||
;; Read the i'th entry (relative to head) without popping.
|
||||
(define
|
||||
er-q-nth
|
||||
(fn (q i) (nth (get q :items) (+ (get q :head-idx) i))))
|
||||
|
||||
;; Remove entry at logical index i, shift tail in.
|
||||
(define
|
||||
er-q-delete-at!
|
||||
(fn
|
||||
(q i)
|
||||
(let
|
||||
((h (get q :head-idx)) (items (get q :items)) (new (list)))
|
||||
(for-each
|
||||
(fn
|
||||
(j)
|
||||
(when (not (= j (+ h i))) (append! new (nth items j))))
|
||||
(range h (len items)))
|
||||
(dict-set! q :items new)
|
||||
(dict-set! q :head-idx 0))))
|
||||
|
||||
;; ── pids ─────────────────────────────────────────────────────────
|
||||
(define er-mk-pid (fn (id) {:id id :tag "pid"}))
|
||||
(define er-pid? (fn (v) (er-is-tagged? v "pid")))
|
||||
@@ -251,25 +271,57 @@
|
||||
(get proc :pid)))))
|
||||
|
||||
;; ── scheduler loop ──────────────────────────────────────────────
|
||||
;; Drain all runnable processes to completion. Synchronous — each
|
||||
;; spawned process runs its :initial-fun front-to-back with no yielding.
|
||||
;; receive-driven suspension arrives in the next roadmap step.
|
||||
;; Each process's entry runs inside a `reset`; `receive` uses `shift`
|
||||
;; to suspend (saving a continuation on the proc record). When a `!`
|
||||
;; delivers a message to a waiting process we re-enqueue it — the
|
||||
;; scheduler step invokes the saved continuation, which retries the
|
||||
;; receive against the updated mailbox.
|
||||
(define er-suspend-marker {:tag "er-suspend-marker"})
|
||||
|
||||
(define
|
||||
er-sched-drain!
|
||||
er-suspended?
|
||||
(fn
|
||||
(v)
|
||||
(and
|
||||
(= (type-of v) "dict")
|
||||
(= (get v :tag) "er-suspend-marker"))))
|
||||
|
||||
(define
|
||||
er-sched-run-all!
|
||||
(fn
|
||||
()
|
||||
(let
|
||||
((pid (er-sched-next-runnable!)))
|
||||
(when
|
||||
(not (= pid nil))
|
||||
(er-sched-set-current! pid)
|
||||
(er-proc-set! pid :state "running")
|
||||
(let
|
||||
((fv (er-proc-field pid :initial-fun)))
|
||||
(when
|
||||
(not (= fv nil))
|
||||
(er-apply-fun fv (list))))
|
||||
(er-proc-set! pid :state "dead")
|
||||
(er-proc-set! pid :exit-reason (er-mk-atom "normal"))
|
||||
(er-sched-set-current! nil)
|
||||
(er-sched-drain!)))))
|
||||
(er-sched-step! pid)
|
||||
(er-sched-run-all!)))))
|
||||
|
||||
(define
|
||||
er-sched-step!
|
||||
(fn
|
||||
(pid)
|
||||
(er-sched-set-current! pid)
|
||||
(er-proc-set! pid :state "running")
|
||||
(let
|
||||
((prev-k (er-proc-field pid :continuation))
|
||||
(result-ref (list nil)))
|
||||
(if
|
||||
(= prev-k nil)
|
||||
(set-nth!
|
||||
result-ref
|
||||
0
|
||||
(reset (er-apply-fun (er-proc-field pid :initial-fun) (list))))
|
||||
(do
|
||||
(er-proc-set! pid :continuation nil)
|
||||
(set-nth! result-ref 0 (prev-k nil))))
|
||||
(let
|
||||
((r (nth result-ref 0)))
|
||||
(cond
|
||||
(er-suspended? r) nil
|
||||
:else (do
|
||||
(er-proc-set! pid :state "dead")
|
||||
(er-proc-set! pid :exit-reason (er-mk-atom "normal"))
|
||||
(er-proc-set! pid :exit-result r)
|
||||
(er-proc-set! pid :continuation nil)))))
|
||||
(er-sched-set-current! nil)))
|
||||
|
||||
Reference in New Issue
Block a user