• Philip Withnall's avatar
    gio: Port GThreadedResolver to use res_nquery() to fix thread-safety · 40be86bb
    Philip Withnall authored
    res_query() uses global state in the form of the struct __res_state
    which contains the contents of resolv.conf (and other things). On Linux,
    this state seems to be thread-local, so there is no problem. On OS X,
    however, it is not, and hence multiple res_query() calls from parallel
    threads will compete and return bogus results.
    The fix for this is to use res_nquery(), introduced in BIND 8.2, which
    takes an explicit state argument. This allows us to manually store the
    state thread-locally. If res_nquery() isn’t available, we fall back to
    res_query(). It should be available on OS X though. As a data point,
    it’s available on Fedora 27.
    There’s a slight complication in the fact that OS X requires the state
    to be freed using res_ndestroy() rather than res_nclose(). Linux uses
    (See, for example, the NetBSD man page:
    https://www.unix.com/man-page/netbsd/3/res_ninit/. The Linux one is
    incomplete and not so useful:
    The new code will call res_ninit() once per res_nquery() task. This is
    not optimal, but no worse than before — since res_query() was being
    called in a worker thread, on Linux, it would implicitly initialise the
    thread-local struct __res_state when it was called. We’ve essentially
    just made that explicit. In practical terms, this means a
    stat("/etc/resolv.conf") call per res_nquery() task.
    In future, we could improve this by using an explicit thread pool with
    some manually-created worker threads, each of which initialises a struct
    __res_state on spawning, and only updates it on receiving
    the #GResolver::reload signal.
    Signed-off-by: Philip Withnall's avatarPhilip Withnall <withnall@endlessm.com>
config.h.meson 23 KB