cURL / Mailing Lists / curl-library / Single Mail

curl-library

resolver thread deadlock during Windows DLL attach

From: Mohun Biswas <m_biswas_at_mailinator.com>
Date: Sun, 30 Dec 2007 19:04:58 -0500

I've encountered a subtle problem porting my libcurl-using application
to Windows.

This is using 7.16.4 with VS.Net 2003 with an app which is both client
and server (i.e. we own both sides and they only talk to each other).
Our use of libcurl is both simple and complex. Simple in the sense that
it needs only a single HTTP connection which is reused throughout the
life of the client process so there are no other protocols required, no
SSL, no multi handles or pipelining or async DNS (in fact the client
only ever talks to one server). Complex in the sense that the client
code is in the form of a DLL/shared library which is inserted into the
address space of third party programs for which we do *not* have source.

This actually works quite well on Unix. On Windows there's a bug for
which I have a simple test case (main snippet appended). As you can see
there's a function which makes a libcurl easy-style connection and is
called from the DllMain routine at attach time. The test program which
loads this DLL deadlocks during DNS resolution, specifically in
init_resolve_thread(). I've found that if I change the URL to use an IP
address everything works fine but with a hostname it will get into
init_resolve_thread() and lock up.

The comment in lib/hostthre.c says

/*
  * init_resolve_thread() starts a new thread that performs the actual
  * resolve. This function returns before the resolve is done.
  */

So presumably starting a new thread from DllMain isn't allowed. In fact
I just found this in MSDN about DllMain:

"Access to the entry point is serialized by the system on a process-wide
basis. Threads in DllMain hold the loader lock so no additional DLLs can
be dynamically loaded or initialized."

So the question is: is there a way to get the hostname resolved without
spawning a new thread? I've not followed any libcurl DNS discussions in
the past because we're at the other end of the spectrum where we only
need to resolve one hostname once. In fact I could probably work around
this by using a wrapper program to convert it to an IP address and place
that in the environment but I'm hoping for a cleaner solution.

I know there's a thing called c-ares but that seems to be going the
wrong direction. What I think I need is a *synchronous* resolver that
will work in the current thread (but then I don't claim to understand DNS).

Thanks,
MB

===================================================

#define _FILE_OFFSET_BITS 64 /* for curl_off_t magic */
#define WIN32_LEAN_AND_MEAN
#include <io.h>
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdio.h>
#include <tchar.h>
#include <windows.h>
#include <winsock2.h>

#include <curl/curl.h>
#include "Auditor.h"

// Put any URL here. With an IP address it will work.
// With a hostname it will lock up hard and need to be killed.
#define TEST_URL "http://www.pair.com"

static void
CurlTest(void)
{
   CURLcode ret;
   CURL *hnd;

   hnd = curl_easy_init();
   curl_easy_setopt(hnd, CURLOPT_URL, TEST_URL);
   curl_easy_setopt(hnd, CURLOPT_VERBOSE, 1);
   ret = curl_easy_perform(hnd);
   curl_easy_cleanup(hnd);
}

BOOL APIENTRY DllMain( HANDLE hModule,
                        DWORD ul_reason_for_call,
                        LPVOID lpReserved
                                         )
{
     switch (ul_reason_for_call)
     {
        case DLL_PROCESS_ATTACH:
                fprintf(stderr, "ATTACHING AUDITOR TO %s ...\n",
                    GetCommandLine());
                fflush(NULL);
                CurlTest();
                break;
        case DLL_THREAD_ATTACH:
        case DLL_THREAD_DETACH:
        case DLL_PROCESS_DETACH:
            break;
     }

     return TRUE;
}
Received on 2007-12-31