curl-library
resolver thread deadlock during Windows DLL attach
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