Commit | Line | Data |
---|---|---|
784cdc68 | 1 | /* |
f2b0325d | 2 | * Copyright 2017-2018 Philippe Proulx <pproulx@efficios.com> |
784cdc68 JG |
3 | * Copyright 2017 Jérémie Galarneau <jeremie.galarneau@efficios.com> |
4 | * | |
784cdc68 JG |
5 | * Permission is hereby granted, free of charge, to any person obtaining a copy |
6 | * of this software and associated documentation files (the "Software"), to deal | |
7 | * in the Software without restriction, including without limitation the rights | |
8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
9 | * copies of the Software, and to permit persons to whom the Software is | |
10 | * furnished to do so, subject to the following conditions: | |
11 | * | |
12 | * The above copyright notice and this permission notice shall be included in | |
13 | * all copies or substantial portions of the Software. | |
14 | * | |
15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |
21 | * SOFTWARE. | |
22 | */ | |
23 | ||
a36bfb16 PP |
24 | #define BT_LOG_TAG "CONNECTION" |
25 | #include <babeltrace/lib-logging-internal.h> | |
26 | ||
8c6884d9 PP |
27 | #include <babeltrace/assert-internal.h> |
28 | #include <babeltrace/assert-pre-internal.h> | |
b2e0c907 PP |
29 | #include <babeltrace/graph/notification-iterator-internal.h> |
30 | #include <babeltrace/graph/component-internal.h> | |
b2e0c907 | 31 | #include <babeltrace/graph/connection-internal.h> |
7b53201c | 32 | #include <babeltrace/graph/connection-const.h> |
b2e0c907 PP |
33 | #include <babeltrace/graph/graph-internal.h> |
34 | #include <babeltrace/graph/port-internal.h> | |
784cdc68 | 35 | #include <babeltrace/object-internal.h> |
3d9990ac | 36 | #include <babeltrace/compiler-internal.h> |
0fbb9a9f | 37 | #include <stdlib.h> |
784cdc68 JG |
38 | #include <glib.h> |
39 | ||
40 | static | |
834e9996 | 41 | void destroy_connection(struct bt_object *obj) |
784cdc68 JG |
42 | { |
43 | struct bt_connection *connection = container_of(obj, | |
44 | struct bt_connection, base); | |
bd14d768 | 45 | |
834e9996 | 46 | BT_LIB_LOGD("Destroying connection: %!+x", connection); |
a36bfb16 | 47 | |
bd14d768 PP |
48 | /* |
49 | * Make sure that each notification iterator which was created | |
50 | * for this connection is finalized before we destroy it. Once a | |
51 | * notification iterator is finalized, all its method return | |
52 | * NULL or the BT_NOTIFICATION_ITERATOR_STATUS_CANCELED status. | |
53 | * | |
54 | * Because connections are destroyed before components within a | |
55 | * graph, this ensures that notification iterators are always | |
56 | * finalized before their upstream component. | |
c42ea0af PP |
57 | * |
58 | * Ending the connection does exactly this. We pass `false` to | |
59 | * bt_connection_end() here to avoid removing this connection | |
60 | * from the graph: if we're here, we're already in the graph's | |
61 | * destructor. | |
bd14d768 | 62 | */ |
c42ea0af PP |
63 | bt_connection_end(connection, false); |
64 | g_ptr_array_free(connection->iterators, TRUE); | |
834e9996 | 65 | connection->iterators = NULL; |
784cdc68 JG |
66 | |
67 | /* | |
8138bfe1 | 68 | * No bt_object_put_ref on ports as a connection only holds _weak_ |
c42ea0af | 69 | * references to them. |
784cdc68 JG |
70 | */ |
71 | g_free(connection); | |
72 | } | |
73 | ||
f167d3c0 | 74 | static |
834e9996 | 75 | void try_remove_connection_from_graph(struct bt_connection *connection) |
f167d3c0 | 76 | { |
1d7bf349 | 77 | void *graph = (void *) bt_object_borrow_parent(&connection->base); |
f167d3c0 | 78 | |
1d7bf349 | 79 | if (connection->base.ref_count > 0 || |
f167d3c0 PP |
80 | connection->downstream_port || |
81 | connection->upstream_port || | |
82 | connection->iterators->len > 0) { | |
83 | return; | |
84 | } | |
85 | ||
86 | /* | |
87 | * At this point we know that: | |
88 | * | |
a36bfb16 | 89 | * 1. The connection is ended (ports were disconnected). |
f167d3c0 PP |
90 | * 2. All the notification iterators that this connection |
91 | * created, if any, are finalized. | |
92 | * 3. The connection's reference count is 0, so only the | |
93 | * parent (graph) owns this connection after this call. | |
94 | * | |
95 | * In other words, no other object than the graph knows this | |
96 | * connection. | |
97 | * | |
98 | * It is safe to remove the connection from the graph, therefore | |
99 | * destroying it. | |
100 | */ | |
834e9996 PP |
101 | BT_LIB_LOGD("Removing self from graph's connections: " |
102 | "%![graph-]+g, %![conn-]+x", graph, connection); | |
f167d3c0 PP |
103 | bt_graph_remove_connection(graph, connection); |
104 | } | |
105 | ||
106 | static | |
834e9996 | 107 | void parent_is_owner(struct bt_object *obj) |
f167d3c0 PP |
108 | { |
109 | struct bt_connection *connection = container_of(obj, | |
110 | struct bt_connection, base); | |
111 | ||
834e9996 | 112 | try_remove_connection_from_graph(connection); |
890882ef PP |
113 | } |
114 | ||
784cdc68 | 115 | BT_HIDDEN |
834e9996 | 116 | struct bt_connection *bt_connection_create(struct bt_graph *graph, |
72b913fb PP |
117 | struct bt_port *upstream_port, |
118 | struct bt_port *downstream_port) | |
784cdc68 JG |
119 | { |
120 | struct bt_connection *connection = NULL; | |
121 | ||
834e9996 PP |
122 | BT_LIB_LOGD("Creating connection: " |
123 | "%![graph-]+g, %![up-port-]+p, %![down-port-]+p", | |
124 | graph, upstream_port, downstream_port); | |
784cdc68 JG |
125 | connection = g_new0(struct bt_connection, 1); |
126 | if (!connection) { | |
a36bfb16 | 127 | BT_LOGE_STR("Failed to allocate one connection."); |
784cdc68 JG |
128 | goto end; |
129 | } | |
130 | ||
1d7bf349 | 131 | bt_object_init_shared_with_parent(&connection->base, |
834e9996 | 132 | destroy_connection); |
1d7bf349 | 133 | bt_object_set_parent_is_owner_listener_func(&connection->base, |
834e9996 | 134 | parent_is_owner); |
bd14d768 PP |
135 | connection->iterators = g_ptr_array_new(); |
136 | if (!connection->iterators) { | |
a36bfb16 | 137 | BT_LOGE_STR("Failed to allocate a GPtrArray."); |
8138bfe1 | 138 | BT_OBJECT_PUT_REF_AND_RESET(connection); |
bd14d768 PP |
139 | goto end; |
140 | } | |
141 | ||
784cdc68 | 142 | /* Weak references are taken, see comment in header. */ |
72b913fb PP |
143 | connection->upstream_port = upstream_port; |
144 | connection->downstream_port = downstream_port; | |
834e9996 | 145 | BT_LIB_LOGD("Setting upstream port's connection: %!+p", upstream_port); |
72b913fb | 146 | bt_port_set_connection(upstream_port, connection); |
834e9996 PP |
147 | BT_LIB_LOGD("Setting downstream port's connection: %!+p", |
148 | downstream_port); | |
72b913fb | 149 | bt_port_set_connection(downstream_port, connection); |
1d7bf349 | 150 | bt_object_set_parent(&connection->base, &graph->base); |
834e9996 | 151 | BT_LIB_LOGD("Created connection: %!+x", connection); |
a36bfb16 | 152 | |
784cdc68 JG |
153 | end: |
154 | return connection; | |
155 | } | |
156 | ||
72b913fb | 157 | BT_HIDDEN |
834e9996 | 158 | void bt_connection_end(struct bt_connection *conn, bool try_remove_from_graph) |
72b913fb PP |
159 | { |
160 | struct bt_component *downstream_comp = NULL; | |
161 | struct bt_component *upstream_comp = NULL; | |
162 | struct bt_port *downstream_port = conn->downstream_port; | |
163 | struct bt_port *upstream_port = conn->upstream_port; | |
c42ea0af | 164 | struct bt_graph *graph = bt_connection_borrow_graph(conn); |
f167d3c0 | 165 | size_t i; |
72b913fb | 166 | |
834e9996 | 167 | BT_LIB_LOGD("Ending connection: %!+x, try-remove-from-graph=%d", |
c42ea0af PP |
168 | conn, try_remove_from_graph); |
169 | ||
834e9996 PP |
170 | /* |
171 | * Any of the following notification callback functions could | |
172 | * remove one of the connection's ports from its component. To | |
173 | * make sure that at least logging in called functions works | |
174 | * with existing objects, get a local reference on both ports. | |
175 | */ | |
176 | bt_object_get_ref(downstream_port); | |
177 | bt_object_get_ref(upstream_port); | |
178 | ||
72b913fb | 179 | if (downstream_port) { |
834e9996 PP |
180 | BT_LIB_LOGD("Disconnecting connection's downstream port: %!+p", |
181 | downstream_port); | |
7b53201c PP |
182 | downstream_comp = bt_port_borrow_component_inline( |
183 | downstream_port); | |
72b913fb PP |
184 | bt_port_set_connection(downstream_port, NULL); |
185 | conn->downstream_port = NULL; | |
186 | } | |
187 | ||
188 | if (upstream_port) { | |
834e9996 PP |
189 | BT_LIB_LOGD("Disconnecting connection's upstream port: %!+p", |
190 | upstream_port); | |
7b53201c PP |
191 | upstream_comp = bt_port_borrow_component_inline( |
192 | upstream_port); | |
72b913fb PP |
193 | bt_port_set_connection(upstream_port, NULL); |
194 | conn->upstream_port = NULL; | |
195 | } | |
196 | ||
834e9996 PP |
197 | if (downstream_comp && conn->notified_downstream_port_connected && |
198 | !conn->notified_downstream_port_disconnected) { | |
a36bfb16 | 199 | /* bt_component_port_disconnected() logs details */ |
72b913fb PP |
200 | bt_component_port_disconnected(downstream_comp, |
201 | downstream_port); | |
834e9996 | 202 | conn->notified_downstream_port_disconnected = true; |
72b913fb PP |
203 | } |
204 | ||
834e9996 PP |
205 | if (upstream_comp && conn->notified_upstream_port_connected && |
206 | !conn->notified_upstream_port_disconnected) { | |
a36bfb16 | 207 | /* bt_component_port_disconnected() logs details */ |
72b913fb | 208 | bt_component_port_disconnected(upstream_comp, upstream_port); |
834e9996 | 209 | conn->notified_upstream_port_disconnected = true; |
72b913fb PP |
210 | } |
211 | ||
8b45963b | 212 | BT_ASSERT(graph); |
634f394c | 213 | |
834e9996 PP |
214 | if (conn->notified_graph_ports_connected && |
215 | !conn->notified_graph_ports_disconnected) { | |
634f394c PP |
216 | /* bt_graph_notify_ports_disconnected() logs details */ |
217 | bt_graph_notify_ports_disconnected(graph, upstream_comp, | |
218 | downstream_comp, upstream_port, downstream_port); | |
834e9996 | 219 | conn->notified_graph_ports_disconnected = true; |
634f394c PP |
220 | } |
221 | ||
834e9996 PP |
222 | /* |
223 | * It is safe to put the local port references now that we don't | |
224 | * need them anymore. This could indeed destroy them. | |
225 | */ | |
226 | bt_object_put_ref(downstream_port); | |
227 | bt_object_put_ref(upstream_port); | |
f167d3c0 PP |
228 | |
229 | /* | |
a36bfb16 | 230 | * Because this connection is ended, finalize (cancel) each |
f167d3c0 PP |
231 | * notification iterator created from it. |
232 | */ | |
233 | for (i = 0; i < conn->iterators->len; i++) { | |
834e9996 | 234 | struct bt_self_component_port_input_notification_iterator *iterator = |
f167d3c0 PP |
235 | g_ptr_array_index(conn->iterators, i); |
236 | ||
834e9996 PP |
237 | BT_LIB_LOGD("Finalizing notification iterator created by " |
238 | "this ended connection: %![iter-]+i", iterator); | |
239 | bt_self_component_port_input_notification_iterator_finalize( | |
240 | iterator); | |
f167d3c0 PP |
241 | |
242 | /* | |
243 | * Make sure this iterator does not try to remove itself | |
244 | * from this connection's iterators on destruction | |
245 | * because this connection won't exist anymore. | |
246 | */ | |
834e9996 | 247 | bt_self_component_port_input_notification_iterator_set_connection( |
fe7265b5 | 248 | iterator, NULL); |
f167d3c0 PP |
249 | } |
250 | ||
251 | g_ptr_array_set_size(conn->iterators, 0); | |
c42ea0af PP |
252 | |
253 | if (try_remove_from_graph) { | |
834e9996 | 254 | try_remove_connection_from_graph(conn); |
c42ea0af | 255 | } |
72b913fb PP |
256 | } |
257 | ||
7b53201c PP |
258 | const struct bt_port_output *bt_connection_borrow_upstream_port_const( |
259 | const struct bt_connection *connection) | |
784cdc68 | 260 | { |
834e9996 PP |
261 | BT_ASSERT_PRE_NON_NULL(connection, "Connection"); |
262 | return (void *) connection->upstream_port; | |
784cdc68 JG |
263 | } |
264 | ||
7b53201c PP |
265 | const struct bt_port_input *bt_connection_borrow_downstream_port_const( |
266 | const struct bt_connection *connection) | |
784cdc68 | 267 | { |
834e9996 PP |
268 | BT_ASSERT_PRE_NON_NULL(connection, "Connection"); |
269 | return (void *) connection->downstream_port; | |
784cdc68 | 270 | } |
bd14d768 PP |
271 | |
272 | BT_HIDDEN | |
273 | void bt_connection_remove_iterator(struct bt_connection *conn, | |
834e9996 | 274 | struct bt_self_component_port_input_notification_iterator *iterator) |
bd14d768 PP |
275 | { |
276 | g_ptr_array_remove(conn->iterators, iterator); | |
834e9996 PP |
277 | BT_LIB_LOGV("Removed notification iterator from connection: " |
278 | "%![conn-]+x, %![iter-]+i", conn, iterator); | |
279 | try_remove_connection_from_graph(conn); | |
bd14d768 | 280 | } |
090a4c0a | 281 | |
7b53201c | 282 | bt_bool bt_connection_is_ended(const struct bt_connection *connection) |
090a4c0a PP |
283 | { |
284 | return !connection->downstream_port && !connection->upstream_port; | |
285 | } | |
8c6884d9 PP |
286 | |
287 | void bt_connection_get_ref(const struct bt_connection *connection) | |
288 | { | |
289 | bt_object_get_ref(connection); | |
290 | } | |
291 | ||
292 | void bt_connection_put_ref(const struct bt_connection *connection) | |
293 | { | |
294 | bt_object_put_ref(connection); | |
295 | } |