erlang: supervisor one-for-one (+7 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:
@@ -869,3 +869,58 @@
|
||||
(define
|
||||
er-load-gen-server!
|
||||
(fn () (erlang-load-module er-gen-server-source)))
|
||||
|
||||
;; ── supervisor (OTP-lite, one-for-one) ──────────────────────────
|
||||
;; Each child spec is `{Id, StartFn}` — `StartFn/0` returns the
|
||||
;; child's pid. The supervisor `process_flag(trap_exit, true)`,
|
||||
;; links to every child, and on `{'EXIT', DeadPid, _}` calls the
|
||||
;; matching `StartFn` to bring up a fresh replacement. Strategy is
|
||||
;; one-for-one: only the dead child restarts; siblings keep running.
|
||||
(define
|
||||
er-supervisor-source
|
||||
"-module(supervisor).
|
||||
start_link(Mod, Args) ->
|
||||
spawn(fun () ->
|
||||
process_flag(trap_exit, true),
|
||||
case Mod:init(Args) of
|
||||
{ok, ChildSpecs} ->
|
||||
Children = lists:map(
|
||||
fun (Spec) -> supervisor:start_child(Spec) end,
|
||||
ChildSpecs),
|
||||
supervisor:loop(Children)
|
||||
end
|
||||
end).
|
||||
start_child({Id, StartFn}) ->
|
||||
P = StartFn(),
|
||||
link(P),
|
||||
{Id, StartFn, P}.
|
||||
which_children(Sup) ->
|
||||
Sup ! {'$sup_which', self()},
|
||||
receive {'$sup_children', Cs} -> Cs end.
|
||||
stop(Sup) ->
|
||||
Sup ! '$sup_stop',
|
||||
ok.
|
||||
loop(Children) ->
|
||||
receive
|
||||
{'EXIT', Dead, _Reason} ->
|
||||
supervisor:loop(supervisor:restart(Children, Dead));
|
||||
{'$sup_which', From} ->
|
||||
From ! {'$sup_children', Children},
|
||||
supervisor:loop(Children);
|
||||
'$sup_stop' ->
|
||||
ok
|
||||
end.
|
||||
restart([], _) -> [];
|
||||
restart([{Id, SF, P} | T], Dead) ->
|
||||
case P =:= Dead of
|
||||
true ->
|
||||
NewP = SF(),
|
||||
link(NewP),
|
||||
[{Id, SF, NewP} | T];
|
||||
false ->
|
||||
[{Id, SF, P} | supervisor:restart(T, Dead)]
|
||||
end.")
|
||||
|
||||
(define
|
||||
er-load-supervisor!
|
||||
(fn () (erlang-load-module er-supervisor-source)))
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
{
|
||||
"language": "erlang",
|
||||
"total_pass": 425,
|
||||
"total": 425,
|
||||
"total_pass": 432,
|
||||
"total": 432,
|
||||
"suites": [
|
||||
{"name":"tokenize","pass":62,"total":62,"status":"ok"},
|
||||
{"name":"parse","pass":52,"total":52,"status":"ok"},
|
||||
{"name":"eval","pass":241,"total":241,"status":"ok"},
|
||||
{"name":"eval","pass":248,"total":248,"status":"ok"},
|
||||
{"name":"runtime","pass":39,"total":39,"status":"ok"},
|
||||
{"name":"ring","pass":4,"total":4,"status":"ok"},
|
||||
{"name":"ping-pong","pass":4,"total":4,"status":"ok"},
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
# Erlang-on-SX Scoreboard
|
||||
|
||||
**Total: 425 / 425 tests passing**
|
||||
**Total: 432 / 432 tests passing**
|
||||
|
||||
| | Suite | Pass | Total |
|
||||
|---|---|---|---|
|
||||
| ✅ | tokenize | 62 | 62 |
|
||||
| ✅ | parse | 52 | 52 |
|
||||
| ✅ | eval | 241 | 241 |
|
||||
| ✅ | eval | 248 | 248 |
|
||||
| ✅ | runtime | 39 | 39 |
|
||||
| ✅ | ring | 4 | 4 |
|
||||
| ✅ | ping-pong | 4 | 4 |
|
||||
|
||||
@@ -768,6 +768,81 @@
|
||||
(nm (ev "P = gen_server:start_link(stk, ignored), gen_server:call(P, pop)"))
|
||||
"empty")
|
||||
|
||||
;; ── supervisor (one-for-one) ────────────────────────────────────
|
||||
(do
|
||||
(er-load-supervisor!)
|
||||
(erlang-load-module
|
||||
"-module(echoer).
|
||||
start() -> spawn(fun () -> echoer:loop() end).
|
||||
loop() ->
|
||||
receive
|
||||
{ping, From} -> From ! pong, echoer:loop();
|
||||
die -> exit(killed)
|
||||
end.")
|
||||
nil)
|
||||
|
||||
(er-eval-test "sup starts children"
|
||||
(do
|
||||
(erlang-load-module
|
||||
"-module(sup1). init(_) -> {ok, [{w1, fun () -> echoer:start() end}]}.")
|
||||
(ev "Sup = supervisor:start_link(sup1, []), receive after 5 -> ok end, length(supervisor:which_children(Sup))"))
|
||||
1)
|
||||
|
||||
(er-eval-test "sup multiple children"
|
||||
(do
|
||||
(erlang-load-module
|
||||
"-module(sup2).
|
||||
init(_) -> {ok, [
|
||||
{w1, fun () -> echoer:start() end},
|
||||
{w2, fun () -> echoer:start() end},
|
||||
{w3, fun () -> echoer:start() end}
|
||||
]}.")
|
||||
(ev "Sup = supervisor:start_link(sup2, []), receive after 5 -> ok end, length(supervisor:which_children(Sup))"))
|
||||
3)
|
||||
|
||||
(er-eval-test "sup child responds"
|
||||
(do
|
||||
(erlang-load-module
|
||||
"-module(sup3). init(_) -> {ok, [{w1, fun () -> echoer:start() end}]}.")
|
||||
(nm (ev "Sup = supervisor:start_link(sup3, []), receive after 5 -> ok end, [{_, _, P1} | _] = supervisor:which_children(Sup), P1 ! {ping, self()}, receive pong -> ok end")))
|
||||
"ok")
|
||||
|
||||
(er-eval-test "sup restarts on exit"
|
||||
(do
|
||||
(erlang-load-module
|
||||
"-module(sup4). init(_) -> {ok, [{w1, fun () -> echoer:start() end}]}.")
|
||||
(nm
|
||||
(ev "Sup = supervisor:start_link(sup4, []), receive after 5 -> ok end, [{_, _, P1} | _] = supervisor:which_children(Sup), P1 ! die, receive after 5 -> ok end, [{_, _, P2} | _] = supervisor:which_children(Sup), P1 =/= P2")))
|
||||
"true")
|
||||
|
||||
(er-eval-test "sup restarted child works"
|
||||
(do
|
||||
(erlang-load-module
|
||||
"-module(sup5). init(_) -> {ok, [{w1, fun () -> echoer:start() end}]}.")
|
||||
(nm
|
||||
(ev "Sup = supervisor:start_link(sup5, []), receive after 5 -> ok end, [{_, _, P1} | _] = supervisor:which_children(Sup), P1 ! die, receive after 5 -> ok end, [{_, _, P2} | _] = supervisor:which_children(Sup), P2 ! {ping, self()}, receive pong -> ok end")))
|
||||
"ok")
|
||||
|
||||
(er-eval-test "sup one-for-one isolates failures"
|
||||
(do
|
||||
(erlang-load-module
|
||||
"-module(sup6).
|
||||
init(_) -> {ok, [
|
||||
{w1, fun () -> echoer:start() end},
|
||||
{w2, fun () -> echoer:start() end}
|
||||
]}.")
|
||||
(nm
|
||||
(ev "Sup = supervisor:start_link(sup6, []), receive after 5 -> ok end, [{_, _, P1}, {_, _, P2}] = supervisor:which_children(Sup), P1 ! die, receive after 5 -> ok end, [{_, _, _NewP1}, {_, _, P2Again}] = supervisor:which_children(Sup), P2 =:= P2Again")))
|
||||
"true")
|
||||
|
||||
(er-eval-test "sup stop"
|
||||
(nm
|
||||
(do
|
||||
(erlang-load-module
|
||||
"-module(sup7). init(_) -> {ok, [{w1, fun () -> echoer:start() end}]}.")
|
||||
(ev "Sup = supervisor:start_link(sup7, []), receive after 5 -> ok end, supervisor:stop(Sup)")))
|
||||
"ok")
|
||||
|
||||
(define
|
||||
er-eval-test-summary
|
||||
(str "eval " er-eval-test-pass "/" er-eval-test-count))
|
||||
|
||||
Reference in New Issue
Block a user