cURL / Mailing Lists / curl-library / Single Mail

curl-library

libcurl multithreading

From: Török Edvin <edwintorok_at_gmail.com>
Date: Fri, 22 Sep 2006 12:37:15 +0300

Hi,

I am bringing up and old issue, in the hope of getting a solution for it.
http://curl.haxx.se/mail/lib-2003-09/0134.html
http://curl.haxx.se/mail/lib-2003-09/0138.html

The problem is that without using CURLOPT_NOSIGNAL, curl just doesn't
work in a multithreaded environment: it jumps into another thread's
stack, and segfaults.
As Daniel Stenberg pointed out, we'd need to use ares, or provide our
own timeout mechanism.

However I don't understand why the suggested pthread_getspecific fix
can't be used _if_ pthreads is available. 'pthread_getspecific' is
part of POSIX Threads extension, and _if_ it is available, then you
should add support for it. It is not just adding support for some
weird threading system/API/style/whatever, it is an IEEE standard!
[http://www.opengroup.org/onlinepubs/009695399/functions/pthread_setspecific.html]

The original 'patch' needed the client to use pthread_setspecific, but
that can be moved into curl_easy_init.

What do you think of these changes below:

In hostip.c:

#ifdef HAVE_SIGSETJMP
#ifndef HAVE_PTHREADS_SPECIFIC
/* Beware this is a global and unique instance. This is used to store the
   return address that we can jump back to from inside a signal handler. This
   is not thread-safe stuff. */
sigjmp_buf curl_jmpenv;
# else
pthread_key_t curl_jmpenv;
# endif
#endif

and then in Curl_resolv:

#ifdef HAVE_SIGSETJMP
  /* this allows us to time-out from the name resolver, as the timeout
     will generate a signal and we will siglongjmp() from that here */
  if(!data->set.no_signal &&
# ifndef HAVE_PTHREADS_SPECIFIC
     sigsetjmp(curl_jmpenv, 1)
# else
     sigsetjmp(*((sigjmp_buf*)pthread_getspecific(curl_jmpenv)), 1)
# endif
     ) {
    /* this is coming from a siglongjmp() */
    failf(data, "name lookup timed out");
    return -1;
  }
#endif

Similarly, in url.c:

RETSIGTYPE alarmfunc(int signal)
{
  /* this is for "-ansi -Wall -pedantic" to stop complaining! (rabe) */
  (void)signal;
#ifdef HAVE_SIGSETJMP
#ifndef HAVE_PTHREADS_SPECIFIC
  siglongjmp(curl_jmpenv, 1);
# else
  siglongjmp(*((sig_jmpbuf*)pthread_getspecific(curl_jmpenv)), 1);
# endif
#endif
  return;
}

In easy.c:
#ifdef HAVE_SIGSETJMP
#include <setjmp.h>
#endif

#if defined(HAVE_PTHREADS_SPECIFIC) && defined(HAVE_SIGSETJMP)
extern pthread_key_t curl_jmpenv;
static pthread_once_t buffer_key_once = PTHREAD_ONCE_INIT;
static void buffer_key_alloc(void)
{
  pthread_key_create(&curl_jmpenv,buffer_destroy);
}
static void buffer_destroy(void* buf)
{
  free(buf);
}

#endif

in curl_easy_init():
# if defined(HAVE_PTHREADS_SPECIFIC) && defined(HAVE_SIGSETJMP)
   pthread_once(&buffer_key_once,buffer_key_alloc);
   pthread_setspecific(curl_jmpenv, malloc(sizeof(sigjmp_buf)));
# endif

and a function to tell the libcurl user if signal based timeouts are
thread-safe or not:

int curl_easy_aresignaltimeouts_threadsafe()
{
#ifdef HAVE_PTHREADS_SPECIFIC
return 1;
#else
return 0;
#endif
}

You should also add a check in configure.ac for
pthread_getspecific/pthread_set_specific.

Best regards,
Edwin
Received on 2006-09-22