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:
@@ -134,6 +134,49 @@ enum {
|
||||
DPM_LOG_DEBUG = 4
|
||||
};
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* Results */
|
||||
/* ------------------------------------------------------------------ */
|
||||
|
||||
/**
|
||||
* @brief Outcome of a dispatch, carried in dpm_result.status
|
||||
*/
|
||||
enum {
|
||||
/** The module returned 0. */
|
||||
DPM_RESULT_OK = 0,
|
||||
|
||||
/** The module returned nonzero; error_code carries the value. */
|
||||
DPM_RESULT_ERROR = 1
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief What a dispatch returns
|
||||
*
|
||||
* Filled by dpm_execute() on every call. status and error_code are the
|
||||
* envelope, the same for every module and every command. data is the
|
||||
* payload: what the module chose to return for the command, in a
|
||||
* layout that module documents for that command at that version. The
|
||||
* caller that dispatched the command knows the layout it asked for.
|
||||
*
|
||||
* The payload belongs to the module that produced it, and release is
|
||||
* how that module frees it. dpm_result_release() calls release and
|
||||
* clears both fields. A module that returns no payload leaves data and
|
||||
* release NULL.
|
||||
*/
|
||||
typedef struct dpm_result {
|
||||
/** DPM_RESULT_OK or DPM_RESULT_ERROR. */
|
||||
int status;
|
||||
|
||||
/** The module's return value; 0 when status is DPM_RESULT_OK. */
|
||||
int error_code;
|
||||
|
||||
/** The payload, or NULL. */
|
||||
void* data;
|
||||
|
||||
/** Frees data; NULL when data needs no release. */
|
||||
void (*release)(void* data);
|
||||
} dpm_result;
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* Context configuration overrides */
|
||||
/* ------------------------------------------------------------------ */
|
||||
@@ -285,21 +328,40 @@ int dpm_get_module_info(dpm_ctx* ctx, dpm_module* mod,
|
||||
* argc > 0; semantics beyond that are the module's to define.
|
||||
*
|
||||
* This is the only path into module code. A caller addresses a module
|
||||
* by name and a capability by command string, so it holds no
|
||||
* compile-time knowledge of the module it is calling — the same call
|
||||
* by name and a capability by command string, and the same call is what
|
||||
* a module makes to reach a peer.
|
||||
*
|
||||
* The envelope at `out` is filled on every call: status and error_code
|
||||
* from the module's return value, and data and release from what the
|
||||
* module handed to dpm_set_result() during the call. The caller owns
|
||||
* the envelope's storage and releases the payload with
|
||||
* dpm_result_release() when it is done with it. Passing NULL for `out`
|
||||
* discards the payload before this call returns.
|
||||
*
|
||||
* @param ctx The libdpm-core.so context
|
||||
* @param mod A module handle from dpm_require()
|
||||
* @param command The command name; NULL or empty behaves as the
|
||||
* module's help command
|
||||
* @param argc Number of arguments
|
||||
* @param argv Argument vector
|
||||
* @param out Receives the result envelope; NULL discards the payload
|
||||
* @return The module's return value verbatim; 0 on success
|
||||
*/
|
||||
DPM_PUBLIC_ABI_EXPORT
|
||||
int dpm_execute(dpm_ctx* ctx, dpm_module* mod, const char* command,
|
||||
int argc, char** argv);
|
||||
int argc, char** argv, dpm_result* out);
|
||||
|
||||
/**
|
||||
* @brief Releases a result's payload
|
||||
*
|
||||
* Calls the release function the module supplied, when there is one,
|
||||
* and clears data and release. status and error_code are left as they
|
||||
* were.
|
||||
*
|
||||
* @param result The envelope whose payload is released; NULL is a no-op
|
||||
*/
|
||||
DPM_PUBLIC_ABI_EXPORT
|
||||
void dpm_result_release(dpm_result* result);
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* Installation */
|
||||
@@ -520,6 +582,28 @@ void dpm_set_last_error(dpm_ctx* ctx, const char* msg);
|
||||
DPM_PUBLIC_ABI_EXPORT
|
||||
const char* dpm_get_last_error(dpm_ctx* ctx);
|
||||
|
||||
/**
|
||||
* @brief Hands a payload to the dispatch in progress
|
||||
*
|
||||
* A module returns data to its caller by calling this from inside
|
||||
* dpm_module_execute. The payload lands in the envelope the caller
|
||||
* passed to dpm_execute(), and release is what frees it when the caller
|
||||
* is done. Nested dispatches each have their own envelope, so a payload
|
||||
* set by a peer the module called does not reach the module's own
|
||||
* caller unless the module sets it again itself.
|
||||
*
|
||||
* A second call during the same dispatch releases the earlier payload
|
||||
* and replaces it. Outside any dispatch there is no envelope to receive
|
||||
* the payload, so it is released at once.
|
||||
*
|
||||
* @param ctx The libdpm-core.so context handed to the module's entry
|
||||
* point; NULL is a no-op
|
||||
* @param data The payload; NULL clears it
|
||||
* @param release Frees data; NULL when data needs no release
|
||||
*/
|
||||
DPM_PUBLIC_ABI_EXPORT
|
||||
void dpm_set_result(dpm_ctx* ctx, void* data, void (*release)(void* data));
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* Module contract (implemented by modules, called by libdpm-core.so) */
|
||||
/* ------------------------------------------------------------------ */
|
||||
@@ -534,9 +618,11 @@ const char* dpm_get_last_error(dpm_ctx* ctx);
|
||||
* const char* dpm_module_aliases(void);
|
||||
*
|
||||
* dpm_module_execute is the only entry through which a module performs
|
||||
* work. The other three are what it reports about itself; the library
|
||||
* reads them at load, serves the version and description through
|
||||
* dpm_get_module_info, and records the aliases at installation.
|
||||
* work. Its return value becomes the envelope's status and error_code,
|
||||
* and a payload for the caller is handed to dpm_set_result before it
|
||||
* returns. The other three are what it reports about itself; the
|
||||
* library reads them at load, serves the version and description
|
||||
* through dpm_get_module_info, and records the aliases at installation.
|
||||
*
|
||||
* dpm_module_aliases returns a comma-separated list of alternate names
|
||||
* the module answers to, or NULL to declare none. NULL is an answer; the
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "internal/metadata.hpp"
|
||||
#include "internal/modules.hpp"
|
||||
@@ -73,6 +74,9 @@ struct dpm_ctx {
|
||||
|
||||
/** Reason for the most recent failure. */
|
||||
std::string last_error;
|
||||
|
||||
/** Envelopes of the dispatches in progress, innermost last. */
|
||||
std::vector<dpm_result*> result_frames;
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user