Commit e3564201 authored by Gisle Vanem's avatar Gisle Vanem
Browse files

Added Traian Nicolescu's patches for threaded resolver on
Windows. Plugged some potential handle and memory leaks.

Refs.
  http://curl.haxx.se/mail/lib-2004-10/0134.html
  http://curl.haxx.se/mail/lib-2004-10/0157.html
parent e8f85cba
Loading
Loading
Loading
Loading
+142 −35
Original line number Diff line number Diff line
@@ -64,6 +64,7 @@
#endif

#ifdef WIN32
#include <stdlib.h>
#include <process.h>
#endif

@@ -156,6 +157,8 @@ struct thread_data {
  DWORD  thread_status;
  curl_socket_t dummy_sock;   /* dummy for Curl_fdset() */
  FILE *stderr_file;
  HANDLE mutex_waiting;  /* marks that we are still waiting for a resolve */
  HANDLE event_resolved; /* marks that the thread obtained the information */
#ifdef CURLRES_IPV6
  struct addrinfo hints;
#endif
@@ -174,7 +177,19 @@ static unsigned __stdcall gethostbyname_thread (void *arg)
  struct connectdata *conn = (struct connectdata*) arg;
  struct thread_data *td = (struct thread_data*) conn->async.os_specific;
  struct hostent *he;
  int    rc;
  int    rc = 0;

  /* Duplicate the passed mutex handle.
   * This allows us to use it even after the container gets destroyed
   * due to a resolver timeout.
   */
  HANDLE mutex_waiting = NULL;
  if (!DuplicateHandle(GetCurrentProcess(), td->mutex_waiting,
                       GetCurrentProcess(), &mutex_waiting, 0, FALSE,
                       DUPLICATE_SAME_ACCESS)) {
    /* failed to duplicate the mutex, no point in continuing */
    return 0;
  }

  /* Sharing the same _iob[] element with our parent thread should
   * hopefully make printouts synchronised. I'm not sure it works
@@ -184,6 +199,16 @@ static unsigned __stdcall gethostbyname_thread (void *arg)

  WSASetLastError (conn->async.status = NO_DATA); /* pending status */
  he = gethostbyname (conn->async.hostname);

  /* is the thread initiator still waiting for us ? */
  if (WaitForSingleObject(mutex_waiting, 0) == WAIT_TIMEOUT) {
    /* yes, it is */

    /* Mark that we have obtained the information, and that we are
     * calling back with it.
     */
    SetEvent(td->event_resolved);

    if (he) {
      Curl_addrinfo4_callback(conn, CURL_ASYNC_SUCCESS, he);
      rc = 1;
@@ -194,6 +219,11 @@ static unsigned __stdcall gethostbyname_thread (void *arg)
    }
    TRACE(("Winsock-error %d, addr %s\n", conn->async.status,
           he ? inet_ntoa(*(struct in_addr*)he->h_addr) : "unknown"));
  }

  /* clean up */
  CloseHandle(mutex_waiting);

  return (rc);
  /* An implicit _endthreadex() here */
}
@@ -215,6 +245,18 @@ static unsigned __stdcall getaddrinfo_thread (void *arg)
  char   service [NI_MAXSERV];
  int    rc;

  /* Duplicate the passed mutex handle.
   * This allows us to use it even after the container gets destroyed
   * due to a resolver timeout.
   */
  HANDLE mutex_waiting = NULL;
  if (!DuplicateHandle(GetCurrentProcess(), td->mutex_waiting,
                       GetCurrentProcess(), &mutex_waiting, 0, FALSE,
                       DUPLICATE_SAME_ACCESS)) {
    /* failed to duplicate the mutex, no point in continuing */
    return 0;
  }

  *stderr = *td->stderr_file;

  itoa(conn->async.port, service, 10);
@@ -223,6 +265,15 @@ static unsigned __stdcall getaddrinfo_thread (void *arg)

  rc = getaddrinfo(conn->async.hostname, service, &td->hints, &res);

  /* is the thread initiator still waiting for us ? */
  if (WaitForSingleObject(mutex_waiting, 0) == WAIT_TIMEOUT) {
    /* yes, it is */

    /* Mark that we have obtained the information, and that we are
     * calling back with it.
     */
    SetEvent(td->event_resolved);

    if (rc == 0) {
#ifdef DEBUG_THREADING_GETADDRINFO
      dump_addrinfo (conn, res);
@@ -233,6 +284,11 @@ static unsigned __stdcall getaddrinfo_thread (void *arg)
      Curl_addrinfo6_callback(conn, (int)WSAGetLastError(), NULL);
      TRACE(("Winsock-error %d, no address\n", conn->async.status));
    }
  }

  /* clean up */
  CloseHandle(mutex_waiting);

  return (rc);
  /* An implicit _endthreadex() here */
}
@@ -248,10 +304,18 @@ static void destroy_thread_data (struct Curl_async *async)
    free(async->hostname);

  if (async->os_specific) {
    curl_socket_t sock = ((const struct thread_data*)async->os_specific)->dummy_sock;
    struct thread_data *td = (struct thread_data*) async->os_specific;
    curl_socket_t sock = td->dummy_sock;

    if (sock != CURL_SOCKET_BAD)
      sclose(sock);

    /* destroy the synchronization objects */
    if (td->mutex_waiting)
      CloseHandle(td->mutex_waiting);
    if (td->event_resolved)
      CloseHandle(td->event_resolved);

    free(async->os_specific);
  }
  async->hostname = NULL;
@@ -288,8 +352,28 @@ static bool init_resolve_thread (struct connectdata *conn,
  conn->async.status = 0;
  conn->async.dns = NULL;
  conn->async.os_specific = (void*) td;

  td->dummy_sock = CURL_SOCKET_BAD;

  /* Create the mutex used to inform the resolver thread that we're
   * still waiting, and take initial ownership.
   */
  td->mutex_waiting = CreateMutex(NULL, TRUE, NULL);
  if (td->mutex_waiting == NULL) {
    destroy_thread_data(&conn->async);
    SetLastError(EAGAIN);
    return FALSE;
  }

  /* Create the event that the thread uses to inform us that it's
   * done resolving. Do not signal it.
   */
  td->event_resolved = CreateEvent(NULL, TRUE, FALSE, NULL);
  if (td->event_resolved == NULL) {
    destroy_thread_data(&conn->async);
    SetLastError(EAGAIN);
    return FALSE;
  }

  td->stderr_file = stderr;
  td->thread_hnd = (HANDLE) _beginthreadex(NULL, 0, THREAD_FUNC,
                                           conn, 0, &td->thread_id);
@@ -342,8 +426,30 @@ CURLcode Curl_wait_for_resolv(struct connectdata *conn,
    CURL_TIMEOUT_RESOLVE; /* default name resolve timeout */
  ticks = GetTickCount();

  status = WaitForSingleObject(td->thread_hnd, 1000UL*timeout);
  if (status == WAIT_OBJECT_0 || status == WAIT_ABANDONED) {
  /* wait for the thread to resolve the name */
  status = WaitForSingleObject(td->event_resolved, 1000UL*timeout);

  /* mark that we are now done waiting */
  ReleaseMutex(td->mutex_waiting);

  /* close our handle to the mutex, no point in hanging on to it */
  CloseHandle(td->mutex_waiting);
  td->mutex_waiting = NULL;

  /* close the event handle, it's useless now */
  CloseHandle(td->event_resolved);
  td->event_resolved = NULL;

  /* has the resolver thread succeeded in resolving our query ? */
  if (status == WAIT_OBJECT_0) {
    /* wait for the thread to exit, it's in the callback sequence */
    if (WaitForSingleObject(td->thread_hnd, 5000) == WAIT_TIMEOUT) {
      TerminateThread(td->thread_hnd, 0);
      conn->async.done = TRUE;
      td->thread_status = (DWORD)-1;
      TRACE(("%s() thread stuck?!, ", THREAD_NAME));
    }
    else {
      /* Thread finished before timeout; propagate Winsock error to this thread.
       * 'conn->async.done = TRUE' is set in Curl_addrinfo4/6_callback().
       */
@@ -352,6 +458,7 @@ CURLcode Curl_wait_for_resolv(struct connectdata *conn,
      TRACE(("%s() status %lu, thread retval %lu, ",
             THREAD_NAME, status, td->thread_status));
    }
  }
  else {
    conn->async.done = TRUE;
    td->thread_status = (DWORD)-1;
@@ -460,8 +567,8 @@ Curl_addrinfo *Curl_getaddrinfo(struct connectdata *conn,
  }

  /* fall-back to blocking version */
  infof(data, "init_resolve_thread() failed for %s; code %lu\n",
        hostname, GetLastError());
  infof(data, "init_resolve_thread() failed for %s; %s\n",
        hostname, Curl_strerror(conn,GetLastError()));

  h = gethostbyname(hostname);
  if (!h) {
@@ -535,8 +642,8 @@ Curl_addrinfo *Curl_getaddrinfo(struct connectdata *conn,
  }

  /* fall-back to blocking version */
  infof(data, "init_resolve_thread() failed for %s; code %lu\n",
        hostname, GetLastError());
  infof(data, "init_resolve_thread() failed for %s; %s\n",
        hostname, Curl_strerror(conn,GetLastError()));

  error = getaddrinfo(hostname, sbuf, &hints, &res);
  if (error) {