Callbacks not always executed on helper thread when using threaded poll
I’m using Avahi 0.6.31 on Linux to advertise some services and run a service browser. I’m using a threaded poll and running into some issues determining when it’s safe to call avahi_threaded_poll_lock().
The documentation for avahi_threaded_poll_start() states that "event loop callback functions are called from the event loop helper thread" but I’ve discovered this is not always true. When adding a new service to an entry group the entry group callback is called synchronously (not in the helper thread) to report the status of the new service and then later called in the helper thread when the status changes. It looks like this is by design based on where avahi_entry_group_set_state() is being called.
Looking at the code there doesn't seem to be any guarantee which status codes will arrive synchronously and which will arrive on the helper thread. So at least for the entry group callback I can't reliably determine which thread is in effect when it's called.
The documentation mildly states that the avahi_threaded_poll_lock() call is "not necessary" in the helper thread but it is in fact fatal due to an assert in the code. So clients really need to know which thread they are in during a callback. Could you add a call so a client can determine whether the current thread is the helper thread or not? Either that or make avahi_threaded_poll_lock() and avahi_thread_poll_unlock() safe to call at any time?
This is still an issue with Avahi 0.8.
It is easy to reproduce:
- Using the synchronous example, convert simple poll to threaded poll.
- Initialize the client with
avahi_client_new() - Don't start the poll - don't call
avahi_threaded_poll_start() - You will see the client callback is called from the same thread.
This will result in the following:
avahi_client_newwithstate==AVAHI_CLIENT_S_RUNNING->create_services()The issue here, is a failure increate_serviceswill result inavahi_threaded_poll_quitbeing called, which will break on it's assert, as @beldenfox described. - Pass a value such as
(AvahiPublishFlags)AVAHI_PUBLISH_UNIQUEthrough toavahi_entry_group_add_service(). It should fail with error:
Failed to add xxx service: Invalid flags
- This will result in error:
publish: thread-watch.c:165: avahi_threaded_poll_quit: Assertion `pthread_equal(pthread_self(), p->thread_id)' failed.
Aborted (core dumped)
An alternative workaround
Check the client state directly after avahi_client_new(), and call the create_services() manually instead of within client_callback. Thus, the initial synchronous call can be marked as not thread-safe via a function parameter, and can be handled accordingly within the method and inner method calls.
/* Check whether creating the client object succeeded */
if (!client) {
fprintf(stderr, "Failed to create client: %s\n", avahi_strerror(error));
goto fail;
}
/* New check */
if (AVAHI_CLIENT_S_RUNNING == avahi_client_get_state(client)) {
create_services(client, NOT_THREADSAFE);
}