Convert array literals into as dval parameters

An array type is constructible into dval like scalars and strings, so
port_spawn(["cat"]) resolves and lowers through the dval constructor; a
non-literal array still fails there with the existing message. The service
fixtures and the port_spawn page use the literal form again, and the services
guide names the oversized-message refusal beside the other two reasons a send
returns false.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
udoandClaude Fable 5.1 committed 2026-10-14 00:37:46 +00:00
1 parent 8423307a9e
commit 5837f200cb
9 files changed
+19 -15

No files matched your search

+2 -2
View File
@@ -185,11 +185,11 @@ e7755838e50bc6c5694efbb9c50c7500c56e4a68d880c5a2117738a55501b20a site_tests_cap
8c7687f9f1f2fd8b214f54005c2f2138a0347e223d5db0eeb06bc8bd524626e5 site_tests_capy-serve-http.capy.wasm
07f986eb52a6ec08b6b2ab7c79315913f7013232a7c09873143712a4b859945a site_tests_capy-serve-http.capy.wasm.source-map
3789bade37c376d4b7ba3358ef7d4a73075ec08c436e40e46ebffc55b67d8663 site_tests_capy-services-api.capy.wasm
05fe02b659796a60e5ad805987c2a84f3b5dc4bafa1c6c11204127898adec6a4 site_tests_capy-services-api.capy.wasm.source-map
921f394702d8deb0471b75a506ade730876f961f1a34234c71427f1de5aa9cc2 site_tests_capy-services-api.capy.wasm.source-map
9458cf9aa5278841cee3646f11d1acc57baad307306b3061bcccb4c00bf3a946 site_tests_capy-services-api-dep.capy.wasm
c3fd87156412bcca1629178b52d15a997f3108f479fb11c25ed96e215218b540 site_tests_capy-services-api-dep.capy.wasm.source-map
8b1fa9f2856e2d5ccca4702cb75dedbdf66f6f5c6908ea5aeae747dcc7015d1c site_tests_capy-services-slice.capy.wasm
5c393f02e13ee7255d0efbb87a54129b32888516513bd9b2058863a6629855ac site_tests_capy-services-slice.capy.wasm.source-map
8ca06db55cab8e106c829ee54756a4d8588698c40cb70954b38d583133e6591b site_tests_capy-services-slice.capy.wasm.source-map
b38be0d419a49e12f46db3fb520e0f3689a46d069ecc0638f6bcd894c6c2898d site_tests_capy-session.capy.wasm
db859884cce403a35a0c4e43088e5673713d494d738e0d8e6c877ea547863396 site_tests_capy-session.capy.wasm.source-map
4d2899e11780142c94f5a5e9f76d9d88a6051989bc8bb19ec2034f439f09a034 site_tests_capy-shell-exec-background-type-trap.capy.wasm
+4 -1
View File
@@ -314,6 +314,10 @@ int main()
assert(unit_validation.valid);
assert(std::any_of(unit_validation.exports.begin(), unit_validation.exports.end(),
[](const auto& exported) { return exported.name == "__wasm_call_ctors" && exported.kind == 0; }));
// An array literal converts into an `as dval` parameter through the dval constructor.
const auto array_as_dval = capy::compile_bearer_unit(
"function take(v : as dval) dval { -> v }\nfunction RENDER() { print(string(take([\"a\", \"b\"])), string(take(\"s\"))) }\n", options);
assert(capy::wasm::validate_bearer_unit(array_as_dval.wasm, {.bearer_abi_version = "11"}).valid);
// Service handlers are named props handlers; every name is an export the runtime dispatches by event.
const auto services = capy::compile_bearer_unit(
"function SERVICE:START(props : dval) { print(string(props.count), string(request.unit)) }\n"
@@ -537,7 +541,6 @@ int main()
const std::string generic_before_dval_bytes(generic_before_dval.wasm.begin(), generic_before_dval.wasm.end());
assert(generic_before_dval_bytes.find("bearer_dv_s32") == std::string::npos);
for (const char* source : {
"function accept(value : as dval) dval { -> value }\nfunction CLI(request : dval) { accept([1]) }\n",
"struct Value { item : s32 }\nfunction accept(value : as dval) dval { -> value }\nfunction CLI(request : dval) { accept(Value(1)) }\n",
})
{
+1 -1
View File
@@ -1,5 +1,5 @@
function COMPONENT(props : dval)
{
print("<div class=\"doc-detail-layout\"><main class=\"content doc-page doc-detail\"><div class=\"doc-title\"><h2>port_spawn</h2><span class=\"call-boundary lib-call\">lib call</span></div><div class=\"doc-section signatures\"><h3>Capy</h3><pre>function port_spawn(argv : as dval, env : as dval = {}) u64</pre></div><div class=\"doc-section content\"><h3>Description</h3><p><code>port_spawn()</code> starts a child process owned by the service, with its standard input, output, and error as pipes the service loop watches. Output arrives as <code>SERVICE:DATA</code> events with <code>port</code>, <code>stream</code> (<code>stdout</code> or <code>stderr</code>), and the bytes that were readable, at most one read per event. When the child exits, <code>SERVICE:EXIT</code> arrives with <code>port</code> and <code>status</code> after the last <code>DATA</code> of that child.</p><p>Write to the child&#39;s standard input with <code>port_write()</code>. <code>port_close()</code> closes the child&#39;s standard input, sends it <code>SIGTERM</code>, and <code>SIGKILL</code> one second later when it is still alive; its remaining output is still delivered, and <code>SERVICE:EXIT</code> arrives last. A service that is stopped or crashes takes its children with it.</p><p>The child inherits the worker&#39;s environment with the <code>env</code> entries added or replaced. A child that cannot be executed still yields a handle; its <code>SERVICE:EXIT</code> then carries status 127 and an <code>error</code>.</p><p>The program is executed directly, not through a shell. Build <code>argv</code> as a list; do not join it into one string.</p></div><div class=\"doc-section params\"><h3>Parameters</h3><div><p>argv : the program and its arguments as a list of strings</p></div><div><p>env : environment variables for the chi")
print("ld as a map of strings, added to the worker&#39;s environment</p></div><div></div></div><div class=\"doc-section\"><h3>Return Values</h3><p>a port handle, or zero when the child could not be started</p></div><div class=\"doc-section\"><h3>Errors</h3><p>A failure to create the pipes or to start the helper yields zero and an operator log line. A program that cannot be executed is reported afterwards: the handle is returned and its <code>SERVICE:EXIT</code> carries status 127 and an <code>error</code>. Outside a service worker the call traps.</p></div><div class=\"doc-section example\"><h3>Example</h3><div class=\"example-language capy\"><h4>Capy</h4><pre class=\"example-source\">var agent := port_spawn(dval([&quot;pi&quot;, &quot;--json&quot;]), {&quot;HOME&quot;: &quot;/srv/agents&quot;})\nagents[string(agent)] = {started: time_precise()}\n</pre></div></div></main><aside class=\"doc-section see-also\"><h3>See also</h3><a class=\"related-link\" href=\"/doc/api/service-start/\">service_start</a><a class=\"related-link\" href=\"/doc/api/service-stop/\">service_stop</a><a class=\"related-link\" href=\"/doc/api/service-status/\">service_status</a><a class=\"related-link\" href=\"/doc/api/service-send/\">service_send</a><a class=\"related-link\" href=\"/doc/api/service-every/\">service_every</a><a class=\"related-link\" href=\"/doc/api/port-connect/\">port_connect</a><a class=\"related-link\" href=\"/doc/api/port-listen/\">port_listen</a><a class=\"related-link\" href=\"/doc/api/port-write/\">port_write</a><a class=\"related-link\" href=\"/doc/api/port-close/\">port_close</a><a class=\"related-link\" href=\"/doc/handler/service/\">SERVICE</a></aside></div>")
print("ld as a map of strings, added to the worker&#39;s environment</p></div><div></div></div><div class=\"doc-section\"><h3>Return Values</h3><p>a port handle, or zero when the child could not be started</p></div><div class=\"doc-section\"><h3>Errors</h3><p>A failure to create the pipes or to start the helper yields zero and an operator log line. A program that cannot be executed is reported afterwards: the handle is returned and its <code>SERVICE:EXIT</code> carries status 127 and an <code>error</code>. Outside a service worker the call traps.</p></div><div class=\"doc-section example\"><h3>Example</h3><div class=\"example-language capy\"><h4>Capy</h4><pre class=\"example-source\">var agent := port_spawn([&quot;pi&quot;, &quot;--json&quot;], {&quot;HOME&quot;: &quot;/srv/agents&quot;})\nagents[string(agent)] = {started: time_precise()}\n</pre></div></div></main><aside class=\"doc-section see-also\"><h3>See also</h3><a class=\"related-link\" href=\"/doc/api/service-start/\">service_start</a><a class=\"related-link\" href=\"/doc/api/service-stop/\">service_stop</a><a class=\"related-link\" href=\"/doc/api/service-status/\">service_status</a><a class=\"related-link\" href=\"/doc/api/service-send/\">service_send</a><a class=\"related-link\" href=\"/doc/api/service-every/\">service_every</a><a class=\"related-link\" href=\"/doc/api/port-connect/\">port_connect</a><a class=\"related-link\" href=\"/doc/api/port-listen/\">port_listen</a><a class=\"related-link\" href=\"/doc/api/port-write/\">port_write</a><a class=\"related-link\" href=\"/doc/api/port-close/\">port_close</a><a class=\"related-link\" href=\"/doc/handler/service/\">SERVICE</a></aside></div>")
}
+4 -4
View File
@@ -2,8 +2,8 @@ function COMPONENT(props : dval)
{
print("<div class=\"doc-detail-layout\"><main class=\"content doc-page doc-detail\"><div class=\"doc-title\"><h2>12. Services</h2></div><div class=\"doc-section content\"><p>A service is a unit that keeps running between requests. It owns I/O that a request handler cannot hold: child processes, pipes, sockets, timers. A request handler asks a service to do something by posting a message; the service answers through whatever channel fits, such as a WebSocket, the database, or the key-value cache. A pool of agents with their own processes, or a broker that keeps a connection open, is a service.</p><h2>The shape of a service</h2><p>Bearer owns the loop. Your code never blocks on a pipe or sleeps in a <code>while</code>. You write handlers, Bearer calls one for every event, each call is short and bounded, and the next event waits until the handler returns. This is the model of Erlang&#39;s <code>gen_server</code> and of Node&#39;s event loop, and it is why a service needs no locks.</p><p>Every service handler is a named <code>SERVICE</code> handler. The runtime&#39;s events are <code>START</code>, <code>STOP</code>, <code>DATA</code>, <code>EXIT</code>, and <code>ACCEPT</code>. Every other name is a signal your own code posts:</p><pre><code class=\"language-capy\">var ticks := 0\n\nfunction SERVICE:START(props : dval)\n{\n ticks = 0\n}\n\nfunction SERVICE:TICK(props : dval)\n{\n ticks = ticks + 1\n}\n\nfunction RENDER()\n{\n print(&quot;service unit ready\\n&quot;)\n}\n</code></pre><p><strong>Output</strong></p><pre><code class=\"language-text\">service unit ready\n</code></pre><p>This unit is also an ordinary page: the <code>RENDER</code> handler answers requests while the <code>SERVICE</code> handlers answer events. A service is identified by its unit path, and there is at most one running service per unit.<")
print("/p><h2>State lives in unit variables</h2><p>A service keeps one workspace for its whole life, so a top-level <code>var</code> is initialized once, when the service starts, and keeps its value across every handler call. That is where service state belongs:</p><pre><code class=\"language-capy\">var agents := {}\nvar seen := 0\n\nfunction SERVICE:START(props : dval)\n{\n agents.capacity = s64(props.count)\n}\n\nfunction SERVICE:DATA(props : dval)\n{\n seen = seen + 1\n agents.last = string(props.data)\n}\n\nfunction RENDER()\n{\n print(&quot;state in unit variables\\n&quot;)\n}\n</code></pre><p>Every handler binds <code>request</code> implicitly, as handlers always do. For a service it is a synthetic request with no session, body, or form, built once for the life of the service. It carries the start arguments in <code>request.props</code> and the unit path in <code>request.unit</code>, and it lets request builtins such as <code>ws_send()</code> and <code>unit_call()</code> work inside a service. Do not use it as a place for state.</p><h2>Start, stop, send</h2><p>A request handler, a task, or another service talks to a service through four calls. <code>service_start(unit, props)</code> records that the service should run and starts it; calling it on a running service does nothing. <code>service_stop(unit)</code> posts a <code>STOP</code> and the worker exits once <code>SERVICE:STOP</code> returns. <code>service_status(unit)</code> reports <code>state</code>, <code>pid</code>, <code>started_at</code>, <code>restarts</code>, <code>mailbox</code>, and <code>stale</code>. <code>service_send(unit, signal, props, delay_ms)</code> posts a message and returns at once:</p><p>service_send(&quot;/agents/pool.capy&quot;, &quot;assign&quot;, task: &quot;review 42&quot;)</p><p>A send starts the service when ")
print("it is not running, so callers never check liveness first. <code>service_stop()</code> therefore means stop now, not keep down: the next send brings the service back. A send returns false only when the mailbox is full or the unit does not exist. The mailbox is the kernel&#39;s datagram queue, about 500 frames per service; messages the service holds for a delay are not counted against it. A send to a listening service is one datagram, and a request never waits for the service itself.</p><p><code>delay_ms</code> is the one-shot timer. A service that posts to itself with a delay gets a retry or a backoff:</p><p>service_send(request.unit, &quot;respawn&quot;, {}, 2000)</p><h2>Intervals</h2><p>Inside a service, <code>service_every(signal, interval_ms, props)</code> delivers <code>SERVICE:&lt;signal&gt;</code> repeatedly. The loop owns the timer and re-arms it only after each delivery, so a slow handler stretches the interval instead of stacking ticks in the mailbox. One timer exists per signal name; calling it again replaces the interval, and an interval of zero cancels it.</p><p>function SERVICE:START(props : dval)\n{\n service_every(&quot;sweep&quot;, 30000)\n}</p><h2>Ports</h2><p>A port is a file descriptor the service owns and the loop watches. <code>port_spawn(argv, env)</code> starts a child with its standard streams as pipes, <code>port_connect(host, port)</code> opens a TCP connection, and <code>port_listen(path)</code> listens on a unix socket. All three return a handle. <code>port_write(handle, data)</code> queues bytes without blocking, and <code>port_close(handle)</code> closes the port and sends a spawned child <code>SIGTERM</code>.</p><p>Readiness arrives as handlers. <code>SERVICE:DATA</code> brings <code>port</code>, <code>stream</code>, and whatever bytes were r")
print("eadable, at most one read per event, so line framing is the service&#39;s job. <code>SERVICE:EXIT</code> brings <code>port</code> and <code>status</code> after a child&#39;s last <code>DATA</code>. <code>SERVICE:ACCEPT</code> brings <code>port</code> and the accepted <code>connection</code>:</p><pre><code class=\"language-capy\">var buffers := {}\n\nfunction SERVICE:DATA(props : dval)\n{\n var key := string(props.port)\n buffers[key] = string(buffers[key], fallback: &quot;&quot;) + string(props.data)\n}\n\nfunction SERVICE:EXIT(props : dval)\n{\n delete(buffers, string(props.port))\n}\n\nfunction RENDER()\n{\n print(&quot;port handlers ready\\n&quot;)\n}\n</code></pre><p>Port calls exist only inside a service worker, because a descriptor needs an owner that outlives one invocation and a loop that watches it. Anywhere else they trap.</p><h2>Failure</h2><p>Each handler call runs under the invocation deadline. A handler that hangs is interrupted, that one event is dropped with an operator log line, and the service continues. What a service prints goes to the operator log, since there is no response to write to.</p><p>A service that exits without a stop is crashed. The supervisor restarts it with increasing delays, <code>SERVICE:START</code> runs again, and <code>restarts</code> in the status counts it. This is Erlang&#39;s advice: let it crash, and make <code>START</code> able to rebuild what the service needs, including its ports and intervals. Messages queued before the crash are gone, as they are in Erlang.</p><p>A service does not restart when its source changes. <code>stale</code> in the status says that the running workspace predates the source, so an admin page can offer a restart. Stop and start it, which recompiles the unit.</p><h2>What a service is not</h2><p>A service has no HTTP r")
print("equest, no session, and no response. Do not pass a request to a service; pass identifiers and small options, as you would to a task, and let the service read what it needs. Do not wait for a service in a page request. Post the message, and let the service report through a channel the page already watches.</p></div><nav class=\"guide-navigation\" aria-label=\"Capy guide navigation\"><div class=\"guide-previous\">Previous: <a href=\"/doc/guide/tasks-and-jobs/\">11. Tasks and jobs</a></div><div class=\"guide-index\"><a href=\"/doc/\">Guide index</a></div><div class=\"guide-next\">Next: <a href=\"/doc/guide/errors-testing-and-style/\">13. Errors and testing</a></div></nav></main><aside class=\"doc-section see-also\"><h3>See also</h3><a class=\"related-link\" href=\"/doc/guide/install-and-first-program/\">1. Getting started</a><a class=\"related-link\" href=\"/doc/guide/source-structure-and-syntax/\">2. Basic syntax</a><a class=\"related-link\" href=\"/doc/guide/values-and-types/\">3. Types</a><a class=\"related-link\" href=\"/doc/guide/expressions-and-control-flow/\">4. Expressions and control flow</a><a class=\"related-link\" href=\"/doc/guide/functions-and-closures/\">5. Functions</a><a class=\"related-link\" href=\"/doc/guide/strings-and-markup/\">6. Strings and HTML</a><a class=\"related-link\" href=\"/doc/guide/collections-and-records/\">7. Arrays and structs</a><a class=\"related-link\" href=\"/doc/guide/dynamic-values/\">8. Dynamic values</a><a class=\"related-link\" href=\"/doc/guide/web-handlers-and-requests/\">9. Handling a web request</a><a class=\"related-link\" href=\"/doc/guide/units-components-and-exports/\">10. Units and components</a><a class=\"related-link\" href=\"/doc/guide/tasks-and-jobs/\">11. Tasks and jobs</a><a class=\"related-link\" href=\"/doc/guide/errors-testing-and-style/\">13. Errors and testing</a><a class=")
print("\"related-link\" href=\"/doc/guide/coming-from-react/\">14. Coming from React</a><a class=\"related-link\" href=\"/doc/handler/service/\">SERVICE</a></aside></div>")
print("it is not running, so callers never check liveness first. <code>service_stop()</code> therefore means stop now, not keep down: the next send brings the service back. A send returns false only when the mailbox is full, the message is larger than one datagram, or the unit does not exist. The mailbox is the kernel&#39;s datagram queue, about 500 frames per service; messages the service holds for a delay are not counted against it. A send to a listening service is one datagram, and a request never waits for the service itself.</p><p><code>delay_ms</code> is the one-shot timer. A service that posts to itself with a delay gets a retry or a backoff:</p><p>service_send(request.unit, &quot;respawn&quot;, {}, 2000)</p><h2>Intervals</h2><p>Inside a service, <code>service_every(signal, interval_ms, props)</code> delivers <code>SERVICE:&lt;signal&gt;</code> repeatedly. The loop owns the timer and re-arms it only after each delivery, so a slow handler stretches the interval instead of stacking ticks in the mailbox. One timer exists per signal name; calling it again replaces the interval, and an interval of zero cancels it.</p><p>function SERVICE:START(props : dval)\n{\n service_every(&quot;sweep&quot;, 30000)\n}</p><h2>Ports</h2><p>A port is a file descriptor the service owns and the loop watches. <code>port_spawn(argv, env)</code> starts a child with its standard streams as pipes, <code>port_connect(host, port)</code> opens a TCP connection, and <code>port_listen(path)</code> listens on a unix socket. All three return a handle. <code>port_write(handle, data)</code> queues bytes without blocking, and <code>port_close(handle)</code> closes the port and sends a spawned child <code>SIGTERM</code>.</p><p>Readiness arrives as handlers. <code>SERVICE:DATA</code> brings <code>port</code>, <cod")
print("e>stream</code>, and whatever bytes were readable, at most one read per event, so line framing is the service&#39;s job. <code>SERVICE:EXIT</code> brings <code>port</code> and <code>status</code> after a child&#39;s last <code>DATA</code>. <code>SERVICE:ACCEPT</code> brings <code>port</code> and the accepted <code>connection</code>:</p><pre><code class=\"language-capy\">var buffers := {}\n\nfunction SERVICE:DATA(props : dval)\n{\n var key := string(props.port)\n buffers[key] = string(buffers[key], fallback: &quot;&quot;) + string(props.data)\n}\n\nfunction SERVICE:EXIT(props : dval)\n{\n delete(buffers, string(props.port))\n}\n\nfunction RENDER()\n{\n print(&quot;port handlers ready\\n&quot;)\n}\n</code></pre><p>Port calls exist only inside a service worker, because a descriptor needs an owner that outlives one invocation and a loop that watches it. Anywhere else they trap.</p><h2>Failure</h2><p>Each handler call runs under the invocation deadline. A handler that hangs is interrupted, that one event is dropped with an operator log line, and the service continues. What a service prints goes to the operator log, since there is no response to write to.</p><p>A service that exits without a stop is crashed. The supervisor restarts it with increasing delays, <code>SERVICE:START</code> runs again, and <code>restarts</code> in the status counts it. This is Erlang&#39;s advice: let it crash, and make <code>START</code> able to rebuild what the service needs, including its ports and intervals. Messages queued before the crash are gone, as they are in Erlang.</p><p>A service does not restart when its source changes. <code>stale</code> in the status says that the running workspace predates the source, so an admin page can offer a restart. Stop and start it, which recompiles the unit.</p><h2>What a ser")
print("vice is not</h2><p>A service has no HTTP request, no session, and no response. Do not pass a request to a service; pass identifiers and small options, as you would to a task, and let the service read what it needs. Do not wait for a service in a page request. Post the message, and let the service report through a channel the page already watches.</p></div><nav class=\"guide-navigation\" aria-label=\"Capy guide navigation\"><div class=\"guide-previous\">Previous: <a href=\"/doc/guide/tasks-and-jobs/\">11. Tasks and jobs</a></div><div class=\"guide-index\"><a href=\"/doc/\">Guide index</a></div><div class=\"guide-next\">Next: <a href=\"/doc/guide/errors-testing-and-style/\">13. Errors and testing</a></div></nav></main><aside class=\"doc-section see-also\"><h3>See also</h3><a class=\"related-link\" href=\"/doc/guide/install-and-first-program/\">1. Getting started</a><a class=\"related-link\" href=\"/doc/guide/source-structure-and-syntax/\">2. Basic syntax</a><a class=\"related-link\" href=\"/doc/guide/values-and-types/\">3. Types</a><a class=\"related-link\" href=\"/doc/guide/expressions-and-control-flow/\">4. Expressions and control flow</a><a class=\"related-link\" href=\"/doc/guide/functions-and-closures/\">5. Functions</a><a class=\"related-link\" href=\"/doc/guide/strings-and-markup/\">6. Strings and HTML</a><a class=\"related-link\" href=\"/doc/guide/collections-and-records/\">7. Arrays and structs</a><a class=\"related-link\" href=\"/doc/guide/dynamic-values/\">8. Dynamic values</a><a class=\"related-link\" href=\"/doc/guide/web-handlers-and-requests/\">9. Handling a web request</a><a class=\"related-link\" href=\"/doc/guide/units-components-and-exports/\">10. Units and components</a><a class=\"related-link\" href=\"/doc/guide/tasks-and-jobs/\">11. Tasks and jobs</a><a class=\"related-link\" href=\"/doc/guide/errors-testing-and-s")
print("tyle/\">13. Errors and testing</a><a class=\"related-link\" href=\"/doc/guide/coming-from-react/\">14. Coming from React</a><a class=\"related-link\" href=\"/doc/handler/service/\">SERVICE</a></aside></div>")
}
+1 -1
View File
@@ -21,7 +21,7 @@ The program is executed directly, not through a shell. Build `argv` as a list; d
A failure to create the pipes or to start the helper yields zero and an operator log line. A program that cannot be executed is reported afterwards: the handle is returned and its `SERVICE:EXIT` carries status 127 and an `error`. Outside a service worker the call traps.
:example capy service
var agent := port_spawn(dval(["pi", "--json"]), {"HOME": "/srv/agents"})
var agent := port_spawn(["pi", "--json"], {"HOME": "/srv/agents"})
agents[string(agent)] = {started: time_precise()}
:see-group
+1 -1
View File
@@ -69,7 +69,7 @@ A request handler, a task, or another service talks to a service through four ca
service_send("/agents/pool.capy", "assign", task: "review 42")
A send starts the service when it is not running, so callers never check liveness first. `service_stop()` therefore means stop now, not keep down: the next send brings the service back. A send returns false only when the mailbox is full or the unit does not exist. The mailbox is the kernel's datagram queue, about 500 frames per service; messages the service holds for a delay are not counted against it. A send to a listening service is one datagram, and a request never waits for the service itself.
A send starts the service when it is not running, so callers never check liveness first. `service_stop()` therefore means stop now, not keep down: the next send brings the service back. A send returns false only when the mailbox is full, the message is larger than one datagram, or the unit does not exist. The mailbox is the kernel's datagram queue, about 500 frames per service; messages the service holds for a delay are not counted against it. A send to a listening service is one datagram, and a request never waits for the service itself.
`delay_ms` is the one-shot timer. A service that posts to itself with a delay gets a retry or a backoff:
+2 -2
View File
@@ -20,7 +20,7 @@ function SERVICE: START(props : dval)
{
setup()
record("START " + string(length(props)))
child = port_spawn(dval(["/bin/cat"]))
child = port_spawn(["/bin/cat"])
record("WRITE " + string(port_write(child, "hello\n")))
record("UNKNOWN " + string(port_write(999999, "x")))
record("BAD " + string(port_spawn(dval([]))))
@@ -124,7 +124,7 @@ function CLI(request : dval)
}
if mode == "spawn"
{
port_spawn(dval(["/bin/cat"]))
port_spawn(["/bin/cat"])
}
if mode == "connect"
{
+2 -2
View File
@@ -18,7 +18,7 @@ function SERVICE: START(props : dval)
{
logfile = dirname(target()) + "/events.log"
record("START " + string(service_status(target()).state))
child = port_spawn(dval(["/bin/cat"]))
child = port_spawn(["/bin/cat"])
record("WRITE " + string(port_write(child, "slice")))
service_every("BAD", 10)
service_every("BAD", 0)
@@ -154,7 +154,7 @@ function CLI: TRAP(request : dval)
}
if mode == "spawn"
{
port_spawn(dval(["/bin/cat"]))
port_spawn(["/bin/cat"])
}
if mode == "connect"
{
+2 -1
View File
@@ -1129,7 +1129,8 @@ struct Module
if (can_convert(source, target))
return true;
const std::string name = target.rfind("struct:", 0) == 0 ? target.substr(7) : target;
if (target == "dval" && (source == "string" || is_scalar(source) || source == "dval"))
// Arrays convert through the dval constructor like scalars and strings.
if (target == "dval" && (source == "string" || is_scalar(source) || source == "dval" || source.rfind("array<", 0) == 0))
return true;
if (target.rfind("struct:", 0) == 0)
{