Commit | Line | Data |
---|---|---|
81447b5b PP |
1 | /* |
2 | * The MIT License (MIT) | |
3 | * | |
4 | * Copyright (c) 2017 Philippe Proulx <pproulx@efficios.com> | |
5 | * | |
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy | |
7 | * of this software and associated documentation files (the "Software"), to deal | |
8 | * in the Software without restriction, including without limitation the rights | |
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
10 | * copies of the Software, and to permit persons to whom the Software is | |
11 | * furnished to do so, subject to the following conditions: | |
12 | * | |
13 | * The above copyright notice and this permission notice shall be included in | |
14 | * all copies or substantial portions of the Software. | |
15 | * | |
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
22 | * THE SOFTWARE. | |
23 | */ | |
24 | ||
81447b5b PP |
25 | /* Types */ |
26 | struct bt_component_class; | |
27 | ||
28 | /* Status */ | |
29 | enum bt_component_class_type { | |
f6a5e476 PP |
30 | BT_COMPONENT_CLASS_TYPE_UNKNOWN = -1, |
31 | BT_COMPONENT_CLASS_TYPE_SOURCE = 0, | |
32 | BT_COMPONENT_CLASS_TYPE_SINK = 1, | |
33 | BT_COMPONENT_CLASS_TYPE_FILTER = 2, | |
81447b5b PP |
34 | }; |
35 | ||
36 | /* General functions */ | |
37 | const char *bt_component_class_get_name( | |
38 | struct bt_component_class *component_class); | |
39 | const char *bt_component_class_get_description( | |
40 | struct bt_component_class *component_class); | |
40910fbb PP |
41 | const char *bt_component_class_get_help( |
42 | struct bt_component_class *component_class); | |
a67681c1 | 43 | struct bt_value *bt_component_class_query( |
911dec08 | 44 | struct bt_component_class *component_class, |
f6a5e476 | 45 | const char *object, struct bt_value *params); |
81447b5b PP |
46 | enum bt_component_class_type bt_component_class_get_type( |
47 | struct bt_component_class *component_class); | |
48 | ||
49 | %{ | |
50 | /* | |
51 | * This hash table associates a BT component class object address to a | |
52 | * user-defined Python class (PyObject *). The keys and values are NOT | |
53 | * owned by this hash table. The Python class objects are owned by the | |
54 | * Python module, which should not be unloaded until it is not possible | |
55 | * to create a user Python component anyway. | |
56 | * | |
57 | * This hash table is written to when a user-defined Python component | |
58 | * class is created by one of the bt_py3_component_class_*_create() | |
59 | * functions. | |
60 | * | |
61 | * This function is read from when a user calls bt_component_create() | |
62 | * with a component class pointer created by one of the functions above. | |
63 | * In this case, the original Python class needs to be found to | |
64 | * instantiate it and associate the created Python component object with | |
65 | * a BT component object instance. | |
66 | */ | |
67 | ||
68 | static GHashTable *bt_cc_ptr_to_py_cls; | |
69 | ||
70 | static void register_cc_ptr_to_py_cls(struct bt_component_class *bt_cc, | |
71 | PyObject *py_cls) | |
72 | { | |
f6a5e476 PP |
73 | if (!bt_cc_ptr_to_py_cls) { |
74 | /* | |
75 | * Lazy-initializing this GHashTable because GLib | |
76 | * might not be initialized yet and it needs to be | |
77 | * before we call g_hash_table_new() | |
78 | */ | |
79 | BT_LOGD_STR("Creating native component class to Python component class hash table."); | |
80 | bt_cc_ptr_to_py_cls = g_hash_table_new(g_direct_hash, g_direct_equal); | |
81 | assert(bt_cc_ptr_to_py_cls); | |
82 | } | |
83 | ||
81447b5b PP |
84 | g_hash_table_insert(bt_cc_ptr_to_py_cls, (gpointer) bt_cc, |
85 | (gpointer) py_cls); | |
86 | } | |
87 | ||
88 | static PyObject *lookup_cc_ptr_to_py_cls(struct bt_component_class *bt_cc) | |
89 | { | |
f6a5e476 PP |
90 | if (!bt_cc_ptr_to_py_cls) { |
91 | BT_LOGW("Cannot look up Python component class because hash table is NULL: " | |
92 | "comp-cls-addr=%p", bt_cc); | |
93 | return NULL; | |
94 | } | |
95 | ||
81447b5b PP |
96 | return (PyObject *) g_hash_table_lookup(bt_cc_ptr_to_py_cls, |
97 | (gconstpointer) bt_cc); | |
98 | } | |
99 | ||
100 | ||
101 | /* | |
102 | * Useful Python objects. | |
103 | */ | |
104 | ||
105 | static PyObject *py_mod_bt2 = NULL; | |
106 | static PyObject *py_mod_bt2_exc_error_type = NULL; | |
107 | static PyObject *py_mod_bt2_exc_unsupported_feature_type = NULL; | |
108 | static PyObject *py_mod_bt2_exc_try_again_type = NULL; | |
109 | static PyObject *py_mod_bt2_exc_stop_type = NULL; | |
f6a5e476 PP |
110 | static PyObject *py_mod_bt2_exc_port_connection_refused_type = NULL; |
111 | static PyObject *py_mod_bt2_exc_graph_canceled_type = NULL; | |
112 | static PyObject *py_mod_bt2_exc_notif_iter_canceled_type = NULL; | |
113 | static PyObject *py_mod_bt2_exc_connection_ended_type = NULL; | |
81447b5b PP |
114 | |
115 | static void bt_py3_cc_init_from_bt2(void) | |
116 | { | |
117 | /* | |
118 | * This is called once the bt2 package is loaded. | |
119 | * | |
120 | * Those modules and functions are needed while the package is | |
121 | * used. Loading them here is safe because we know the bt2 | |
122 | * package is imported, and we know that the user cannot use the | |
123 | * code here without importing bt2 first. | |
124 | */ | |
125 | py_mod_bt2 = PyImport_ImportModule("bt2"); | |
126 | assert(py_mod_bt2); | |
81447b5b PP |
127 | py_mod_bt2_exc_error_type = |
128 | PyObject_GetAttrString(py_mod_bt2, "Error"); | |
129 | assert(py_mod_bt2_exc_error_type); | |
130 | py_mod_bt2_exc_unsupported_feature_type = | |
131 | PyObject_GetAttrString(py_mod_bt2, "UnsupportedFeature"); | |
132 | py_mod_bt2_exc_try_again_type = | |
133 | PyObject_GetAttrString(py_mod_bt2, "TryAgain"); | |
134 | py_mod_bt2_exc_stop_type = | |
135 | PyObject_GetAttrString(py_mod_bt2, "Stop"); | |
f6a5e476 PP |
136 | py_mod_bt2_exc_port_connection_refused_type = |
137 | PyObject_GetAttrString(py_mod_bt2, "PortConnectionRefused"); | |
138 | py_mod_bt2_exc_graph_canceled_type = | |
139 | PyObject_GetAttrString(py_mod_bt2, "GraphCanceled"); | |
140 | py_mod_bt2_exc_connection_ended_type = | |
141 | PyObject_GetAttrString(py_mod_bt2, "ConnectionEnded"); | |
81447b5b PP |
142 | assert(py_mod_bt2_exc_stop_type); |
143 | } | |
144 | ||
145 | static void bt_py3_cc_exit_handler(void) | |
146 | { | |
147 | /* | |
148 | * This is an exit handler (set by the bt2 package). | |
149 | * | |
150 | * We only give back the references that we took in | |
151 | * bt_py3_cc_init_from_bt2() here. The global variables continue | |
152 | * to exist for the code of this file, but they are now borrowed | |
153 | * references. If this code is executed, it means that somehow | |
f6a5e476 PP |
154 | * the modules are still loaded, so it should be safe to use |
155 | * them even without a strong reference. | |
81447b5b PP |
156 | * |
157 | * We cannot do this in the library's destructor because it | |
158 | * gets executed once Python is already finalized. | |
159 | */ | |
81447b5b PP |
160 | Py_XDECREF(py_mod_bt2); |
161 | Py_XDECREF(py_mod_bt2_exc_error_type); | |
162 | Py_XDECREF(py_mod_bt2_exc_unsupported_feature_type); | |
163 | Py_XDECREF(py_mod_bt2_exc_try_again_type); | |
164 | Py_XDECREF(py_mod_bt2_exc_stop_type); | |
f6a5e476 PP |
165 | Py_XDECREF(py_mod_bt2_exc_port_connection_refused_type); |
166 | Py_XDECREF(py_mod_bt2_exc_graph_canceled_type); | |
167 | Py_XDECREF(py_mod_bt2_exc_notif_iter_canceled_type); | |
168 | Py_XDECREF(py_mod_bt2_exc_connection_ended_type); | |
81447b5b PP |
169 | } |
170 | ||
171 | ||
f6a5e476 | 172 | /* Library destructor */ |
81447b5b PP |
173 | |
174 | __attribute__((destructor)) | |
175 | static void bt_py3_native_comp_class_dtor(void) { | |
176 | /* Destroy component class association hash table */ | |
177 | if (bt_cc_ptr_to_py_cls) { | |
f6a5e476 | 178 | BT_LOGD_STR("Destroying native component class to Python component class hash table."); |
81447b5b PP |
179 | g_hash_table_destroy(bt_cc_ptr_to_py_cls); |
180 | } | |
181 | } | |
182 | ||
183 | ||
f6a5e476 | 184 | /* Component class proxy methods (delegate to the attached Python object) */ |
81447b5b | 185 | |
f6a5e476 | 186 | static enum bt_notification_iterator_status bt_py3_exc_to_notif_iter_status(void) |
81447b5b | 187 | { |
f6a5e476 PP |
188 | enum bt_notification_iterator_status status = |
189 | BT_NOTIFICATION_ITERATOR_STATUS_OK; | |
190 | PyObject *exc = PyErr_Occurred(); | |
81447b5b | 191 | |
f6a5e476 | 192 | if (!exc) { |
81447b5b PP |
193 | goto end; |
194 | } | |
195 | ||
f6a5e476 PP |
196 | if (PyErr_GivenExceptionMatches(exc, |
197 | py_mod_bt2_exc_unsupported_feature_type)) { | |
198 | status = BT_NOTIFICATION_ITERATOR_STATUS_UNSUPPORTED; | |
199 | } else if (PyErr_GivenExceptionMatches(exc, | |
200 | py_mod_bt2_exc_stop_type)) { | |
201 | status = BT_NOTIFICATION_ITERATOR_STATUS_END; | |
202 | } else if (PyErr_GivenExceptionMatches(exc, | |
203 | py_mod_bt2_exc_try_again_type)) { | |
204 | status = BT_NOTIFICATION_ITERATOR_STATUS_AGAIN; | |
205 | } else { | |
206 | status = BT_NOTIFICATION_ITERATOR_STATUS_ERROR; | |
207 | } | |
81447b5b PP |
208 | |
209 | end: | |
81447b5b | 210 | PyErr_Clear(); |
f6a5e476 | 211 | return status; |
81447b5b PP |
212 | } |
213 | ||
f6a5e476 | 214 | static enum bt_component_status bt_py3_exc_to_component_status(void) |
81447b5b | 215 | { |
f6a5e476 PP |
216 | enum bt_component_status status = BT_COMPONENT_STATUS_OK; |
217 | PyObject *exc = PyErr_Occurred(); | |
81447b5b | 218 | |
f6a5e476 PP |
219 | if (!exc) { |
220 | goto end; | |
81447b5b PP |
221 | } |
222 | ||
f6a5e476 PP |
223 | if (PyErr_GivenExceptionMatches(exc, |
224 | py_mod_bt2_exc_unsupported_feature_type)) { | |
225 | status = BT_COMPONENT_STATUS_UNSUPPORTED; | |
226 | } else if (PyErr_GivenExceptionMatches(exc, | |
227 | py_mod_bt2_exc_try_again_type)) { | |
228 | status = BT_COMPONENT_STATUS_AGAIN; | |
229 | } else if (PyErr_GivenExceptionMatches(exc, | |
230 | py_mod_bt2_exc_stop_type)) { | |
231 | status = BT_COMPONENT_STATUS_END; | |
232 | } else if (PyErr_GivenExceptionMatches(exc, | |
233 | py_mod_bt2_exc_port_connection_refused_type)) { | |
234 | status = BT_COMPONENT_STATUS_REFUSE_PORT_CONNECTION; | |
235 | } else { | |
236 | status = BT_COMPONENT_STATUS_ERROR; | |
81447b5b PP |
237 | } |
238 | ||
f6a5e476 PP |
239 | end: |
240 | PyErr_Clear(); | |
241 | return status; | |
242 | } | |
243 | ||
244 | static enum bt_component_status bt_py3_cc_init( | |
245 | struct bt_private_component *priv_comp, | |
246 | struct bt_value *params, void *init_method_data) | |
247 | { | |
248 | struct bt_component *comp = | |
249 | bt_component_from_private_component(priv_comp); | |
250 | struct bt_component_class *comp_cls = bt_component_get_class(comp); | |
251 | enum bt_component_status status = BT_COMPONENT_STATUS_OK; | |
252 | PyObject *py_cls = NULL; | |
253 | PyObject *py_comp = NULL; | |
254 | PyObject *py_params_ptr = NULL; | |
255 | PyObject *py_comp_ptr = NULL; | |
256 | ||
257 | (void) init_method_data; | |
258 | assert(comp); | |
259 | assert(comp_cls); | |
260 | ||
261 | /* | |
262 | * Get the user-defined Python class which created this | |
263 | * component's class in the first place (borrowed | |
264 | * reference). | |
265 | */ | |
266 | py_cls = lookup_cc_ptr_to_py_cls(comp_cls); | |
267 | if (!py_cls) { | |
268 | BT_LOGE("Cannot find Python class associated to native component class: " | |
269 | "comp-cls-addr=%p", comp_cls); | |
81447b5b PP |
270 | goto error; |
271 | } | |
272 | ||
f6a5e476 PP |
273 | /* Parameters pointer -> SWIG pointer Python object */ |
274 | py_params_ptr = SWIG_NewPointerObj(SWIG_as_voidptr(params), | |
275 | SWIGTYPE_p_bt_value, 0); | |
276 | if (!py_params_ptr) { | |
277 | BT_LOGE_STR("Failed to create a SWIG pointer object."); | |
81447b5b PP |
278 | goto error; |
279 | } | |
280 | ||
f6a5e476 PP |
281 | /* Private component pointer -> SWIG pointer Python object */ |
282 | py_comp_ptr = SWIG_NewPointerObj(SWIG_as_voidptr(priv_comp), | |
283 | SWIGTYPE_p_bt_private_component, 0); | |
284 | if (!py_comp_ptr) { | |
285 | BT_LOGE_STR("Failed to create a SWIG pointer object."); | |
286 | goto error; | |
81447b5b PP |
287 | } |
288 | ||
f6a5e476 PP |
289 | /* |
290 | * Do the equivalent of this: | |
291 | * | |
292 | * py_comp = py_cls._init_from_native(py_comp_ptr, py_params_ptr) | |
293 | * | |
294 | * _UserComponentType._init_from_native() calls the Python | |
295 | * component object's __init__() function. | |
296 | */ | |
297 | py_comp = PyObject_CallMethod(py_cls, | |
298 | "_init_from_native", "(OO)", py_comp_ptr, py_params_ptr); | |
299 | if (!py_comp) { | |
300 | BT_LOGE("Failed to call Python class's _init_from_native() method: " | |
301 | "py-cls-addr=%p", py_cls); | |
81447b5b PP |
302 | goto error; |
303 | } | |
304 | ||
f6a5e476 PP |
305 | /* |
306 | * Our user Python component object is now fully created and | |
307 | * initialized by the user. Since we just created it, this | |
308 | * native component is its only (persistent) owner. | |
309 | */ | |
310 | bt_private_component_set_user_data(priv_comp, py_comp); | |
311 | py_comp = NULL; | |
81447b5b PP |
312 | goto end; |
313 | ||
314 | error: | |
f6a5e476 | 315 | status = BT_COMPONENT_STATUS_ERROR; |
81447b5b PP |
316 | |
317 | /* | |
f6a5e476 PP |
318 | * Clear any exception: we're returning a bad status anyway. If |
319 | * this call originated from Python (creation from a plugin's | |
320 | * component class, for example), then the user gets an | |
321 | * appropriate creation error. | |
81447b5b | 322 | */ |
f6a5e476 | 323 | PyErr_Clear(); |
81447b5b | 324 | |
f6a5e476 PP |
325 | end: |
326 | bt_put(comp_cls); | |
327 | bt_put(comp); | |
328 | Py_XDECREF(py_comp); | |
329 | Py_XDECREF(py_params_ptr); | |
330 | Py_XDECREF(py_comp_ptr); | |
331 | return status; | |
332 | } | |
81447b5b | 333 | |
f6a5e476 PP |
334 | static void bt_py3_cc_finalize(struct bt_private_component *priv_comp) |
335 | { | |
336 | PyObject *py_comp = bt_private_component_get_user_data(priv_comp); | |
337 | PyObject *py_method_result = NULL; | |
81447b5b | 338 | |
f6a5e476 | 339 | assert(py_comp); |
81447b5b | 340 | |
f6a5e476 PP |
341 | /* Call user's _finalize() method */ |
342 | py_method_result = PyObject_CallMethod(py_comp, | |
343 | "_finalize", NULL); | |
81447b5b | 344 | |
f6a5e476 PP |
345 | if (PyErr_Occurred()) { |
346 | BT_LOGW("User's _finalize() method raised an exception: ignoring."); | |
347 | } | |
81447b5b | 348 | |
f6a5e476 PP |
349 | /* |
350 | * Ignore any exception raised by the _finalize() method because | |
351 | * it won't change anything at this point: the component is | |
352 | * being destroyed anyway. | |
353 | */ | |
354 | PyErr_Clear(); | |
355 | Py_XDECREF(py_method_result); | |
356 | Py_DECREF(py_comp); | |
357 | } | |
81447b5b | 358 | |
f6a5e476 PP |
359 | static enum bt_component_status bt_py3_cc_accept_port_connection( |
360 | struct bt_private_component *priv_comp, | |
361 | struct bt_private_port *self_priv_port, | |
362 | struct bt_port *other_port) | |
363 | { | |
364 | enum bt_component_status status; | |
365 | PyObject *py_comp = NULL; | |
366 | PyObject *py_self_port_ptr = NULL; | |
367 | PyObject *py_other_port_ptr = NULL; | |
368 | PyObject *py_method_result = NULL; | |
81447b5b | 369 | |
f6a5e476 PP |
370 | py_comp = bt_private_component_get_user_data(priv_comp); |
371 | assert(py_comp); | |
372 | py_self_port_ptr = SWIG_NewPointerObj(SWIG_as_voidptr(self_priv_port), | |
373 | SWIGTYPE_p_bt_private_port, 0); | |
374 | if (!py_self_port_ptr) { | |
375 | BT_LOGE_STR("Failed to create a SWIG pointer object."); | |
376 | goto error; | |
377 | } | |
81447b5b | 378 | |
f6a5e476 PP |
379 | py_other_port_ptr = SWIG_NewPointerObj(SWIG_as_voidptr(other_port), |
380 | SWIGTYPE_p_bt_port, 0); | |
381 | if (!py_other_port_ptr) { | |
382 | BT_LOGE_STR("Failed to create a SWIG pointer object."); | |
383 | goto error; | |
384 | } | |
81447b5b | 385 | |
f6a5e476 PP |
386 | py_method_result = PyObject_CallMethod(py_comp, |
387 | "_accept_port_connection_from_native", "(OO)", py_self_port_ptr, | |
388 | py_other_port_ptr); | |
389 | status = bt_py3_exc_to_component_status(); | |
390 | if (!py_method_result && status == BT_COMPONENT_STATUS_OK) { | |
391 | /* Pretty sure this should never happen, but just in case */ | |
392 | BT_LOGE("User's _accept_port_connection() method failed without raising an exception: " | |
393 | "status=%d", status); | |
394 | goto error; | |
395 | } | |
81447b5b | 396 | |
f6a5e476 | 397 | if (status == BT_COMPONENT_STATUS_REFUSE_PORT_CONNECTION) { |
81447b5b | 398 | /* |
f6a5e476 PP |
399 | * Looks like the user method raised |
400 | * PortConnectionRefused: accept this like if it | |
401 | * returned False. | |
81447b5b | 402 | */ |
f6a5e476 PP |
403 | goto end; |
404 | } else if (status != BT_COMPONENT_STATUS_OK) { | |
405 | BT_LOGE("User's _accept_port_connection() raised an unexpected exception: " | |
406 | "status=%d", status); | |
407 | goto error; | |
408 | } | |
81447b5b | 409 | |
f6a5e476 | 410 | assert(PyBool_Check(py_method_result)); |
81447b5b | 411 | |
f6a5e476 PP |
412 | if (py_method_result == Py_True) { |
413 | status = BT_COMPONENT_STATUS_OK; | |
81447b5b | 414 | } else { |
f6a5e476 PP |
415 | status = BT_COMPONENT_STATUS_REFUSE_PORT_CONNECTION; |
416 | } | |
81447b5b | 417 | |
f6a5e476 | 418 | goto end; |
81447b5b | 419 | |
f6a5e476 PP |
420 | error: |
421 | status = BT_COMPONENT_STATUS_ERROR; | |
81447b5b | 422 | |
f6a5e476 PP |
423 | /* |
424 | * Clear any exception: we're returning a bad status anyway. If | |
425 | * this call originated from Python, then the user gets an | |
426 | * appropriate error. | |
427 | */ | |
428 | PyErr_Clear(); | |
81447b5b | 429 | |
f6a5e476 PP |
430 | end: |
431 | Py_XDECREF(py_self_port_ptr); | |
432 | Py_XDECREF(py_other_port_ptr); | |
433 | Py_XDECREF(py_method_result); | |
81447b5b PP |
434 | return status; |
435 | } | |
436 | ||
f6a5e476 PP |
437 | static void bt_py3_cc_port_connected( |
438 | struct bt_private_component *priv_comp, | |
439 | struct bt_private_port *self_priv_port, | |
440 | struct bt_port *other_port) | |
81447b5b | 441 | { |
f6a5e476 PP |
442 | PyObject *py_comp = NULL; |
443 | PyObject *py_self_port_ptr = NULL; | |
444 | PyObject *py_other_port_ptr = NULL; | |
445 | PyObject *py_method_result = NULL; | |
81447b5b | 446 | |
f6a5e476 PP |
447 | py_comp = bt_private_component_get_user_data(priv_comp); |
448 | assert(py_comp); | |
449 | py_self_port_ptr = SWIG_NewPointerObj(SWIG_as_voidptr(self_priv_port), | |
450 | SWIGTYPE_p_bt_private_port, 0); | |
451 | if (!py_self_port_ptr) { | |
452 | BT_LOGF_STR("Failed to create a SWIG pointer object."); | |
453 | abort(); | |
454 | } | |
455 | ||
456 | py_other_port_ptr = SWIG_NewPointerObj(SWIG_as_voidptr(other_port), | |
457 | SWIGTYPE_p_bt_port, 0); | |
458 | if (!py_other_port_ptr) { | |
459 | BT_LOGF_STR("Failed to create a SWIG pointer object."); | |
460 | abort(); | |
461 | } | |
462 | ||
463 | py_method_result = PyObject_CallMethod(py_comp, | |
464 | "_port_connected_from_native", "(OO)", py_self_port_ptr, | |
465 | py_other_port_ptr); | |
466 | assert(py_method_result == Py_None); | |
467 | Py_XDECREF(py_self_port_ptr); | |
468 | Py_XDECREF(py_other_port_ptr); | |
469 | Py_XDECREF(py_method_result); | |
470 | } | |
81447b5b | 471 | |
f6a5e476 PP |
472 | static void bt_py3_cc_port_disconnected( |
473 | struct bt_private_component *priv_comp, | |
474 | struct bt_private_port *priv_port) | |
475 | { | |
476 | PyObject *py_comp = NULL; | |
477 | PyObject *py_port_ptr = NULL; | |
478 | PyObject *py_method_result = NULL; | |
479 | ||
480 | py_comp = bt_private_component_get_user_data(priv_comp); | |
481 | assert(py_comp); | |
482 | py_port_ptr = SWIG_NewPointerObj(SWIG_as_voidptr(priv_port), | |
483 | SWIGTYPE_p_bt_private_port, 0); | |
484 | if (!py_port_ptr) { | |
485 | BT_LOGF_STR("Failed to create a SWIG pointer object."); | |
486 | abort(); | |
487 | } | |
488 | ||
489 | py_method_result = PyObject_CallMethod(py_comp, | |
490 | "_port_disconnected_from_native", "(O)", py_port_ptr); | |
491 | assert(py_method_result == Py_None); | |
492 | Py_XDECREF(py_port_ptr); | |
493 | Py_XDECREF(py_method_result); | |
81447b5b PP |
494 | } |
495 | ||
a67681c1 | 496 | static struct bt_value *bt_py3_cc_query( |
f6a5e476 | 497 | struct bt_component_class *comp_cls, |
a67681c1 | 498 | const char *object, struct bt_value *params) |
911dec08 PP |
499 | { |
500 | PyObject *py_cls = NULL; | |
f6a5e476 | 501 | PyObject *py_params_ptr = NULL; |
a67681c1 PP |
502 | PyObject *py_query_func = NULL; |
503 | PyObject *py_object = NULL; | |
911dec08 PP |
504 | PyObject *py_results_addr = NULL; |
505 | struct bt_value *results = NULL; | |
506 | ||
f6a5e476 | 507 | py_cls = lookup_cc_ptr_to_py_cls(comp_cls); |
911dec08 | 508 | if (!py_cls) { |
f6a5e476 PP |
509 | BT_LOGE("Cannot find Python class associated to native component class: " |
510 | "comp-cls-addr=%p", comp_cls); | |
911dec08 PP |
511 | goto error; |
512 | } | |
513 | ||
f6a5e476 PP |
514 | py_params_ptr = SWIG_NewPointerObj(SWIG_as_voidptr(params), |
515 | SWIGTYPE_p_bt_value, 0); | |
516 | if (!py_params_ptr) { | |
517 | BT_LOGE_STR("Failed to create a SWIG pointer object."); | |
911dec08 PP |
518 | goto error; |
519 | } | |
520 | ||
a67681c1 PP |
521 | py_object = SWIG_FromCharPtr(object); |
522 | if (!py_object) { | |
f6a5e476 | 523 | BT_LOGE_STR("Failed to create a Python string."); |
911dec08 PP |
524 | goto error; |
525 | } | |
526 | ||
527 | py_results_addr = PyObject_CallMethod(py_cls, | |
f6a5e476 PP |
528 | "_query_from_native", "(OO)", py_object, py_params_ptr); |
529 | if (!py_results_addr || py_results_addr == Py_None) { | |
530 | BT_LOGE_STR("User's _query() method failed."); | |
911dec08 PP |
531 | goto error; |
532 | } | |
533 | ||
f6a5e476 PP |
534 | if (py_results_addr == Py_NotImplemented) { |
535 | BT_LOGE_STR("User's _query() method is not implemented."); | |
911dec08 PP |
536 | goto error; |
537 | } | |
538 | ||
f6a5e476 PP |
539 | /* |
540 | * The returned object, on success, is an integer object | |
541 | * (PyLong) containing the address of a BT value object (new | |
542 | * reference). | |
543 | */ | |
544 | results = (void *) PyLong_AsUnsignedLongLong(py_results_addr); | |
545 | assert(!PyErr_Occurred()); | |
546 | assert(results); | |
911dec08 PP |
547 | goto end; |
548 | ||
549 | error: | |
550 | BT_PUT(results); | |
551 | PyErr_Clear(); | |
552 | ||
553 | end: | |
f6a5e476 | 554 | Py_XDECREF(py_params_ptr); |
a67681c1 PP |
555 | Py_XDECREF(py_query_func); |
556 | Py_XDECREF(py_object); | |
911dec08 PP |
557 | Py_XDECREF(py_results_addr); |
558 | return results; | |
559 | } | |
560 | ||
81447b5b | 561 | static enum bt_notification_iterator_status bt_py3_cc_notification_iterator_init( |
f6a5e476 PP |
562 | struct bt_private_notification_iterator *priv_notif_iter, |
563 | struct bt_private_port *priv_port) | |
81447b5b PP |
564 | { |
565 | enum bt_notification_iterator_status status = | |
566 | BT_NOTIFICATION_ITERATOR_STATUS_OK; | |
567 | PyObject *py_comp_cls = NULL; | |
568 | PyObject *py_iter_cls = NULL; | |
569 | PyObject *py_iter_ptr = NULL; | |
570 | PyObject *py_init_method_result = NULL; | |
571 | PyObject *py_iter = NULL; | |
f6a5e476 PP |
572 | struct bt_private_component *priv_comp = |
573 | bt_private_notification_iterator_get_private_component( | |
574 | priv_notif_iter); | |
575 | PyObject *py_comp; | |
576 | ||
577 | assert(priv_comp); | |
578 | py_comp = bt_private_component_get_user_data(priv_comp); | |
81447b5b PP |
579 | |
580 | /* Find user's Python notification iterator class */ | |
581 | py_comp_cls = PyObject_GetAttrString(py_comp, "__class__"); | |
582 | if (!py_comp_cls) { | |
f6a5e476 | 583 | BT_LOGE_STR("Cannot get Python object's `__class__` attribute."); |
81447b5b PP |
584 | goto error; |
585 | } | |
586 | ||
587 | py_iter_cls = PyObject_GetAttrString(py_comp_cls, "_iter_cls"); | |
588 | if (!py_iter_cls) { | |
f6a5e476 PP |
589 | BT_LOGE_STR("Cannot get Python class's `_iter_cls` attribute."); |
590 | goto error; | |
591 | } | |
592 | ||
593 | py_iter_ptr = SWIG_NewPointerObj(SWIG_as_voidptr(priv_notif_iter), | |
594 | SWIGTYPE_p_bt_private_notification_iterator, 0); | |
595 | if (!py_iter_ptr) { | |
596 | BT_LOGE_STR("Failed to create a SWIG pointer object."); | |
81447b5b PP |
597 | goto error; |
598 | } | |
599 | ||
600 | /* | |
f6a5e476 | 601 | * Create object with borrowed native notification iterator |
81447b5b PP |
602 | * reference: |
603 | * | |
604 | * py_iter = py_iter_cls.__new__(py_iter_cls, py_iter_ptr) | |
605 | */ | |
81447b5b PP |
606 | py_iter = PyObject_CallMethod(py_iter_cls, "__new__", |
607 | "(OO)", py_iter_cls, py_iter_ptr); | |
608 | if (!py_iter) { | |
f6a5e476 PP |
609 | BT_LOGE("Failed to call Python class's __new__() method: " |
610 | "py-cls-addr=%p", py_iter_cls); | |
81447b5b PP |
611 | goto error; |
612 | } | |
613 | ||
614 | /* | |
615 | * Initialize object: | |
616 | * | |
617 | * py_iter.__init__() | |
618 | * | |
619 | * At this point, py_iter._ptr is set, so this initialization | |
f6a5e476 PP |
620 | * function has access to self._component (which gives it the |
621 | * user Python component object from which the iterator was | |
622 | * created). | |
81447b5b PP |
623 | */ |
624 | py_init_method_result = PyObject_CallMethod(py_iter, "__init__", NULL); | |
625 | if (!py_init_method_result) { | |
f6a5e476 | 626 | BT_LOGE_STR("User's __init__() method failed."); |
81447b5b PP |
627 | goto error; |
628 | } | |
629 | ||
630 | /* | |
631 | * Since the Python code can never instantiate a user-defined | |
f6a5e476 | 632 | * notification iterator class, the native notification iterator |
81447b5b PP |
633 | * object does NOT belong to a user Python notification iterator |
634 | * object (borrowed reference). However this Python object is | |
f6a5e476 | 635 | * owned by this native notification iterator object. |
81447b5b | 636 | * |
f6a5e476 | 637 | * In the Python world, the lifetime of the native notification |
81447b5b PP |
638 | * iterator is managed by a _GenericNotificationIterator |
639 | * instance: | |
640 | * | |
641 | * _GenericNotificationIterator instance: | |
642 | * owns a native bt_notification_iterator object (iter) | |
f6a5e476 | 643 | * owns a _UserNotificationIterator instance (py_iter) |
81447b5b | 644 | * self._ptr is a borrowed reference to the |
f6a5e476 PP |
645 | * native bt_private_notification_iterator |
646 | * object (iter) | |
81447b5b | 647 | */ |
f6a5e476 PP |
648 | bt_private_notification_iterator_set_user_data(priv_notif_iter, |
649 | py_iter); | |
81447b5b PP |
650 | py_iter = NULL; |
651 | goto end; | |
652 | ||
653 | error: | |
654 | status = bt_py3_exc_to_notif_iter_status(); | |
655 | if (status == BT_NOTIFICATION_ITERATOR_STATUS_OK) { | |
656 | /* | |
657 | * Looks like there wasn't any exception from the Python | |
658 | * side, but we're still in an error state here. | |
659 | */ | |
660 | status = BT_NOTIFICATION_ITERATOR_STATUS_ERROR; | |
661 | } | |
662 | ||
663 | /* | |
664 | * Clear any exception: we're returning a bad status anyway. If | |
665 | * this call originated from Python, then the user gets an | |
666 | * appropriate creation error. | |
667 | */ | |
668 | PyErr_Clear(); | |
669 | ||
670 | end: | |
f6a5e476 | 671 | bt_put(priv_comp); |
81447b5b PP |
672 | Py_XDECREF(py_comp_cls); |
673 | Py_XDECREF(py_iter_cls); | |
674 | Py_XDECREF(py_iter_ptr); | |
675 | Py_XDECREF(py_init_method_result); | |
676 | Py_XDECREF(py_iter); | |
677 | return status; | |
678 | } | |
679 | ||
f6a5e476 PP |
680 | static void bt_py3_cc_notification_iterator_finalize( |
681 | struct bt_private_notification_iterator *priv_notif_iter) | |
81447b5b PP |
682 | { |
683 | PyObject *py_notif_iter = | |
f6a5e476 PP |
684 | bt_private_notification_iterator_get_user_data(priv_notif_iter); |
685 | PyObject *py_method_result = NULL; | |
81447b5b PP |
686 | |
687 | assert(py_notif_iter); | |
f6a5e476 PP |
688 | |
689 | /* Call user's _finalize() method */ | |
690 | py_method_result = PyObject_CallMethod(py_notif_iter, | |
691 | "_finalize", NULL); | |
692 | ||
693 | if (PyErr_Occurred()) { | |
694 | BT_LOGW("User's _finalize() method raised an exception: ignoring."); | |
695 | } | |
81447b5b PP |
696 | |
697 | /* | |
f6a5e476 PP |
698 | * Ignore any exception raised by the _finalize() method because |
699 | * it won't change anything at this point: the component is | |
700 | * being destroyed anyway. | |
81447b5b PP |
701 | */ |
702 | PyErr_Clear(); | |
f6a5e476 | 703 | Py_XDECREF(py_method_result); |
81447b5b PP |
704 | Py_DECREF(py_notif_iter); |
705 | } | |
706 | ||
f6a5e476 PP |
707 | static struct bt_notification_iterator_next_return |
708 | bt_py3_cc_notification_iterator_next( | |
709 | struct bt_private_notification_iterator *priv_notif_iter) | |
81447b5b | 710 | { |
f6a5e476 PP |
711 | struct bt_notification_iterator_next_return next_ret = { |
712 | .status = BT_NOTIFICATION_ITERATOR_STATUS_OK, | |
713 | .notification = NULL, | |
714 | }; | |
81447b5b | 715 | PyObject *py_notif_iter = |
f6a5e476 PP |
716 | bt_private_notification_iterator_get_user_data(priv_notif_iter); |
717 | PyObject *py_method_result = NULL; | |
81447b5b PP |
718 | |
719 | assert(py_notif_iter); | |
f6a5e476 PP |
720 | py_method_result = PyObject_CallMethod(py_notif_iter, |
721 | "_next_from_native", NULL); | |
722 | if (!py_method_result) { | |
723 | next_ret.status = bt_py3_exc_to_notif_iter_status(); | |
724 | assert(next_ret.status != BT_NOTIFICATION_ITERATOR_STATUS_OK); | |
725 | goto end; | |
81447b5b PP |
726 | } |
727 | ||
728 | /* | |
729 | * The returned object, on success, is an integer object | |
f6a5e476 | 730 | * (PyLong) containing the address of a native notification |
911dec08 | 731 | * object (which is now ours). |
81447b5b | 732 | */ |
f6a5e476 PP |
733 | next_ret.notification = |
734 | (struct bt_notification *) PyLong_AsUnsignedLongLong( | |
735 | py_method_result); | |
81447b5b PP |
736 | |
737 | /* Clear potential overflow error; should never happen */ | |
f6a5e476 PP |
738 | assert(!PyErr_Occurred()); |
739 | assert(next_ret.notification); | |
81447b5b PP |
740 | goto end; |
741 | ||
81447b5b | 742 | end: |
f6a5e476 PP |
743 | Py_XDECREF(py_method_result); |
744 | return next_ret; | |
81447b5b PP |
745 | } |
746 | ||
747 | static enum bt_component_status bt_py3_cc_sink_consume( | |
f6a5e476 | 748 | struct bt_private_component *priv_comp) |
81447b5b | 749 | { |
f6a5e476 PP |
750 | PyObject *py_comp = bt_private_component_get_user_data(priv_comp); |
751 | PyObject *py_method_result = NULL; | |
81447b5b PP |
752 | enum bt_component_status status; |
753 | ||
f6a5e476 PP |
754 | assert(py_comp); |
755 | py_method_result = PyObject_CallMethod(py_comp, | |
81447b5b PP |
756 | "_consume", NULL); |
757 | status = bt_py3_exc_to_component_status(); | |
f6a5e476 | 758 | if (!py_method_result && status == BT_COMPONENT_STATUS_OK) { |
81447b5b | 759 | /* Pretty sure this should never happen, but just in case */ |
f6a5e476 PP |
760 | BT_LOGE("User's _consume() method failed without raising an exception: " |
761 | "status=%d", status); | |
81447b5b PP |
762 | status = BT_COMPONENT_STATUS_ERROR; |
763 | } | |
764 | ||
f6a5e476 | 765 | Py_XDECREF(py_method_result); |
81447b5b PP |
766 | return status; |
767 | } | |
768 | ||
769 | ||
770 | /* Component class creation functions (called from Python module) */ | |
771 | ||
772 | static int bt_py3_cc_set_optional_attrs_methods(struct bt_component_class *cc, | |
40910fbb | 773 | const char *description, const char *help) |
81447b5b PP |
774 | { |
775 | int ret = 0; | |
776 | ||
777 | if (description) { | |
778 | ret = bt_component_class_set_description(cc, description); | |
779 | if (ret) { | |
f6a5e476 PP |
780 | BT_LOGE("Cannot set component class's description: " |
781 | "comp-cls-addr=%p", cc); | |
81447b5b PP |
782 | goto end; |
783 | } | |
784 | } | |
785 | ||
40910fbb PP |
786 | if (help) { |
787 | ret = bt_component_class_set_help(cc, help); | |
788 | if (ret) { | |
f6a5e476 PP |
789 | BT_LOGE("Cannot set component class's help text: " |
790 | "comp-cls-addr=%p", cc); | |
40910fbb PP |
791 | goto end; |
792 | } | |
793 | } | |
794 | ||
81447b5b | 795 | ret = bt_component_class_set_init_method(cc, bt_py3_cc_init); |
f6a5e476 PP |
796 | assert(ret == 0); |
797 | ret = bt_component_class_set_finalize_method(cc, bt_py3_cc_finalize); | |
798 | assert(ret == 0); | |
799 | ret = bt_component_class_set_accept_port_connection_method(cc, | |
800 | bt_py3_cc_accept_port_connection); | |
801 | assert(ret == 0); | |
802 | ret = bt_component_class_set_port_connected_method(cc, | |
803 | bt_py3_cc_port_connected); | |
804 | assert(ret == 0); | |
805 | ret = bt_component_class_set_port_disconnected_method(cc, | |
806 | bt_py3_cc_port_disconnected); | |
807 | assert(ret == 0); | |
a67681c1 | 808 | ret = bt_component_class_set_query_method(cc, bt_py3_cc_query); |
f6a5e476 | 809 | assert(ret == 0); |
911dec08 | 810 | |
81447b5b PP |
811 | end: |
812 | return ret; | |
813 | } | |
814 | ||
f6a5e476 | 815 | static void bt_py3_cc_set_optional_iter_methods(struct bt_component_class *cc, |
81447b5b | 816 | int (*set_notif_iter_init_method)(struct bt_component_class *, bt_component_class_notification_iterator_init_method), |
f6a5e476 | 817 | int (*set_notif_iter_finalize_method)(struct bt_component_class *, bt_component_class_notification_iterator_finalize_method)) |
81447b5b | 818 | { |
dac20463 | 819 | int ret __attribute__((unused)); |
81447b5b PP |
820 | |
821 | ret = set_notif_iter_init_method( | |
822 | cc, bt_py3_cc_notification_iterator_init); | |
f6a5e476 PP |
823 | assert(ret == 0); |
824 | ret = set_notif_iter_finalize_method( | |
825 | cc, bt_py3_cc_notification_iterator_finalize); | |
826 | assert(ret == 0); | |
81447b5b PP |
827 | } |
828 | ||
829 | static struct bt_component_class *bt_py3_component_class_source_create( | |
830 | PyObject *py_cls, const char *name, const char *description, | |
f6a5e476 | 831 | const char *help) |
81447b5b PP |
832 | { |
833 | struct bt_component_class *cc; | |
834 | int ret; | |
835 | ||
836 | assert(py_cls); | |
837 | cc = bt_component_class_source_create(name, | |
81447b5b PP |
838 | bt_py3_cc_notification_iterator_next); |
839 | if (!cc) { | |
f6a5e476 | 840 | BT_LOGE_STR("Cannot create source component class."); |
81447b5b PP |
841 | goto end; |
842 | } | |
843 | ||
40910fbb | 844 | ret = bt_py3_cc_set_optional_attrs_methods(cc, description, help); |
81447b5b | 845 | if (ret) { |
f6a5e476 | 846 | BT_LOGE_STR("Cannot set source component class's optional attributes and methods."); |
81447b5b PP |
847 | BT_PUT(cc); |
848 | goto end; | |
849 | } | |
850 | ||
f6a5e476 | 851 | bt_py3_cc_set_optional_iter_methods(cc, |
81447b5b | 852 | bt_component_class_source_set_notification_iterator_init_method, |
f6a5e476 | 853 | bt_component_class_source_set_notification_iterator_finalize_method); |
81447b5b PP |
854 | register_cc_ptr_to_py_cls(cc, py_cls); |
855 | bt_component_class_freeze(cc); | |
856 | ||
857 | end: | |
858 | return cc; | |
859 | } | |
860 | ||
861 | static struct bt_component_class *bt_py3_component_class_filter_create( | |
862 | PyObject *py_cls, const char *name, const char *description, | |
f6a5e476 | 863 | const char *help) |
81447b5b PP |
864 | { |
865 | struct bt_component_class *cc; | |
866 | int ret; | |
867 | ||
868 | assert(py_cls); | |
869 | cc = bt_component_class_filter_create(name, | |
81447b5b PP |
870 | bt_py3_cc_notification_iterator_next); |
871 | if (!cc) { | |
f6a5e476 | 872 | BT_LOGE_STR("Cannot create filter component class."); |
81447b5b PP |
873 | goto end; |
874 | } | |
875 | ||
40910fbb | 876 | ret = bt_py3_cc_set_optional_attrs_methods(cc, description, help); |
81447b5b | 877 | if (ret) { |
f6a5e476 | 878 | BT_LOGE_STR("Cannot set filter component class's optional attributes and methods."); |
81447b5b PP |
879 | BT_PUT(cc); |
880 | goto end; | |
881 | } | |
882 | ||
f6a5e476 | 883 | bt_py3_cc_set_optional_iter_methods(cc, |
81447b5b | 884 | bt_component_class_filter_set_notification_iterator_init_method, |
f6a5e476 | 885 | bt_component_class_filter_set_notification_iterator_finalize_method); |
81447b5b PP |
886 | register_cc_ptr_to_py_cls(cc, py_cls); |
887 | bt_component_class_freeze(cc); | |
888 | ||
889 | end: | |
890 | return cc; | |
891 | } | |
892 | ||
893 | static struct bt_component_class *bt_py3_component_class_sink_create( | |
40910fbb PP |
894 | PyObject *py_cls, const char *name, const char *description, |
895 | const char *help) | |
81447b5b PP |
896 | { |
897 | struct bt_component_class *cc; | |
898 | int ret; | |
899 | ||
900 | assert(py_cls); | |
901 | cc = bt_component_class_sink_create(name, bt_py3_cc_sink_consume); | |
902 | if (!cc) { | |
f6a5e476 | 903 | BT_LOGE_STR("Cannot create sink component class."); |
81447b5b PP |
904 | goto end; |
905 | } | |
906 | ||
40910fbb | 907 | ret = bt_py3_cc_set_optional_attrs_methods(cc, description, help); |
81447b5b | 908 | if (ret) { |
f6a5e476 | 909 | BT_LOGE_STR("Cannot set sink component class's optional attributes and methods."); |
81447b5b PP |
910 | BT_PUT(cc); |
911 | goto end; | |
912 | } | |
913 | ||
914 | register_cc_ptr_to_py_cls(cc, py_cls); | |
915 | bt_component_class_freeze(cc); | |
916 | ||
917 | end: | |
918 | return cc; | |
919 | } | |
81447b5b PP |
920 | %} |
921 | ||
81447b5b PP |
922 | struct bt_component_class *bt_py3_component_class_source_create( |
923 | PyObject *py_cls, const char *name, const char *description, | |
f6a5e476 | 924 | const char *help); |
81447b5b PP |
925 | struct bt_component_class *bt_py3_component_class_filter_create( |
926 | PyObject *py_cls, const char *name, const char *description, | |
f6a5e476 | 927 | const char *help); |
81447b5b | 928 | struct bt_component_class *bt_py3_component_class_sink_create( |
40910fbb PP |
929 | PyObject *py_cls, const char *name, const char *description, |
930 | const char *help); | |
81447b5b PP |
931 | void bt_py3_cc_init_from_bt2(void); |
932 | void bt_py3_cc_exit_handler(void); |