From 1d9157896dc759fcf7da3803523cf32a2400f1c0 Mon Sep 17 00:00:00 2001 From: Philippe Proulx Date: Fri, 18 Aug 2017 16:43:18 -0400 Subject: [PATCH] lib: graph: disallow recursive consuming MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit 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 Signed-off-by: Jérémie Galarneau --- bindings/python/bt2/bt2/__init__.py.in | 4 + bindings/python/bt2/bt2/graph.py | 2 + bindings/python/bt2/bt2/native_btgraph.i | 1 + include/babeltrace/graph/graph-internal.h | 3 + include/babeltrace/graph/graph.h | 1 + lib/graph/graph.c | 24 +++++ tests/bindings/python/bt2/test_graph.py | 103 ++++++++++++++++++++++ 7 files changed, 138 insertions(+) diff --git a/bindings/python/bt2/bt2/__init__.py.in b/bindings/python/bt2/bt2/__init__.py.in index 2cc4baaa..0a49b794 100644 --- a/bindings/python/bt2/bt2/__init__.py.in +++ b/bindings/python/bt2/bt2/__init__.py.in @@ -128,6 +128,10 @@ class IncompleteUserClass(Error): pass +class CannotConsumeGraph(Error): + pass + + class GraphCanceled(Exception): pass diff --git a/bindings/python/bt2/bt2/graph.py b/bindings/python/bt2/bt2/graph.py index 56148395..ce861028 100644 --- a/bindings/python/bt2/bt2/graph.py +++ b/bindings/python/bt2/bt2/graph.py @@ -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) diff --git a/bindings/python/bt2/bt2/native_btgraph.i b/bindings/python/bt2/bt2/native_btgraph.i index f8ee71da..395f6f38 100644 --- a/bindings/python/bt2/bt2/native_btgraph.i +++ b/bindings/python/bt2/bt2/native_btgraph.i @@ -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, }; diff --git a/include/babeltrace/graph/graph-internal.h b/include/babeltrace/graph/graph-internal.h index 677c65bd..7fdca46a 100644 --- a/include/babeltrace/graph/graph-internal.h +++ b/include/babeltrace/graph/graph-internal.h @@ -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)"; } diff --git a/include/babeltrace/graph/graph.h b/include/babeltrace/graph/graph.h index f790e449..f8559784 100644 --- a/include/babeltrace/graph/graph.h +++ b/include/babeltrace/graph/graph.h @@ -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, }; diff --git a/lib/graph/graph.c b/lib/graph/graph.c index cb197eed..b6460f74 100644 --- a/lib/graph/graph.c +++ b/lib/graph/graph.c @@ -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; } diff --git a/tests/bindings/python/bt2/test_graph.py b/tests/bindings/python/bt2/test_graph.py index 7ce3ed8c..ff4a8198 100644 --- a/tests/bindings/python/bt2/test_graph.py +++ b/tests/bindings/python/bt2/test_graph.py @@ -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): -- 2.34.1