]> code.delx.au - pulseaudio/blobdiff - src/modules/module-zeroconf-publish.c
zeroconf-publish: Don't assume any particular defer event ordering
[pulseaudio] / src / modules / module-zeroconf-publish.c
index 0110e1d68d1bda708c31dd414294b15ed61b1233..be8806e32a72248e0f40e3c14e26a261b8dd20f5 100644 (file)
@@ -141,6 +141,9 @@ struct userdata {
     pa_hook_slot *sink_new_slot, *source_new_slot, *sink_unlink_slot, *source_unlink_slot, *sink_changed_slot, *source_changed_slot;
 
     pa_native_protocol *native;
+
+    bool shutting_down; /* Used in the main thread. */
+    bool client_freed; /* Used in the Avahi thread. */
 };
 
 /* Runs in PA mainloop context */
@@ -638,6 +641,11 @@ static void unpublish_all_services(struct userdata *u, bool rem) {
 static int avahi_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) {
     struct userdata *u = (struct userdata *) data;
 
+    pa_assert(u);
+
+    if (u->shutting_down)
+        return 0;
+
     switch (code) {
         case AVAHI_MESSAGE_PUBLISH_ALL:
             publish_all_services(u);
@@ -647,10 +655,6 @@ static int avahi_process_msg(pa_msgobject *o, int code, void *data, int64_t offs
             pa_module_unload(u->core, u->module, true);
             break;
 
-        case AVAHI_MESSAGE_SHUTDOWN_COMPLETE:
-            /* pa__done() is waiting for this */
-            break;
-
         default:
             pa_assert_not_reached();
     }
@@ -704,6 +708,16 @@ static void create_client(pa_mainloop_api *api PA_GCC_UNUSED, void *userdata) {
     struct userdata *u = (struct userdata *) userdata;
     int error;
 
+    /* create_client() and client_free() are called via defer events. If the
+     * two defer events are created very quickly one after another, we can't
+     * assume that the defer event that runs create_client() will be dispatched
+     * before the defer event that runs client_free() (at the time of writing,
+     * pa_mainloop actually always dispatches queued defer events in reverse
+     * creation order). For that reason we must be prepared for the case where
+     * client_free() has already been called. */
+    if (u->client_freed)
+        return;
+
     pa_thread_mq_install(&u->thread_mq);
 
     if (!(u->client = avahi_client_new(u->avahi_poll, AVAHI_CLIENT_NO_FAIL, client_callback, u, &error))) {
@@ -730,7 +744,7 @@ int pa__init(pa_module*m) {
         goto fail;
     }
 
-    m->userdata = u = pa_xnew(struct userdata, 1);
+    m->userdata = u = pa_xnew0(struct userdata, 1);
     u->core = m->core;
     u->module = m;
     u->native = pa_native_protocol_get(u->core);
@@ -754,8 +768,6 @@ int pa__init(pa_module*m) {
     u->source_changed_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_PROPLIST_CHANGED], PA_HOOK_LATE, (pa_hook_cb_t) device_new_or_changed_cb, u);
     u->source_unlink_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_UNLINK], PA_HOOK_LATE, (pa_hook_cb_t) device_unlink_cb, u);
 
-    u->main_entry_group = NULL;
-
     un = pa_get_user_name_malloc();
     hn = pa_get_host_name_malloc();
     u->service_name = pa_truncate_utf8(pa_sprintf_malloc("%s@%s", un, hn), AVAHI_LABEL_MAX-1);
@@ -797,7 +809,9 @@ static void client_free(pa_mainloop_api *api PA_GCC_UNUSED, void *userdata) {
     if (u->avahi_poll)
         pa_avahi_poll_free(u->avahi_poll);
 
-    pa_asyncmsgq_post(u->thread_mq.outq, PA_MSGOBJECT(u->msg), AVAHI_MESSAGE_SHUTDOWN_COMPLETE, NULL, 0, NULL, NULL);
+    pa_asyncmsgq_post(u->thread_mq.outq, PA_MSGOBJECT(u->msg), AVAHI_MESSAGE_SHUTDOWN_COMPLETE, u, 0, NULL, NULL);
+
+    u->client_freed = true;
 }
 
 void pa__done(pa_module*m) {
@@ -807,7 +821,11 @@ void pa__done(pa_module*m) {
     if (!(u = m->userdata))
         return;
 
+    u->shutting_down = true;
+
+    pa_threaded_mainloop_lock(u->mainloop);
     pa_mainloop_api_once(u->api, client_free, u);
+    pa_threaded_mainloop_unlock(u->mainloop);
     pa_asyncmsgq_wait_for(u->thread_mq.outq, AVAHI_MESSAGE_SHUTDOWN_COMPLETE);
 
     pa_threaded_mainloop_stop(u->mainloop);