Also allow the hook users to report failure. --- src/modules/module-experimental-router.c | 40 +++++++++++++++++++++++--------- src/pulsecore/core.h | 32 +++++++++++++++++++++++-- src/pulsecore/node.c | 15 +++++++++++- src/pulsecore/node.h | 10 ++++++++ 4 files changed, 83 insertions(+), 14 deletions(-) diff --git a/src/modules/module-experimental-router.c b/src/modules/module-experimental-router.c index fe918bc..594593d 100644 --- a/src/modules/module-experimental-router.c +++ b/src/modules/module-experimental-router.c @@ -40,14 +40,26 @@ struct userdata { static pa_hook_result_t node_set_initial_routing_cb(void *hook_data, void *call_data, void *userdata) { pa_core *core = hook_data; - pa_node *node = call_data; + pa_node_set_initial_routing_hook_data *data = call_data; + pa_node *node; int r; pa_assert(core); - pa_assert(node); + pa_assert(data); + + if (data->ret_valid) + /* Some other router module already applied some policy, we don't want + * to override that. */ + return PA_HOOK_OK; + + node = data->node; if (node->type != PA_NODE_TYPE_SINK_INPUT && node->type != PA_NODE_TYPE_SOURCE_OUTPUT) - goto finish; + /* We don't set data->ret_valid, because we don't care about nodes that + * aren't sink inputs or source outputs. If there is another router + * module, it's free to apply its policy, and otherwise pa_node_put() + * should apply its fallback policy. */ + return PA_HOOK_OK; if (node->type == PA_NODE_TYPE_SINK_INPUT) { pa_sink_input *sink_input = node->owner; @@ -60,10 +72,10 @@ static pa_hook_result_t node_set_initial_routing_cb(void *hook_data, void *call_ default_sink = pa_namereg_get_default_sink(core); - if (!default_sink) - goto fail; - - r = pa_sink_input_set_initial_sink(sink_input, default_sink); + if (default_sink) + r = pa_sink_input_set_initial_sink(sink_input, default_sink); + else + r = -PA_ERR_NOENTITY; } else { pa_source_output *source_output = node->owner; pa_source *default_source; @@ -75,10 +87,10 @@ static pa_hook_result_t node_set_initial_routing_cb(void *hook_data, void *call_ default_source = pa_namereg_get_default_source(core); - if (!default_source) - goto fail; - - r = pa_source_output_set_initial_source(source_output, default_source); + if (default_source) + r = pa_source_output_set_initial_source(source_output, default_source); + else + r = -PA_ERR_NOENTITY; } if (r < 0) @@ -87,11 +99,17 @@ static pa_hook_result_t node_set_initial_routing_cb(void *hook_data, void *call_ finish: pa_log_debug("Successfully routed node %s.", node->name); + data->ret = 0; + data->ret_valid = true; + return PA_HOOK_OK; fail: pa_log_debug("Failed to route node %s.", node->name); + data->ret = r; + data->ret_valid = true; + return PA_HOOK_OK; } diff --git a/src/pulsecore/core.h b/src/pulsecore/core.h index 78675da..443af48 100644 --- a/src/pulsecore/core.h +++ b/src/pulsecore/core.h @@ -159,8 +159,36 @@ typedef enum pa_core_hook { PA_CORE_HOOK_PORT_AVAILABLE_CHANGED, PA_CORE_HOOK_PORT_LATENCY_OFFSET_CHANGED, - /* Fired when a new node is added to the system. Router modules can use - * this to set the initial routing for the new node. Call data: pa_node. */ + /* Fired when a new node is added to the system (from pa_node_put()). + * Router modules can use this to set the initial routing for the new node. + * Call data: pa_node_set_initial_routing_hook_data, details below: + * + * struct pa_node_set_initial_routing_hook_data { + * pa_node *node; + * pa_node **requested_connections; + * unsigned n_requested_connections; + * int ret; + * bool ret_valid; + * } + * + * Users of this hook are expected to create zero or more connections + * between data.node and other nodes in the system. The requested + * connections should usually be taken into account when deciding the + * initial routing, but if you really want to ignore those, you're free to + * do so. If n_requested_connections is zero, it means "use default" (i.e. + * nothing specific was requested). XXX: We might some day need to support + * also the "don't connect anywhere" case, so the interpretation of + * n_requested_connections == 0 may change in the future... + * + * ret_valid must be set to true in the hook callback, because if it's + * false, pa_node_put() assumes that there are no router modules present so + * it will run its own fallback routing algorithm. + * + * If ret is set to a negative error code, the node creation will fail. + * Usually you should report an error if your router module can't create + * the requested connections, but you don't have to do that if you think it + * makes sense to let the node creation succeed even when the requested + * connections couldn't be created. */ PA_CORE_HOOK_NODE_SET_INITIAL_ROUTING, PA_CORE_HOOK_MAX diff --git a/src/pulsecore/node.c b/src/pulsecore/node.c index 790c054..b1dc841 100644 --- a/src/pulsecore/node.c +++ b/src/pulsecore/node.c @@ -153,6 +153,14 @@ void pa_node_free(pa_node *node) { } int pa_node_put(pa_node *node, pa_node **requested_connections, unsigned n_requested_connections) { + pa_node_set_initial_routing_hook_data data = { + .node = node, + .requested_connections = requested_connections, + .n_requested_connections = n_requested_connections, + .ret = 0, + .ret_valid = false + }; + pa_assert(node); pa_assert(node->state == PA_NODE_STATE_INIT); pa_assert(node->owner); @@ -164,7 +172,12 @@ int pa_node_put(pa_node *node, pa_node **requested_connections, unsigned n_reque if (node->monitor_of) node->monitor_of->monitor = node; - pa_hook_fire(&node->core->hooks[PA_CORE_HOOK_NODE_SET_INITIAL_ROUTING], node); + pa_hook_fire(&node->core->hooks[PA_CORE_HOOK_NODE_SET_INITIAL_ROUTING], &data); + + if (data.ret_valid && data.ret < 0) { + pa_log_info("Setting initial routing for node %s failed.", node->name); + return data.ret; + } pa_log_debug("Created node %s.", node->name); diff --git a/src/pulsecore/node.h b/src/pulsecore/node.h index 7366fed..395609e 100644 --- a/src/pulsecore/node.h +++ b/src/pulsecore/node.h @@ -77,6 +77,16 @@ struct pa_node { void *owner; }; +/* See the documentation of PA_CORE_HOOK_NODE_SET_INITIAL_ROUTING to understand + * this struct. */ +typedef struct pa_node_set_initial_routing_hook_data { + pa_node *node; + pa_node **requested_connections; + unsigned n_requested_connections; + int ret; + bool ret_valid; +} pa_node_set_initial_routing_hook_data; + pa_node_new_data *pa_node_new_data_init(pa_node_new_data *data); void pa_node_new_data_set_fallback_name_prefix(pa_node_new_data *data, const char *prefix); void pa_node_new_data_set_description(pa_node_new_data *data, const char *description); -- 1.8.3.1