lib: add bt_graph_add_simple_sink_component()
authorPhilippe Proulx <eeppeliteloop@gmail.com>
Mon, 5 Aug 2019 20:15:32 +0000 (16:15 -0400)
committerPhilippe Proulx <eeppeliteloop@gmail.com>
Tue, 6 Aug 2019 20:17:13 +0000 (16:17 -0400)
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 <eeppeliteloop@gmail.com>
Change-Id: I02aaae16215160cd861c2a76793adddf808202d6
Reviewed-on: https://review.lttng.org/c/babeltrace/+/1828
Reviewed-by: Simon Marchi <simon.marchi@efficios.com>
Tested-by: jenkins <jenkins@lttng.org>
include/babeltrace2/graph/graph.h
src/lib/graph/Makefile.am
src/lib/graph/component-class-sink-simple.c [new file with mode: 0644]
src/lib/graph/component-class-sink-simple.h [new file with mode: 0644]
src/lib/graph/graph.c

index 30459fc497a0dd74ef0b0084fee50a2b35d385a9..06e3dc44c86c53d7489589fd145039240525b20d 100644 (file)
@@ -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,
index aa9d86c7710f4cc5c12a7d9fda63e83e7cb2eebb..16e0a1e29a411d2a7fb9726f54a2f99c46e0dd7a 100644 (file)
@@ -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 (file)
index 0000000..797ee50
--- /dev/null
@@ -0,0 +1,217 @@
+/*
+ * Copyright 2017-2019 Philippe Proulx <pproulx@efficios.com>
+ *
+ * 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 <babeltrace2/graph/component-class-sink.h>
+#include <babeltrace2/graph/self-component-sink.h>
+#include <babeltrace2/graph/self-component-port.h>
+#include <babeltrace2/graph/self-component-port-input-message-iterator.h>
+#include <babeltrace2/graph/self-component.h>
+#include <glib.h>
+
+#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 (file)
index 0000000..9910484
--- /dev/null
@@ -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 <pproulx@efficios.com>
+ *
+ * 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 <stdint.h>
+#include <babeltrace2/types.h>
+#include <babeltrace2/graph/message-const.h>
+
+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 */
index fe79fb20ab9a3bff034c45b45b4acdff847be998..d4ed5afcec1bbace5f9782c7682984731799b884 100644 (file)
@@ -42,6 +42,7 @@
 #include <unistd.h>
 #include <glib.h>
 
+#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)
This page took 0.030413 seconds and 4 git commands to generate.