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 | ||
b03487ab | 24 | #define BT_LOG_TAG "LIB/CONNECTION" |
1633ef46 | 25 | #include "lib/logging.h" |
a36bfb16 | 26 | |
57952005 MJ |
27 | #include "common/assert.h" |
28 | #include "lib/assert-pre.h" | |
0341b021 | 29 | #include "lib/assert-post.h" |
7704a0af | 30 | #include <babeltrace2/graph/connection.h> |
57952005 MJ |
31 | #include "lib/object.h" |
32 | #include "compat/compiler.h" | |
969c1d8a | 33 | #include <stdbool.h> |
0fbb9a9f | 34 | #include <stdlib.h> |
784cdc68 JG |
35 | #include <glib.h> |
36 | ||
57952005 MJ |
37 | #include "component.h" |
38 | #include "connection.h" | |
39 | #include "graph.h" | |
40 | #include "message/iterator.h" | |
41 | #include "port.h" | |
42 | ||
784cdc68 | 43 | static |
834e9996 | 44 | void destroy_connection(struct bt_object *obj) |
784cdc68 JG |
45 | { |
46 | struct bt_connection *connection = container_of(obj, | |
47 | struct bt_connection, base); | |
bd14d768 | 48 | |
a684a357 | 49 | BT_LIB_LOGI("Destroying connection: %!+x", connection); |
a36bfb16 | 50 | |
bd14d768 | 51 | /* |
904860cb PP |
52 | * Make sure that each message iterator which was created for |
53 | * this connection is finalized before we destroy it. Once a | |
d73bb381 | 54 | * message iterator is finalized, you cannot use it. |
bd14d768 PP |
55 | * |
56 | * Because connections are destroyed before components within a | |
b09a5592 | 57 | * graph, this ensures that message iterators are always |
bd14d768 | 58 | * finalized before their upstream component. |
c42ea0af PP |
59 | * |
60 | * Ending the connection does exactly this. We pass `false` to | |
61 | * bt_connection_end() here to avoid removing this connection | |
62 | * from the graph: if we're here, we're already in the graph's | |
63 | * destructor. | |
bd14d768 | 64 | */ |
c42ea0af PP |
65 | bt_connection_end(connection, false); |
66 | g_ptr_array_free(connection->iterators, TRUE); | |
834e9996 | 67 | connection->iterators = NULL; |
784cdc68 JG |
68 | |
69 | /* | |
8138bfe1 | 70 | * No bt_object_put_ref on ports as a connection only holds _weak_ |
c42ea0af | 71 | * references to them. |
784cdc68 JG |
72 | */ |
73 | g_free(connection); | |
74 | } | |
75 | ||
f167d3c0 | 76 | static |
834e9996 | 77 | void try_remove_connection_from_graph(struct bt_connection *connection) |
f167d3c0 | 78 | { |
1d7bf349 | 79 | void *graph = (void *) bt_object_borrow_parent(&connection->base); |
f167d3c0 | 80 | |
1d7bf349 | 81 | if (connection->base.ref_count > 0 || |
f167d3c0 PP |
82 | connection->downstream_port || |
83 | connection->upstream_port || | |
84 | connection->iterators->len > 0) { | |
85 | return; | |
86 | } | |
87 | ||
88 | /* | |
89 | * At this point we know that: | |
90 | * | |
a36bfb16 | 91 | * 1. The connection is ended (ports were disconnected). |
b09a5592 | 92 | * 2. All the message iterators that this connection |
f167d3c0 PP |
93 | * created, if any, are finalized. |
94 | * 3. The connection's reference count is 0, so only the | |
95 | * parent (graph) owns this connection after this call. | |
96 | * | |
97 | * In other words, no other object than the graph knows this | |
98 | * connection. | |
99 | * | |
100 | * It is safe to remove the connection from the graph, therefore | |
101 | * destroying it. | |
102 | */ | |
834e9996 PP |
103 | BT_LIB_LOGD("Removing self from graph's connections: " |
104 | "%![graph-]+g, %![conn-]+x", graph, connection); | |
f167d3c0 PP |
105 | bt_graph_remove_connection(graph, connection); |
106 | } | |
107 | ||
108 | static | |
834e9996 | 109 | void parent_is_owner(struct bt_object *obj) |
f167d3c0 PP |
110 | { |
111 | struct bt_connection *connection = container_of(obj, | |
112 | struct bt_connection, base); | |
113 | ||
834e9996 | 114 | try_remove_connection_from_graph(connection); |
890882ef PP |
115 | } |
116 | ||
784cdc68 | 117 | BT_HIDDEN |
834e9996 | 118 | struct bt_connection *bt_connection_create(struct bt_graph *graph, |
72b913fb PP |
119 | struct bt_port *upstream_port, |
120 | struct bt_port *downstream_port) | |
784cdc68 JG |
121 | { |
122 | struct bt_connection *connection = NULL; | |
123 | ||
a684a357 | 124 | BT_LIB_LOGI("Creating connection: " |
834e9996 PP |
125 | "%![graph-]+g, %![up-port-]+p, %![down-port-]+p", |
126 | graph, upstream_port, downstream_port); | |
784cdc68 JG |
127 | connection = g_new0(struct bt_connection, 1); |
128 | if (!connection) { | |
a8f90e5d | 129 | BT_LIB_LOGE_APPEND_CAUSE("Failed to allocate one connection."); |
784cdc68 JG |
130 | goto end; |
131 | } | |
132 | ||
1d7bf349 | 133 | bt_object_init_shared_with_parent(&connection->base, |
834e9996 | 134 | destroy_connection); |
1d7bf349 | 135 | bt_object_set_parent_is_owner_listener_func(&connection->base, |
834e9996 | 136 | parent_is_owner); |
bd14d768 PP |
137 | connection->iterators = g_ptr_array_new(); |
138 | if (!connection->iterators) { | |
a8f90e5d | 139 | BT_LIB_LOGE_APPEND_CAUSE("Failed to allocate a GPtrArray."); |
8138bfe1 | 140 | BT_OBJECT_PUT_REF_AND_RESET(connection); |
bd14d768 PP |
141 | goto end; |
142 | } | |
143 | ||
784cdc68 | 144 | /* Weak references are taken, see comment in header. */ |
72b913fb PP |
145 | connection->upstream_port = upstream_port; |
146 | connection->downstream_port = downstream_port; | |
834e9996 | 147 | BT_LIB_LOGD("Setting upstream port's connection: %!+p", upstream_port); |
72b913fb | 148 | bt_port_set_connection(upstream_port, connection); |
834e9996 PP |
149 | BT_LIB_LOGD("Setting downstream port's connection: %!+p", |
150 | downstream_port); | |
72b913fb | 151 | bt_port_set_connection(downstream_port, connection); |
1d7bf349 | 152 | bt_object_set_parent(&connection->base, &graph->base); |
a684a357 | 153 | BT_LIB_LOGI("Created connection: %!+x", connection); |
a36bfb16 | 154 | |
784cdc68 JG |
155 | end: |
156 | return connection; | |
157 | } | |
158 | ||
72b913fb | 159 | BT_HIDDEN |
834e9996 | 160 | void bt_connection_end(struct bt_connection *conn, bool try_remove_from_graph) |
72b913fb | 161 | { |
72b913fb PP |
162 | struct bt_port *downstream_port = conn->downstream_port; |
163 | struct bt_port *upstream_port = conn->upstream_port; | |
f167d3c0 | 164 | size_t i; |
72b913fb | 165 | |
a684a357 | 166 | BT_LIB_LOGI("Ending connection: %!+x, try-remove-from-graph=%d", |
c42ea0af PP |
167 | conn, try_remove_from_graph); |
168 | ||
834e9996 | 169 | /* |
b09a5592 | 170 | * Any of the following message callback functions could |
834e9996 PP |
171 | * remove one of the connection's ports from its component. To |
172 | * make sure that at least logging in called functions works | |
173 | * with existing objects, get a local reference on both ports. | |
174 | */ | |
175 | bt_object_get_ref(downstream_port); | |
176 | bt_object_get_ref(upstream_port); | |
177 | ||
72b913fb | 178 | if (downstream_port) { |
834e9996 PP |
179 | BT_LIB_LOGD("Disconnecting connection's downstream port: %!+p", |
180 | downstream_port); | |
72b913fb PP |
181 | bt_port_set_connection(downstream_port, NULL); |
182 | conn->downstream_port = NULL; | |
183 | } | |
184 | ||
185 | if (upstream_port) { | |
834e9996 PP |
186 | BT_LIB_LOGD("Disconnecting connection's upstream port: %!+p", |
187 | upstream_port); | |
72b913fb PP |
188 | bt_port_set_connection(upstream_port, NULL); |
189 | conn->upstream_port = NULL; | |
190 | } | |
191 | ||
834e9996 PP |
192 | /* |
193 | * It is safe to put the local port references now that we don't | |
194 | * need them anymore. This could indeed destroy them. | |
195 | */ | |
196 | bt_object_put_ref(downstream_port); | |
197 | bt_object_put_ref(upstream_port); | |
f167d3c0 PP |
198 | |
199 | /* | |
904860cb PP |
200 | * Because this connection is ended, finalize each message |
201 | * iterator created from it. | |
202 | * | |
203 | * In practice, this only happens when the connection is | |
204 | * destroyed and not all its message iterators were finalized, | |
205 | * which is on graph destruction. | |
f167d3c0 PP |
206 | */ |
207 | for (i = 0; i < conn->iterators->len; i++) { | |
fbd8a4e0 | 208 | struct bt_message_iterator *iterator = |
f167d3c0 PP |
209 | g_ptr_array_index(conn->iterators, i); |
210 | ||
b09a5592 | 211 | BT_LIB_LOGD("Finalizing message iterator created by " |
834e9996 | 212 | "this ended connection: %![iter-]+i", iterator); |
fbd8a4e0 | 213 | bt_message_iterator_try_finalize( |
834e9996 | 214 | iterator); |
f167d3c0 PP |
215 | |
216 | /* | |
217 | * Make sure this iterator does not try to remove itself | |
218 | * from this connection's iterators on destruction | |
219 | * because this connection won't exist anymore. | |
220 | */ | |
fbd8a4e0 | 221 | bt_message_iterator_set_connection( |
fe7265b5 | 222 | iterator, NULL); |
f167d3c0 PP |
223 | } |
224 | ||
225 | g_ptr_array_set_size(conn->iterators, 0); | |
c42ea0af PP |
226 | |
227 | if (try_remove_from_graph) { | |
834e9996 | 228 | try_remove_connection_from_graph(conn); |
c42ea0af | 229 | } |
72b913fb PP |
230 | } |
231 | ||
7b53201c PP |
232 | const struct bt_port_output *bt_connection_borrow_upstream_port_const( |
233 | const struct bt_connection *connection) | |
784cdc68 | 234 | { |
fa6cfec3 | 235 | BT_ASSERT_PRE_DEV_NON_NULL(connection, "Connection"); |
834e9996 | 236 | return (void *) connection->upstream_port; |
784cdc68 JG |
237 | } |
238 | ||
7b53201c PP |
239 | const struct bt_port_input *bt_connection_borrow_downstream_port_const( |
240 | const struct bt_connection *connection) | |
784cdc68 | 241 | { |
fa6cfec3 | 242 | BT_ASSERT_PRE_DEV_NON_NULL(connection, "Connection"); |
834e9996 | 243 | return (void *) connection->downstream_port; |
784cdc68 | 244 | } |
bd14d768 PP |
245 | |
246 | BT_HIDDEN | |
247 | void bt_connection_remove_iterator(struct bt_connection *conn, | |
fbd8a4e0 | 248 | struct bt_message_iterator *iterator) |
bd14d768 PP |
249 | { |
250 | g_ptr_array_remove(conn->iterators, iterator); | |
a684a357 | 251 | BT_LIB_LOGD("Removed message iterator from connection: " |
834e9996 PP |
252 | "%![conn-]+x, %![iter-]+i", conn, iterator); |
253 | try_remove_connection_from_graph(conn); | |
bd14d768 | 254 | } |
090a4c0a | 255 | |
8c6884d9 PP |
256 | void bt_connection_get_ref(const struct bt_connection *connection) |
257 | { | |
258 | bt_object_get_ref(connection); | |
259 | } | |
260 | ||
261 | void bt_connection_put_ref(const struct bt_connection *connection) | |
262 | { | |
263 | bt_object_put_ref(connection); | |
264 | } |