An expression that makes a function value keeps its module mapped, a process a merged program starts does not take the session's socket, and an evaluation's value is its own expression's and not one a restart resumed
This commit is contained in:
parent
020392b064
commit
96dbcb1f2f
30
lib/dev.ml
30
lib/dev.ml
@ -283,6 +283,19 @@ let stop_reply t =
|
|||||||
|
|
||||||
let stop_gen t : int option = Option.map fst (stop_reply t)
|
let stop_gen t : int option = Option.map fst (stop_reply t)
|
||||||
|
|
||||||
|
(* How many evaluated expressions the agent has queued, and the highest one
|
||||||
|
that has returned a value; [None] from an agent without the verb. *)
|
||||||
|
let calls t : (int * int) option =
|
||||||
|
match request t "calls" with
|
||||||
|
| exception Unix.Unix_error _ -> None
|
||||||
|
| text ->
|
||||||
|
(match String.split_on_char ' ' (String.trim text) with
|
||||||
|
| [ q; v ] ->
|
||||||
|
(match int_of_string_opt q, int_of_string_opt v with
|
||||||
|
| Some q, Some v -> Some (q, v)
|
||||||
|
| _ -> None)
|
||||||
|
| _ -> None)
|
||||||
|
|
||||||
(* The same stop with whose code it stopped in: [Some true] when the thread
|
(* The same stop with whose code it stopped in: [Some true] when the thread
|
||||||
was running an evaluated thunk, [Some false] when it was in the program's
|
was running an evaluated thunk, [Some false] when it was in the program's
|
||||||
own code, [None] from an agent that does not say. *)
|
own code, [None] from an agent that does not say. *)
|
||||||
@ -1253,6 +1266,10 @@ let eval_expr t ~code ~origin ~pause =
|
|||||||
match Session.eval_expr ~origin ~pause t.session code with
|
match Session.eval_expr ~origin ~pause t.session code with
|
||||||
| c ->
|
| c ->
|
||||||
let before = match result t with Some (g, _) -> g | None -> 0L in
|
let before = match result t with Some (g, _) -> g | None -> 0L in
|
||||||
|
(* This expression's number among those the agent has queued: an
|
||||||
|
earlier one resumed by a restart can publish after this one is sent,
|
||||||
|
and the result counter alone would take its value for this one's. *)
|
||||||
|
let mine = Option.map (fun (q, _) -> q + 1) (calls t) in
|
||||||
(* Read here, beside [before], and for the same kind of reason: all
|
(* Read here, beside [before], and for the same kind of reason: all
|
||||||
three are the "how things stood" half of a difference the wait below
|
three are the "how things stood" half of a difference the wait below
|
||||||
measures. A program already sitting in a break when the request
|
measures. A program already sitting in a break when the request
|
||||||
@ -1406,9 +1423,16 @@ let eval_expr t ~code ~origin ~pause =
|
|||||||
the sleep has to stay a sleep. *)
|
the sleep has to stay a sleep. *)
|
||||||
drain t;
|
drain t;
|
||||||
let value () =
|
let value () =
|
||||||
match result t with
|
let returned =
|
||||||
| Some (g, v) when Int64.compare g before > 0 -> Some v
|
match mine, calls t with
|
||||||
| _ -> None
|
| Some m, Some (_, v) -> v >= m
|
||||||
|
| _ -> true
|
||||||
|
in
|
||||||
|
if not returned then None
|
||||||
|
else
|
||||||
|
match result t with
|
||||||
|
| Some (g, v) when Int64.compare g before > 0 -> Some v
|
||||||
|
| _ -> None
|
||||||
in
|
in
|
||||||
match value () with
|
match value () with
|
||||||
| Some v -> `Value v
|
| Some v -> `Value v
|
||||||
|
|||||||
40
lib/emit.ml
40
lib/emit.ml
@ -5770,6 +5770,43 @@ let program ?(checks = true) ?(dev = false) ?(debug = false) ?(pnames = [])
|
|||||||
|
|
||||||
String literals still have to come along: they are this module's own
|
String literals still have to come along: they are this module's own
|
||||||
constants, and omitting them is an undefined [@.str.N] at link time. *)
|
constants, and omitting them is an undefined [@.str.N] at link time. *)
|
||||||
|
|
||||||
|
(* Whether an expression thunk makes a function value anywhere in its body or
|
||||||
|
in the clauses lifted out of it. Such a value's code address is in this
|
||||||
|
module — a lambda's body, or the thick wrapper a named function is handed
|
||||||
|
out through — and it may be stored anywhere, so the module must stay
|
||||||
|
mapped. Both backends ask this before marking a thunk's module
|
||||||
|
unloadable. *)
|
||||||
|
let thunk_makes_fn_values (p : Tast.program) name =
|
||||||
|
let mine = Hashtbl.create 8 in
|
||||||
|
Hashtbl.replace mine name ();
|
||||||
|
(* Lifted clauses nest: a lambda inside a lambda is lifted out of the
|
||||||
|
outer one's body, so the set grows until nothing new joins it. *)
|
||||||
|
let rec close () =
|
||||||
|
let grew = ref false in
|
||||||
|
List.iter
|
||||||
|
(fun (f : Tast.fn) ->
|
||||||
|
match f.Tast.fparent with
|
||||||
|
| Some q when Hashtbl.mem mine q && not (Hashtbl.mem mine f.Tast.name) ->
|
||||||
|
Hashtbl.replace mine f.Tast.name (); grew := true
|
||||||
|
| _ -> ())
|
||||||
|
p.Tast.fns;
|
||||||
|
if !grew then close ()
|
||||||
|
in
|
||||||
|
close ();
|
||||||
|
let found = ref false in
|
||||||
|
List.iter
|
||||||
|
(fun (f : Tast.fn) ->
|
||||||
|
if Hashtbl.mem mine f.Tast.name then
|
||||||
|
List.iter
|
||||||
|
(Tast.walk (fun (e : Tast.expr) ->
|
||||||
|
match e.Tast.e with
|
||||||
|
| Tast.FnAddr _ | Tast.Closure _ | Tast.Thicken _ -> found := true
|
||||||
|
| _ -> ()))
|
||||||
|
f.Tast.body)
|
||||||
|
p.Tast.fns;
|
||||||
|
!found
|
||||||
|
|
||||||
let redefinition ?(checks = true) ?(dev = false) ?(debug = false)
|
let redefinition ?(checks = true) ?(dev = false) ?(debug = false)
|
||||||
?(known = fun _ -> true) ?(retains = true)
|
?(known = fun _ -> true) ?(retains = true)
|
||||||
?call ?(consts = []) ?(annotate = false) (p : Tast.program) ~fns
|
?call ?(consts = []) ?(annotate = false) (p : Tast.program) ~fns
|
||||||
@ -6045,7 +6082,8 @@ let redefinition ?(checks = true) ?(dev = false) ?(debug = false)
|
|||||||
into the result buffer, so nothing outside the module holds an address
|
into the result buffer, so nothing outside the module holds an address
|
||||||
inside it once the call has returned. Without this, clicking through
|
inside it once the call has returned. Without this, clicking through
|
||||||
the frames of a break loop costs a permanent mapping per click. *)
|
the frames of a break loop costs a permanent mapping per click. *)
|
||||||
if fns = [ fn ] && consts = [] && ((not retains) || m.nstr = 0) then
|
if fns = [ fn ] && consts = [] && ((not retains) || m.nstr = 0)
|
||||||
|
&& not (thunk_makes_fn_values p fn) then
|
||||||
Buffer.add_string m.out "\n@flan_reload_transient = global i8 1\n"
|
Buffer.add_string m.out "\n@flan_reload_transient = global i8 1\n"
|
||||||
| None -> ()
|
| None -> ()
|
||||||
end;
|
end;
|
||||||
|
|||||||
@ -5621,7 +5621,7 @@ let redefinition ~checks ?(dev = true) ?(known = fun _ -> true)
|
|||||||
function's registry names are not counted: the registry copies them. *)
|
function's registry names are not counted: the registry copies them. *)
|
||||||
(match call with
|
(match call with
|
||||||
| Some fn
|
| Some fn
|
||||||
when fns = [ fn ] && consts = []
|
when fns = [ fn ] && consts = [] && not (Emit.thunk_makes_fn_values p fn)
|
||||||
&& ((not retains) || md.Emit.nstr = 0) ->
|
&& ((not retains) || md.Emit.nstr = 0) ->
|
||||||
Buffer.add_string out
|
Buffer.add_string out
|
||||||
"\n\t.data\n\t.globl\tflan_reload_transient\n\
|
"\n\t.data\n\t.globl\tflan_reload_transient\n\
|
||||||
|
|||||||
@ -65,8 +65,9 @@ let send path line =
|
|||||||
binary is the parent of every program it starts, which is the
|
binary is the parent of every program it starts, which is the
|
||||||
--two-process shape. *)
|
--two-process shape. *)
|
||||||
let daemon_env path =
|
let daemon_env path =
|
||||||
[| "FLAN_AGENT_SOCKET=" ^ path;
|
let me = string_of_int (Unix.getpid ()) in
|
||||||
"FLAN_AGENT_OWNER=" ^ string_of_int (Unix.getpid ()) |]
|
[| "FLAN_AGENT_SOCKET=" ^ path; "FLAN_AGENT_OWNER=" ^ me;
|
||||||
|
"FLAN_DEV_PARENT=" ^ me |]
|
||||||
|
|
||||||
let () =
|
let () =
|
||||||
match Sys.command "command -v clang > /dev/null 2>&1 && command -v llc > /dev/null 2>&1" with
|
match Sys.command "command -v clang > /dev/null 2>&1 && command -v llc > /dev/null 2>&1" with
|
||||||
@ -342,59 +343,71 @@ let () =
|
|||||||
is not this program's, and it picks and announces a path of its own as
|
is not this program's, and it picks and announces a path of its own as
|
||||||
if nothing were set. The file standing in for the session's socket has
|
if nothing were set. The file standing in for the session's socket has
|
||||||
to still be the same file afterwards. *)
|
to still be the same file afterwards. *)
|
||||||
let stolen = tmp "stolen.sock" and serr = tmp "stolen.err" in
|
let inherited ~shape ~owner =
|
||||||
Out_channel.with_open_bin stolen (fun oc ->
|
let stolen = tmp "stolen.sock" and serr = tmp "stolen.err" in
|
||||||
output_string oc "the session's");
|
Out_channel.with_open_bin stolen (fun oc ->
|
||||||
let senv =
|
output_string oc "the session's");
|
||||||
Array.append aenv
|
let senv =
|
||||||
[| "FLAN_AGENT_SOCKET=" ^ stolen; "FLAN_AGENT_OWNER=1" |]
|
Array.append aenv
|
||||||
|
[| "FLAN_AGENT_SOCKET=" ^ stolen; "FLAN_AGENT_OWNER=" ^ owner |]
|
||||||
|
in
|
||||||
|
let s1 = ofd (tmp "stolen.out") and s2 = ofd serr in
|
||||||
|
let spid = Unix.create_process_env aexe [| aexe |] senv Unix.stdin s1 s2 in
|
||||||
|
Unix.close s1;
|
||||||
|
Unix.close s2;
|
||||||
|
let prefix = "flan agent: listening on " in
|
||||||
|
let sannounced () =
|
||||||
|
let text = In_channel.with_open_bin serr In_channel.input_all in
|
||||||
|
List.find_map
|
||||||
|
(fun l ->
|
||||||
|
if String.length l > String.length prefix
|
||||||
|
&& String.sub l 0 (String.length prefix) = prefix
|
||||||
|
then Some (String.sub l (String.length prefix)
|
||||||
|
(String.length l - String.length prefix))
|
||||||
|
else None)
|
||||||
|
(String.split_on_char '\n' text)
|
||||||
|
in
|
||||||
|
(match
|
||||||
|
if await (fun () -> sannounced () <> None) then sannounced () else None
|
||||||
|
with
|
||||||
|
| None ->
|
||||||
|
fail "%s: a program with someone else's FLAN_AGENT_SOCKET announced no \
|
||||||
|
socket of its own" shape;
|
||||||
|
(try Unix.kill spid Sys.sigkill with Unix.Unix_error _ -> ())
|
||||||
|
| Some p ->
|
||||||
|
if p = stolen then fail "%s: the inherited path was bound: %S" shape p;
|
||||||
|
if not (await (fun () -> Sys.file_exists p)) then
|
||||||
|
fail "%s: nothing was bound at the announced %S" shape p
|
||||||
|
else ignore (send p aso);
|
||||||
|
let reaped =
|
||||||
|
await ~ms:5000 (fun () ->
|
||||||
|
match Unix.waitpid [ Unix.WNOHANG ] spid with
|
||||||
|
| 0, _ -> false
|
||||||
|
| _ -> true)
|
||||||
|
in
|
||||||
|
if not reaped then begin
|
||||||
|
(try Unix.kill spid Sys.sigkill with Unix.Unix_error _ -> ());
|
||||||
|
fail "%s: the program with an inherited variable never finished" shape
|
||||||
|
end);
|
||||||
|
(match In_channel.with_open_bin stolen In_channel.input_all with
|
||||||
|
| "the session's" -> ()
|
||||||
|
| _ -> fail "%s: the inherited FLAN_AGENT_SOCKET's file was replaced" shape
|
||||||
|
| exception Sys_error _ ->
|
||||||
|
fail "%s: the inherited FLAN_AGENT_SOCKET's file was removed" shape);
|
||||||
|
List.iter (fun f -> try Sys.remove f with Sys_error _ -> ())
|
||||||
|
[ stolen; serr; tmp "stolen.out" ]
|
||||||
in
|
in
|
||||||
let s1 = ofd (tmp "stolen.out") and s2 = ofd serr in
|
(* Nobody's pid. *)
|
||||||
let spid = Unix.create_process_env aexe [| aexe |] senv Unix.stdin s1 s2 in
|
inherited ~shape:"an owner that is not this process" ~owner:"1";
|
||||||
Unix.close s1;
|
(* A merged build's owner is the program itself, so a process the program
|
||||||
Unix.close s2;
|
starts has the owner as its parent; with no FLAN_DEV_PARENT naming it,
|
||||||
let prefix = "flan agent: listening on " in
|
that is not the --two-process shape and the socket is not its. Here the
|
||||||
let sannounced () =
|
test binary stands in for the program. *)
|
||||||
let text = In_channel.with_open_bin serr In_channel.input_all in
|
inherited ~shape:"a child of a merged program"
|
||||||
List.find_map
|
~owner:(string_of_int (Unix.getpid ()));
|
||||||
(fun l ->
|
|
||||||
if String.length l > String.length prefix
|
|
||||||
&& String.sub l 0 (String.length prefix) = prefix
|
|
||||||
then Some (String.sub l (String.length prefix)
|
|
||||||
(String.length l - String.length prefix))
|
|
||||||
else None)
|
|
||||||
(String.split_on_char '\n' text)
|
|
||||||
in
|
|
||||||
(match
|
|
||||||
if await (fun () -> sannounced () <> None) then sannounced () else None
|
|
||||||
with
|
|
||||||
| None ->
|
|
||||||
fail "a program with someone else's FLAN_AGENT_SOCKET announced no \
|
|
||||||
socket of its own";
|
|
||||||
(try Unix.kill spid Sys.sigkill with Unix.Unix_error _ -> ())
|
|
||||||
| Some p ->
|
|
||||||
if p = stolen then fail "the inherited path was bound: %S" p;
|
|
||||||
if not (await (fun () -> Sys.file_exists p)) then
|
|
||||||
fail "nothing was bound at the announced %S" p
|
|
||||||
else ignore (send p aso);
|
|
||||||
let reaped =
|
|
||||||
await ~ms:5000 (fun () ->
|
|
||||||
match Unix.waitpid [ Unix.WNOHANG ] spid with
|
|
||||||
| 0, _ -> false
|
|
||||||
| _ -> true)
|
|
||||||
in
|
|
||||||
if not reaped then begin
|
|
||||||
(try Unix.kill spid Sys.sigkill with Unix.Unix_error _ -> ());
|
|
||||||
fail "the program with an inherited variable never finished"
|
|
||||||
end);
|
|
||||||
(match In_channel.with_open_bin stolen In_channel.input_all with
|
|
||||||
| "the session's" -> ()
|
|
||||||
| _ -> fail "the inherited FLAN_AGENT_SOCKET's file was replaced"
|
|
||||||
| exception Sys_error _ ->
|
|
||||||
fail "the inherited FLAN_AGENT_SOCKET's file was removed");
|
|
||||||
|
|
||||||
List.iter (fun f -> try Sys.remove f with Sys_error _ -> ())
|
List.iter (fun f -> try Sys.remove f with Sys_error _ -> ())
|
||||||
[ aexe; aso; aout; aerr; bad; stolen; serr; tmp "stolen.out" ];
|
[ aexe; aso; aout; aerr; bad ];
|
||||||
|
|
||||||
(* ── No (agent/start) at all ────────────────────────────────────── *)
|
(* ── No (agent/start) at all ────────────────────────────────────── *)
|
||||||
|
|
||||||
|
|||||||
@ -6784,7 +6784,38 @@ let () =
|
|||||||
(%s)" backend
|
(%s)" backend
|
||||||
(Option.value ~default:"" (Wire.string_field r "value"))
|
(Option.value ~default:"" (Wire.string_field r "value"))
|
||||||
(said r)
|
(said r)
|
||||||
end
|
end;
|
||||||
|
(* A function value an expression makes has its code in that
|
||||||
|
expression's module — a lambda's body, or the wrapper a named
|
||||||
|
function is handed out through — so that module stays mapped.
|
||||||
|
Later expressions are mapped between the store and the call,
|
||||||
|
where an unloaded one would have been. *)
|
||||||
|
let defd code =
|
||||||
|
let r =
|
||||||
|
request c
|
||||||
|
(Printf.sprintf
|
||||||
|
"(:op \"eval\" :code %s \
|
||||||
|
:file \"programs/dev-dyn-global.flan\")" (Wire.quote code))
|
||||||
|
in
|
||||||
|
if status r <> "ok" then fail "--%s: %s: %s" backend code (said r)
|
||||||
|
in
|
||||||
|
defd "(defonce kept (Option (Fn [i64] i64)))";
|
||||||
|
defd "(defn twice [x i64] i64 (* x 2))";
|
||||||
|
let call_kept want what =
|
||||||
|
for i = 1 to 3 do
|
||||||
|
ignore (ev (Printf.sprintf "(do (println \"pad %d\") %d)" i i))
|
||||||
|
done;
|
||||||
|
let r = ev "(match kept (Some f) (f 1) (None) -1)" in
|
||||||
|
if Wire.string_field r "value" <> Some want then
|
||||||
|
fail "--%s: %s kept by an unloaded expression answered %S \
|
||||||
|
(%s)" backend what
|
||||||
|
(Option.value ~default:"" (Wire.string_field r "value"))
|
||||||
|
(said r)
|
||||||
|
in
|
||||||
|
ignore (ev "(do (set kept (Some (fn [x] (+ x 7)))) 0)");
|
||||||
|
call_kept "8" "a lambda";
|
||||||
|
ignore (ev "(do (set kept (Some twice)) 0)");
|
||||||
|
call_kept "2" "a named function"
|
||||||
end;
|
end;
|
||||||
ignore (request c "(:op \"close\")");
|
ignore (request c "(:op \"close\")");
|
||||||
(try Unix.close c with Unix.Unix_error _ -> ());
|
(try Unix.close c with Unix.Unix_error _ -> ());
|
||||||
@ -8958,6 +8989,23 @@ let () =
|
|||||||
"(:op \"eval-expr\" :code %s :file \"programs/dev-own-break.flan\")"
|
"(:op \"eval-expr\" :code %s :file \"programs/dev-own-break.flan\")"
|
||||||
(Wire.quote code))
|
(Wire.quote code))
|
||||||
in
|
in
|
||||||
|
(* An expression stopped in a break and resumed by a restart finishes
|
||||||
|
after the restart's reply, and here it finishes after the next
|
||||||
|
expression has been sent: its value must not answer for that one. *)
|
||||||
|
let r =
|
||||||
|
ev "(restart-case (do (error (Late {})) 0) \
|
||||||
|
(slow [] (do (usleep 500000) 5)))"
|
||||||
|
in
|
||||||
|
if status r <> "error" then
|
||||||
|
fail "resumed value: the first expression did not stop: %s" (said r)
|
||||||
|
else begin
|
||||||
|
let r = request c "(:op \"restart\" :name \"slow\")" in
|
||||||
|
if status r <> "ok" then fail "resumed value: restart: %s" (said r);
|
||||||
|
let r = ev "(do (usleep 300000) 23)" in
|
||||||
|
if Wire.string_field r "value" <> Some "23" then
|
||||||
|
fail "resumed value: the next expression answered %S (%s)"
|
||||||
|
(Option.value ~default:"" (Wire.string_field r "value")) (said r)
|
||||||
|
end;
|
||||||
let r = ev "(do (set go 1) 0)" in
|
let r = ev "(do (set go 1) 0)" in
|
||||||
if status r <> "ok" then fail "own break: setting go: %s" (said r)
|
if status r <> "ok" then fail "own break: setting go: %s" (said r)
|
||||||
else begin
|
else begin
|
||||||
|
|||||||
38
vendor/agent/flan_agent.c
vendored
38
vendor/agent/flan_agent.c
vendored
@ -214,8 +214,19 @@ typedef struct {
|
|||||||
void *handle;
|
void *handle;
|
||||||
int stopped_only;
|
int stopped_only;
|
||||||
int32_t at_stop;
|
int32_t at_stop;
|
||||||
|
uint32_t call_id; /* its place among jobs with a call; 0 if none */
|
||||||
} job;
|
} job;
|
||||||
|
|
||||||
|
/* Which evaluated expression the result buffer holds. Every job with a call
|
||||||
|
* is numbered as the listener queues it, and a call that returns records its
|
||||||
|
* number, so a daemon waiting for its own expression's value is not answered
|
||||||
|
* by an earlier expression that a restart resumed and that published after
|
||||||
|
* the new one was sent. The highest wins: an expression run inside another's
|
||||||
|
* break returns first, and the outer one only resumes on a later request.
|
||||||
|
* The [calls] verb answers both counts. */
|
||||||
|
static _Atomic uint32_t calls_queued;
|
||||||
|
static _Atomic uint32_t calls_valued;
|
||||||
|
|
||||||
/* Said once, in one place, and shipped to the daemon over [refusals] rather
|
/* Said once, in one place, and shipped to the daemon over [refusals] rather
|
||||||
* than written down again at the other end. A refusal is a sentence naming
|
* than written down again at the other end. A refusal is a sentence naming
|
||||||
* what actually happened, and the thing that actually happened is not "the
|
* what actually happened, and the thing that actually happened is not "the
|
||||||
@ -1335,6 +1346,8 @@ int32_t flan_agent_poll(void) {
|
|||||||
if (sigsetjmp(escape, 1) == 0) {
|
if (sigsetjmp(escape, 1) == 0) {
|
||||||
eval_escape = &escape;
|
eval_escape = &escape;
|
||||||
j.call();
|
j.call();
|
||||||
|
if (j.call_id > atomic_load(&calls_valued))
|
||||||
|
atomic_store(&calls_valued, j.call_id);
|
||||||
} else {
|
} else {
|
||||||
if (flan_condition_stacks_restore) flan_condition_stacks_restore(mh, mr, md);
|
if (flan_condition_stacks_restore) flan_condition_stacks_restore(mh, mr, md);
|
||||||
if (flan_dev_frames_restore) flan_dev_frames_restore(mf);
|
if (flan_dev_frames_restore) flan_dev_frames_restore(mf);
|
||||||
@ -1920,6 +1933,14 @@ static void handle_line(char *line, sink *o) {
|
|||||||
* editor polls this without knowing the state already. */
|
* editor polls this without knowing the state already. */
|
||||||
/* After the number, whose code stopped: "eval" when the thread was inside
|
/* After the number, whose code stopped: "eval" when the thread was inside
|
||||||
* an evaluated thunk, "program" when it was in the program's own code. */
|
* an evaluated thunk, "program" when it was in the program's own code. */
|
||||||
|
if (strcmp(line, "calls") == 0) {
|
||||||
|
char hdr[48];
|
||||||
|
int k = snprintf(hdr, sizeof hdr, "%u %u\n",
|
||||||
|
(unsigned)atomic_load(&calls_queued),
|
||||||
|
(unsigned)atomic_load(&calls_valued));
|
||||||
|
if (k > 0) emit(o, hdr, (size_t)k);
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (strcmp(line, "stop") == 0) {
|
if (strcmp(line, "stop") == 0) {
|
||||||
snapshot *s = (atomic_load(&depth) > 0) ? snap_top() : NULL;
|
snapshot *s = (atomic_load(&depth) > 0) ? snap_top() : NULL;
|
||||||
char hdr[32];
|
char hdr[32];
|
||||||
@ -2199,7 +2220,9 @@ static void handle_line(char *line, sink *o) {
|
|||||||
* is the failure being fixed. */
|
* is the failure being fixed. */
|
||||||
if (!publish((job){ .install = f, .call = c,
|
if (!publish((job){ .install = f, .call = c,
|
||||||
.handle = transient == NULL ? NULL : h,
|
.handle = transient == NULL ? NULL : h,
|
||||||
.stopped_only = stopped_only, .at_stop = at_stop }))
|
.stopped_only = stopped_only, .at_stop = at_stop,
|
||||||
|
.call_id = c == NULL ? 0
|
||||||
|
: atomic_fetch_add(&calls_queued, 1) + 1 }))
|
||||||
fprintf(stderr, "flan: reload queue full after it was checked\n");
|
fprintf(stderr, "flan: reload queue full after it was checked\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -2498,8 +2521,17 @@ static const char *daemon_socket(void) {
|
|||||||
return NULL;
|
return NULL;
|
||||||
pid = strtol(own, &end, 10);
|
pid = strtol(own, &end, 10);
|
||||||
if (end == own || *end != '\0' || pid <= 0) return NULL;
|
if (end == own || *end != '\0' || pid <= 0) return NULL;
|
||||||
if (pid != (long)getpid() && pid != (long)getppid()) return NULL;
|
if (pid == (long)getpid()) return env;
|
||||||
return env;
|
/* The parent only under --two-process, which is the one shape that sets
|
||||||
|
* FLAN_DEV_PARENT, and to the same pid. In a merged build the owner is the
|
||||||
|
* program itself, so a process it starts has the owner as its parent and
|
||||||
|
* must not take the socket. */
|
||||||
|
{
|
||||||
|
const char *par = getenv("FLAN_DEV_PARENT");
|
||||||
|
if (par != NULL && strcmp(par, own) == 0 && pid == (long)getppid())
|
||||||
|
return env;
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* [path] is a Flan string: ptr and len, not NUL-terminated.
|
/* [path] is a Flan string: ptr and len, not NUL-terminated.
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user