Dispatch returns a result envelope

dpm_execute fills a dpm_result on every call: status and error_code
from the module's return value, and a payload the module hands back
through dpm_set_result. The payload's layout belongs to the module and
is documented per command; its release function is carried in the
envelope and invoked by dpm_result_release. The context keeps a stack
of the envelopes in progress, so a module calling a peer receives the
peer's payload in its own envelope and its caller sees only what the
module sets itself.

The good fixture returns a payload and counts its releases, and the
modules test covers the envelope fields, the payload round trip, single
release, discard on a NULL envelope, and a payload set outside any
dispatch. The prose documents describe the envelope and the payload
header a module ships.
This commit is contained in:
2026-09-07 14:53:24 -04:00
parent 0291e61fd8
commit 55c852586b
13 changed files with 404 additions and 39 deletions

View File

@@ -70,13 +70,28 @@ dpm_get_module_info(ctx, mod, &info);
Deciding whether that version is suitable is yours. libdpm-core.so applies no version criterion of its own — a handle means the module is valid, not that it suits you.
`dpm_execute` invokes the module — a command name and arguments:
`dpm_execute` invokes the module — a command name, arguments, and an envelope for the result:
```
int rc = dpm_execute(ctx, mod, "command", argc, argv);
dpm_result result;
int rc = dpm_execute(ctx, mod, "command", argc, argv, &result);
```
This is the only path into module code, and it is the same path the `dpm` binary uses and the same path a module uses to reach a peer. You address a module by name and a capability by command string, so your program compiles against no module header, no struct layout, and no module symbol. What a module accepts as commands and arguments, and what its return codes mean, is documented by that module.
This is the only path into module code, and it is the same path the `dpm` binary uses and the same path a module uses to reach a peer. You address a module by name and a capability by command string. What a module accepts as commands and arguments, what its return codes mean, and what it returns as a payload, is documented by that module.
## Results
Every dispatch fills the envelope. `result.status` is `DPM_RESULT_OK` when the module returned 0 and `DPM_RESULT_ERROR` otherwise; `result.error_code` is the module's return value. Those two fields mean the same thing for every module and every command.
`result.data` is the payload: what the module returns for that command, in a layout the module documents for that command at that version. The module ships that layout as a header from its own repository, and your program includes it for the commands it reads. A module that returns nothing leaves `data` NULL.
The payload belongs to the module that produced it. When you are done with it:
```
dpm_result_release(&result);
```
That calls the release function the module supplied and clears `data`. Passing NULL as the envelope to `dpm_execute` discards the payload before the call returns, for a command whose result you do not need.
## Enumerating Modules
@@ -106,10 +121,12 @@ A name is recorded once. Adding one that is already an alias, or that belongs to
- `dpm_get_resolved_module_path`(ctx) — the resolved module directory.
- `dpm_set_last_error`(ctx, msg) — records a failure reason on the context; what a module calls to explain a nonzero return.
- `dpm_get_last_error`(ctx) — a human-readable description of the most recent failure on the context, or NULL.
- `dpm_set_result`(ctx, data, release) — hands a payload to the dispatch in progress; what a module calls to return data.
- `dpm_result_release`(result) — releases a payload through the function the module supplied.
## Ownership and Errors
Strings returned by the library are owned by the context (or by the module that produced them) and remain valid until `dpm_close`; callers never free them. Functions returning int use 0 for success. Functions returning pointers use NULL for failure, with detail available from `dpm_get_last_error`.
Strings returned by the library are owned by the context (or by the module that produced them) and remain valid until `dpm_close`; callers never free them. A payload in a `dpm_result` is owned by the module that produced it and is released with `dpm_result_release`. Functions returning int use 0 for success. Functions returning pointers use NULL for failure, with detail available from `dpm_get_last_error`.
## Complete Example
@@ -131,7 +148,9 @@ int main(void) {
return 1;
}
int rc = dpm_execute(ctx, mod, "version", 0, NULL);
dpm_result result;
int rc = dpm_execute(ctx, mod, "version", 0, NULL, &result);
dpm_result_release(&result);
dpm_close(ctx);
return rc;