lib: graph: disallow recursive consuming
authorPhilippe Proulx <eeppeliteloop@gmail.com>
Fri, 18 Aug 2017 20:43:18 +0000 (16:43 -0400)
committerJérémie Galarneau <jeremie.galarneau@efficios.com>
Fri, 15 Sep 2017 18:24:01 +0000 (14:24 -0400)
Do not allow a user component to consume or run its parent graph if the
graph is in one of the following states (operations):

* Consuming (running)
* Connection ports
* Adding (creating) a component

test_graph.py tests this feature through the Python bindings.

Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
Signed-off-by: Jérémie Galarneau <jeremie.galarneau@efficios.com>
bindings/python/bt2/bt2/__init__.py.in
bindings/python/bt2/bt2/graph.py
bindings/python/bt2/bt2/native_btgraph.i
include/babeltrace/graph/graph-internal.h
include/babeltrace/graph/graph.h
lib/graph/graph.c
tests/bindings/python/bt2/test_graph.py

index 2cc4baaa51f4837cfda3c5ea6e9bd28ccfaa54f4..0a49b7948a72317baa7ef44c8f2deed5e7d11192 100644 (file)
@@ -128,6 +128,10 @@ class IncompleteUserClass(Error):
     pass
 
 
+class CannotConsumeGraph(Error):
+    pass
+
+
 class GraphCanceled(Exception):
     pass
 
index 56148395deafdea4d657896a7605f586febdf83a..ce861028f9388988457e9d2be96f8df95ba58144 100644 (file)
@@ -106,6 +106,8 @@ class Graph(object._Object):
             raise bt2.TryAgain
         elif status == native_bt.GRAPH_STATUS_NO_SINK:
             raise bt2.NoSinkComponent
+        elif status == native_bt.GRAPH_STATUS_CANNOT_CONSUME:
+            raise bt2.CannotConsumeGraph
         elif status < 0:
             raise bt2.Error(gen_error_msg)
 
index f8ee71dac98c6ad9a2145137f56a38518f072b9e..395f6f38ee3591577ce8bf91550d4d6c0eb93c67 100644 (file)
@@ -34,6 +34,7 @@ enum bt_graph_status {
        BT_GRAPH_STATUS_OK = 0,
        BT_GRAPH_STATUS_INVALID = -22,
        BT_GRAPH_STATUS_NO_SINK = -6,
+       BT_GRAPH_STATUS_CANNOT_CONSUME = -2,
        BT_GRAPH_STATUS_ERROR = -1,
        BT_GRAPH_STATUS_NOMEM = -12,
 };
index 677c65bd219edc5cf1b9779ac1a60e3b79ce955b..7fdca46ae8cdfb105ad7b30cd3188c95f9c36045 100644 (file)
@@ -60,6 +60,7 @@ struct bt_graph {
        bt_bool canceled;
        bt_bool in_remove_listener;
        bt_bool has_sink;
+       bt_bool can_consume;
 
        struct {
                GArray *port_added;
@@ -113,6 +114,8 @@ const char *bt_graph_status_string(enum bt_graph_status status)
                return "BT_GRAPH_STATUS_COMPONENT_REFUSES_PORT_CONNECTION";
        case BT_GRAPH_STATUS_NOMEM:
                return "BT_GRAPH_STATUS_NOMEM";
+       case BT_GRAPH_STATUS_CANNOT_CONSUME:
+               return "BT_GRAPH_STATUS_CANNOT_CONSUME";
        default:
                return "(unknown)";
        }
index f790e4496813c2a884d0605fdefb9b12c95142cc..f85597845e718d871b30772a377b10fb565927aa 100644 (file)
@@ -52,6 +52,7 @@ enum bt_graph_status {
        BT_GRAPH_STATUS_NO_SINK = -6,
        /** General error. */
        BT_GRAPH_STATUS_ERROR = -1,
+       BT_GRAPH_STATUS_CANNOT_CONSUME = -2,
        BT_GRAPH_STATUS_NOMEM = -12,
 };
 
index cb197eed098414eba0698773c2823f5aec41d802..b6460f74e228bd684c6ad9f59b8fa139f440475a 100644 (file)
@@ -188,6 +188,7 @@ struct bt_graph *bt_graph_create(void)
                goto error;
        }
 
+       graph->can_consume = BT_TRUE;
        ret = init_listeners_array(&graph->listeners.port_added);
        if (ret) {
                BT_LOGE_STR("Cannot create the \"port added\" listener array.");
@@ -232,6 +233,7 @@ enum bt_graph_status bt_graph_connect_ports(struct bt_graph *graph,
        struct bt_component *upstream_component = NULL;
        struct bt_component *downstream_component = NULL;
        enum bt_component_status component_status;
+       const bt_bool init_can_consume = graph->can_consume;
 
        if (!graph) {
                BT_LOGW_STR("Invalid parameter: graph is NULL.");
@@ -264,6 +266,8 @@ enum bt_graph_status bt_graph_connect_ports(struct bt_graph *graph,
                goto end;
        }
 
+       graph->can_consume = BT_FALSE;
+
        /* Ensure appropriate types for upstream and downstream ports. */
        if (bt_port_get_type(upstream_port) != BT_PORT_TYPE_OUTPUT) {
                BT_LOGW_STR("Invalid parameter: upstream port is not an output port.");
@@ -406,6 +410,7 @@ end:
        bt_put(upstream_component);
        bt_put(downstream_component);
        bt_put(connection);
+       graph->can_consume = init_can_consume;
        return status;
 }
 
@@ -490,7 +495,15 @@ enum bt_graph_status bt_graph_consume(struct bt_graph *graph)
                goto end;
        }
 
+       if (!graph->can_consume) {
+               BT_LOGW_STR("Cannot consume graph in its current state.");
+               status = BT_GRAPH_STATUS_CANNOT_CONSUME;
+               goto end;
+       }
+
+       graph->can_consume = BT_FALSE;
        status = bt_graph_consume_no_check(graph);
+       graph->can_consume = BT_TRUE;
 
 end:
        return status;
@@ -513,6 +526,13 @@ enum bt_graph_status bt_graph_run(struct bt_graph *graph)
                goto end;
        }
 
+       if (!graph->can_consume) {
+               BT_LOGW_STR("Cannot run graph in its current state.");
+               status = BT_GRAPH_STATUS_CANNOT_CONSUME;
+               goto end;
+       }
+
+       graph->can_consume = BT_FALSE;
        BT_LOGV("Running graph: addr=%p", graph);
 
        do {
@@ -556,6 +576,7 @@ enum bt_graph_status bt_graph_run(struct bt_graph *graph)
 
 end:
        BT_LOGV("Graph ran: status=%s", bt_graph_status_string(status));
+       graph->can_consume = BT_TRUE;
        return status;
 }
 
@@ -863,6 +884,7 @@ enum bt_graph_status bt_graph_add_component_with_init_method_data(
        struct bt_component *component = NULL;
        enum bt_component_class_type type;
        size_t i;
+       const bt_bool init_can_consume = graph->can_consume;
 
        bt_get(params);
 
@@ -878,6 +900,7 @@ enum bt_graph_status bt_graph_add_component_with_init_method_data(
                goto end;
        }
 
+       graph->can_consume = BT_FALSE;
        type = bt_component_class_get_type(component_class);
        BT_LOGD("Adding component to graph: "
                "graph-addr=%p, comp-cls-addr=%p, "
@@ -1011,6 +1034,7 @@ enum bt_graph_status bt_graph_add_component_with_init_method_data(
 end:
        bt_put(component);
        bt_put(params);
+       graph->can_consume = init_can_consume;
        return graph_status;
 }
 
index 7ce3ed8cb4f31cf45b0ab97bc5223778e59d8d13..ff4a81985b8a375362743e51f9fbbfa5c2336552 100644 (file)
@@ -155,6 +155,75 @@ class GraphTestCase(unittest.TestCase):
             conn = self._graph.connect_ports(src.output_ports['out'],
                                              sink.input_ports['in'])
 
+    def test_connect_ports_cannot_consume_accept(self):
+        class MyIter(bt2._UserNotificationIterator):
+            def __next__(self):
+                raise bt2.Stop
+
+        class MySource(bt2._UserSourceComponent,
+                       notification_iterator_class=MyIter):
+            def __init__(self, params):
+                self._add_output_port('out')
+
+        class MySink(bt2._UserSinkComponent):
+            def __init__(self, params):
+                self._add_input_port('in')
+
+            def _consume(self):
+                raise bt2.Stop
+
+            def _accept_port_connection(self, port, other_port):
+                nonlocal exc
+
+                try:
+                    self.graph.run()
+                except Exception as e:
+                    exc = e
+
+                return True
+
+        exc = None
+        src = self._graph.add_component(MySource, 'src')
+        sink = self._graph.add_component(MySink, 'sink')
+        self._graph.connect_ports(src.output_ports['out'],
+                                  sink.input_ports['in'])
+        self.assertIs(type(exc), bt2.CannotConsumeGraph)
+
+    def test_connect_ports_cannot_consume_connected(self):
+        class MyIter(bt2._UserNotificationIterator):
+            def __next__(self):
+                raise bt2.Stop
+
+        class MySource(bt2._UserSourceComponent,
+                       notification_iterator_class=MyIter):
+            def __init__(self, params):
+                self._add_output_port('out')
+
+        class MySink(bt2._UserSinkComponent):
+            def __init__(self, params):
+                self._add_input_port('in')
+
+            def _consume(self):
+                raise bt2.Stop
+
+            def _port_connected(self, port, other_port):
+                nonlocal exc
+
+                try:
+                    self.graph.run()
+                except Exception as e:
+                    exc = e
+
+                return True
+
+        exc = None
+        src = self._graph.add_component(MySource, 'src')
+        sink = self._graph.add_component(MySink, 'sink')
+        self._graph.connect_ports(src.output_ports['out'],
+                                  sink.input_ports['in'])
+        self._graph.run()
+        self.assertIs(type(exc), bt2.CannotConsumeGraph)
+
     def test_cancel(self):
         self.assertFalse(self._graph.is_canceled)
         self._graph.cancel()
@@ -388,6 +457,40 @@ class GraphTestCase(unittest.TestCase):
         with self.assertRaises(bt2.Error):
             self._graph.run()
 
+    def test_run_cannot_consume(self):
+        class MyIter(bt2._UserNotificationIterator):
+            pass
+
+        class MySource(bt2._UserSourceComponent,
+                       notification_iterator_class=MyIter):
+            def __init__(self, params):
+                self._add_output_port('out')
+
+        class MySink(bt2._UserSinkComponent):
+            def __init__(self, params):
+                self._add_input_port('in')
+                self._at = 0
+
+            def _consume(comp_self):
+                nonlocal exc
+
+                try:
+                    print('going in')
+                    comp_self.graph.run()
+                    print('going out')
+                except Exception as e:
+                    exc = e
+
+                raise bt2.Stop
+
+        exc = None
+        src = self._graph.add_component(MySource, 'src')
+        sink = self._graph.add_component(MySink, 'sink')
+        conn = self._graph.connect_ports(src.output_ports['out'],
+                                         sink.input_ports['in'])
+        self._graph.run()
+        self.assertIs(type(exc), bt2.CannotConsumeGraph)
+
     def test_listeners(self):
         class MyIter(bt2._UserNotificationIterator):
             def __next__(self):
This page took 0.030615 seconds and 4 git commands to generate.