From: Philippe Proulx Date: Mon, 5 Aug 2019 20:15:32 +0000 (-0400) Subject: lib: add bt_graph_add_simple_sink_component() X-Git-Url: http://git.efficios.com/?p=babeltrace.git;a=commitdiff_plain;h=078033edee10d9764e5779f081b38507af41bdcf lib: add bt_graph_add_simple_sink_component() This patch adds bt_graph_add_simple_sink_component(), an easy way to add a simple sink component which has a single message iterator iterating a single input port named `in` and which calls user-provided functions at specific locations. This makes it possible to create a sink component without having to: * Create a sink component class with initialization, "graph is configured", "consume", and finalization methods. * Create an input port in the component class's initialization method. * Create an input port message iterator in the component class's "graph is configured" method. * Put the input port message iterator's reference in the component class's finalization method. The goal of this new function is to make it easy to get messages at the sink endpoint of a graph, just like we used to do with the output port message iterator concept (but the graph model is honored now). The user must still call bt_graph_run() or bt_graph_run_once() to make her consume function (see details below) called: the added simple sink component is not special in any way. bt_graph_add_simple_sink_component() receives three function pointers (and custom user data): Initialize (optional): Called after the simple sink component creates the input port message iterator in the "graph is configured" method. The user function receives the message iterator to perform any initial task. Consume: Called for each "consume" method call of the simple sink component. The user function receives the message iterator and can get the next messages with bt_self_component_port_input_message_iterator_next() as usual. Finalize (optional): Called when the simple sink component is finalized. The message iterator is not available at this point. I'm not wrapping this one in Python because it's so easy to replicate with our bindings: class _SimpleSink: def __init__(self, params, consume_cb): self._consume_cb = consume_cb self._add_input_port('in') def _user_graph_is_configured(self): self._msg_iter = self._create_input_port_message_iterator( self._input_ports['in'] ) def _consume(self): self._consume_cb(self._msg_iter) def _mein_consume(msg_iter): ... ... graph.add_component(_SimpleSink, 'simple', _mein_consume) ... Signed-off-by: Philippe Proulx Change-Id: I02aaae16215160cd861c2a76793adddf808202d6 Reviewed-on: https://review.lttng.org/c/babeltrace/+/1828 Reviewed-by: Simon Marchi Tested-by: jenkins --- diff --git a/include/babeltrace2/graph/graph.h b/include/babeltrace2/graph/graph.h index 30459fc4..06e3dc44 100644 --- a/include/babeltrace2/graph/graph.h +++ b/include/babeltrace2/graph/graph.h @@ -91,6 +91,32 @@ typedef bt_graph_listener_func_status typedef void (* bt_graph_listener_removed_func)(void *data); +typedef enum bt_graph_simple_sink_component_init_func_status { + BT_GRAPH_SIMPLE_SINK_COMPONENT_INIT_FUNC_STATUS_OK = __BT_FUNC_STATUS_OK, + BT_GRAPH_SIMPLE_SINK_COMPONENT_INIT_FUNC_STATUS_ERROR = __BT_FUNC_STATUS_ERROR, + BT_GRAPH_SIMPLE_SINK_COMPONENT_INIT_FUNC_STATUS_MEMORY_ERROR = __BT_FUNC_STATUS_MEMORY_ERROR, +} bt_graph_simple_sink_component_init_func_status; + +typedef bt_graph_simple_sink_component_init_func_status +(*bt_graph_simple_sink_component_init_func)( + bt_self_component_port_input_message_iterator *iterator, + void *data); + +typedef enum bt_graph_simple_sink_component_consume_func_status { + BT_GRAPH_SIMPLE_SINK_COMPONENT_CONSUME_FUNC_STATUS_OK = __BT_FUNC_STATUS_OK, + BT_GRAPH_SIMPLE_SINK_COMPONENT_CONSUME_FUNC_STATUS_ERROR = __BT_FUNC_STATUS_ERROR, + BT_GRAPH_SIMPLE_SINK_COMPONENT_CONSUME_FUNC_STATUS_MEMORY_ERROR = __BT_FUNC_STATUS_MEMORY_ERROR, + BT_GRAPH_SIMPLE_SINK_COMPONENT_CONSUME_FUNC_STATUS_AGAIN = __BT_FUNC_STATUS_AGAIN, + BT_GRAPH_SIMPLE_SINK_COMPONENT_CONSUME_FUNC_STATUS_END = __BT_FUNC_STATUS_END, +} bt_graph_simple_sink_component_consume_func_status; + +typedef bt_graph_simple_sink_component_consume_func_status +(*bt_graph_simple_sink_component_consume_func)( + bt_self_component_port_input_message_iterator *iterator, + void *data); + +typedef void (*bt_graph_simple_sink_component_finalize_func)(void *data); + extern bt_graph *bt_graph_create(void); typedef enum bt_graph_add_component_status { @@ -142,6 +168,13 @@ bt_graph_add_sink_component_with_init_method_data( void *init_method_data, bt_logging_level log_level, const bt_component_sink **component); +extern bt_graph_add_component_status +bt_graph_add_simple_sink_component(bt_graph *graph, const char *name, + bt_graph_simple_sink_component_init_func init_func, + bt_graph_simple_sink_component_consume_func consume_func, + bt_graph_simple_sink_component_finalize_func finalize_func, + void *user_data, const bt_component_sink **component); + typedef enum bt_graph_connect_ports_status { BT_GRAPH_CONNECT_PORTS_STATUS_OK = __BT_FUNC_STATUS_OK, BT_GRAPH_CONNECT_PORTS_STATUS_ERROR = __BT_FUNC_STATUS_ERROR, diff --git a/src/lib/graph/Makefile.am b/src/lib/graph/Makefile.am index aa9d86c7..16e0a1e2 100644 --- a/src/lib/graph/Makefile.am +++ b/src/lib/graph/Makefile.am @@ -4,16 +4,18 @@ noinst_LTLIBRARIES = libgraph.la # Graph library libgraph_la_SOURCES = \ - component.c \ + component-class-sink-simple.c \ + component-class-sink-simple.h \ component-class.c \ component-class.h \ component-filter.c \ component-filter.h \ - component.h \ component-sink.c \ component-sink.h \ component-source.c \ component-source.h \ + component.c \ + component.h \ connection.c \ connection.h \ graph.c \ diff --git a/src/lib/graph/component-class-sink-simple.c b/src/lib/graph/component-class-sink-simple.c new file mode 100644 index 00000000..797ee50a --- /dev/null +++ b/src/lib/graph/component-class-sink-simple.c @@ -0,0 +1,217 @@ +/* + * Copyright 2017-2019 Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#define BT_LOG_TAG "LIB/COMPONENT-CLASS-SINK-SIMPLE" +#include "lib/logging.h" + +#include "common/assert.h" +#include "common/common.h" +#include "lib/assert-pre.h" +#include "lib/object.h" +#include +#include +#include +#include +#include +#include + +#include "component-class-sink-simple.h" +#include "lib/func-status.h" + +/* + * We keep a single simple sink component class reference. It's created + * the first time bt_component_class_sink_simple_borrow() is called and + * put by the put_simple_sink_component_class() library destructor. + */ +static +struct bt_component_class_sink *simple_comp_cls; + +struct simple_sink_data { + bt_self_component_port_input_message_iterator *msg_iter; + struct simple_sink_init_method_data init_method_data; +}; + +static +enum bt_component_class_init_method_status simple_sink_init( + struct bt_self_component_sink *self_comp, + const struct bt_value *params, void *init_method_data) +{ + int status = BT_FUNC_STATUS_OK; + struct simple_sink_data *data = NULL; + + data = g_new0(struct simple_sink_data, 1); + if (!data) { + BT_LIB_LOGE_APPEND_CAUSE( + "Failed to allocate simple sink component private data."); + status = BT_FUNC_STATUS_MEMORY_ERROR; + goto end; + } + + BT_ASSERT(init_method_data); + data->init_method_data = + *((struct simple_sink_init_method_data *) init_method_data); + + /* Add input port */ + status = bt_self_component_sink_add_input_port(self_comp, "in", + NULL, NULL); + if (status != BT_FUNC_STATUS_OK) { + BT_LIB_LOGE_APPEND_CAUSE( + "Cannot add input port to simple sink component."); + goto end; + } + + bt_self_component_set_data( + bt_self_component_sink_as_self_component(self_comp), data); + +end: + return status; +} + +static +void simple_sink_finalize(struct bt_self_component_sink *self_comp) +{ + struct simple_sink_data *data = bt_self_component_get_data( + bt_self_component_sink_as_self_component(self_comp)); + + if (data) { + if (data->init_method_data.finalize_func) { + /* Call user's finalization function */ + data->init_method_data.finalize_func( + data->init_method_data.user_data); + } + + BT_OBJECT_PUT_REF_AND_RESET(data->msg_iter); + g_free(data); + } +} + +static +enum bt_component_class_sink_graph_is_configured_method_status +simple_sink_graph_is_configured( + bt_self_component_sink *self_comp) +{ + int status = BT_FUNC_STATUS_OK; + struct simple_sink_data *data = bt_self_component_get_data( + bt_self_component_sink_as_self_component(self_comp)); + + struct bt_self_component_port_input *self_port = + bt_self_component_sink_borrow_input_port_by_name(self_comp, + "in"); + + if (!bt_port_is_connected(bt_self_component_port_as_port( + bt_self_component_port_input_as_self_component_port(self_port)))) { + BT_LIB_LOGE_APPEND_CAUSE( + "Simple sink component's input port is not connected: " + "%![comp-]+c, %![port-]+p", self_comp, self_port); + status = BT_FUNC_STATUS_ERROR; + goto end; + } + + BT_ASSERT(data); + data->msg_iter = + bt_self_component_port_input_message_iterator_create_from_sink_component( + self_comp, self_port); + if (!data->msg_iter) { + BT_LIB_LOGE_APPEND_CAUSE( + "Cannot create input port message iterator: " + "%![comp-]+c, %![port-]+p", self_comp, self_port); + status = BT_FUNC_STATUS_MEMORY_ERROR; + goto end; + } + + if (data->init_method_data.init_func) { + /* Call user's initialization function */ + status = data->init_method_data.init_func(data->msg_iter, + data->init_method_data.user_data); + if (status != BT_FUNC_STATUS_OK) { + BT_LIB_LOGW_APPEND_CAUSE( + "Simple sink component's user's initialization function failed: " + "status=%s, %![comp-]+c, %![port-]+p", + bt_common_func_status_string(status), + self_comp, self_port); + goto end; + } + } + +end: + return status; +} + +static +enum bt_component_class_sink_consume_method_status simple_sink_consume( + struct bt_self_component_sink *self_comp) +{ + int status; + struct simple_sink_data *data = bt_self_component_get_data( + bt_self_component_sink_as_self_component(self_comp)); + + BT_ASSERT(data); + BT_ASSERT(data->init_method_data.consume_func); + BT_ASSERT(data->msg_iter); + + /* Call user's "consume" function */ + status = data->init_method_data.consume_func(data->msg_iter, + data->init_method_data.user_data); + if (status != BT_FUNC_STATUS_OK) { + BT_LIB_LOGW_APPEND_CAUSE( + "Simple sink component's user's \"consume\" function failed: " + "status=%s, %![comp-]+c", + bt_common_func_status_string(status), self_comp); + } + + return status; +} + +struct bt_component_class_sink *bt_component_class_sink_simple_borrow(void) +{ + enum bt_component_class_set_method_status set_method_status; + + if (simple_comp_cls) { + goto end; + } + + simple_comp_cls = bt_component_class_sink_create("simple-sink", + simple_sink_consume); + if (!simple_comp_cls) { + BT_LIB_LOGE_APPEND_CAUSE( + "Cannot create simple sink component class."); + goto end; + } + + set_method_status = bt_component_class_sink_set_init_method( + simple_comp_cls, simple_sink_init); + BT_ASSERT(set_method_status == BT_FUNC_STATUS_OK); + set_method_status = bt_component_class_sink_set_finalize_method( + simple_comp_cls, simple_sink_finalize); + BT_ASSERT(set_method_status == BT_FUNC_STATUS_OK); + set_method_status = bt_component_class_sink_set_graph_is_configured_method( + simple_comp_cls, simple_sink_graph_is_configured); + BT_ASSERT(set_method_status == BT_FUNC_STATUS_OK); + +end: + return simple_comp_cls; +} + +__attribute__((destructor)) static +void put_simple_sink_component_class(void) { + BT_OBJECT_PUT_REF_AND_RESET(simple_comp_cls); +} diff --git a/src/lib/graph/component-class-sink-simple.h b/src/lib/graph/component-class-sink-simple.h new file mode 100644 index 00000000..9910484f --- /dev/null +++ b/src/lib/graph/component-class-sink-simple.h @@ -0,0 +1,40 @@ +#ifndef BABELTRACE_GRAPH_COMPONENT_CLASS_SINK_SIMPLE_H +#define BABELTRACE_GRAPH_COMPONENT_CLASS_SINK_SIMPLE_H + +/* + * Copyright 2017-2019 Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include +#include + +struct simple_sink_init_method_data { + bt_graph_simple_sink_component_init_func init_func; + bt_graph_simple_sink_component_consume_func consume_func; + bt_graph_simple_sink_component_finalize_func finalize_func; + void *user_data; +}; + +extern struct bt_component_class_sink * +bt_component_class_sink_simple_borrow(void); + +#endif /* BABELTRACE_GRAPH_COMPONENT_CLASS_SINK_SIMPLE_H */ diff --git a/src/lib/graph/graph.c b/src/lib/graph/graph.c index fe79fb20..d4ed5afc 100644 --- a/src/lib/graph/graph.c +++ b/src/lib/graph/graph.c @@ -42,6 +42,7 @@ #include #include +#include "component-class-sink-simple.h" #include "component.h" #include "component-sink.h" #include "connection.h" @@ -1466,6 +1467,44 @@ enum bt_graph_add_component_status bt_graph_add_sink_component( graph, comp_cls, name, params, NULL, log_level, component); } +enum bt_graph_add_component_status +bt_graph_add_simple_sink_component(struct bt_graph *graph, const char *name, + bt_graph_simple_sink_component_init_func init_func, + bt_graph_simple_sink_component_consume_func consume_func, + bt_graph_simple_sink_component_finalize_func finalize_func, + void *user_data, const bt_component_sink **component) +{ + enum bt_graph_add_component_status status; + struct bt_component_class_sink *comp_cls; + struct simple_sink_init_method_data init_method_data = { + .init_func = init_func, + .consume_func = consume_func, + .finalize_func = finalize_func, + .user_data = user_data, + }; + + /* + * Other preconditions are checked by + * bt_graph_add_sink_component_with_init_method_data(). + */ + BT_ASSERT_PRE_NON_NULL(consume_func, "Consume function"); + + comp_cls = bt_component_class_sink_simple_borrow(); + if (!comp_cls) { + BT_LIB_LOGE_APPEND_CAUSE( + "Cannot borrow simple sink component class."); + status = BT_FUNC_STATUS_MEMORY_ERROR; + goto end; + } + + status = bt_graph_add_sink_component_with_init_method_data(graph, + comp_cls, name, NULL, &init_method_data, + BT_LOGGING_LEVEL_NONE, component); + +end: + return status; +} + BT_HIDDEN int bt_graph_remove_unconnected_component(struct bt_graph *graph, struct bt_component *component)