node: use spa_list_for_each_safe() for pw_node_events_*

Introduce spa_hook_list_call_simple_safe() as a new helper that uses
spa_list_for_each_safe() and use it for pw_node_events_* This way multiple
threads can iterate at the same time and, if only one thread is active, the
current list entry can be safely removed (e.g. in
pw_node_events_destroy()).

Without this the node listener_list may be corrupted when the main and data
loop iterate over the list at the same time (See #143).
This commit is contained in:
Michael Olbrich 2019-05-02 15:02:59 +02:00 committed by Wim Taymans
parent 323917ab4b
commit 6b269cce35
2 changed files with 14 additions and 1 deletions

View file

@ -94,6 +94,19 @@ static inline void spa_hook_remove(struct spa_hook *hook)
} \
})
#define spa_hook_list_call_simple_safe(l,type,method,vers,...) \
({ \
struct spa_hook_list *list = l; \
struct spa_hook *ci; \
struct spa_hook *tmp; \
spa_list_for_each_safe(ci, tmp, &list->list, link) { \
const type *cb = ci->funcs; \
if (cb && cb->version >= vers && cb->method) { \
cb->method(ci->data, ## __VA_ARGS__); \
} \
} \
})
/** Call all hooks in a list, starting from the given one and optionally stopping
* after calling the first non-NULL function, returns the number of methods
* called */

View file

@ -303,7 +303,7 @@ struct pw_module {
void *user_data; /**< module user_data */
};
#define pw_node_events_emit(o,m,v,...) spa_hook_list_call(&o->listener_list, struct pw_node_events, m, v, ##__VA_ARGS__)
#define pw_node_events_emit(o,m,v,...) spa_hook_list_call_simple_safe(&o->listener_list, struct pw_node_events, m, v, ##__VA_ARGS__)
#define pw_node_events_destroy(n) pw_node_events_emit(n, destroy, 0)
#define pw_node_events_free(n) pw_node_events_emit(n, free, 0)
#define pw_node_events_initialized(n) pw_node_events_emit(n, initialized, 0)