Skip to content

Commit

Permalink
lib: make curl_global_init() threadsafe when possible
Browse files Browse the repository at this point in the history
Use a posix pthread or a Windows SRWLOCK to lock curl_global_init*() and
curl_global_cleanup().

Closes #8680
  • Loading branch information
tguillem authored and bagder committed Jun 7, 2022
1 parent 134963a commit 23af112
Show file tree
Hide file tree
Showing 5 changed files with 142 additions and 5 deletions.
2 changes: 2 additions & 0 deletions configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ AC_SUBST(libext)
dnl figure out the libcurl version
CURLVERSION=`$SED -ne 's/^#define LIBCURL_VERSION "\(.*\)".*/\1/p' ${srcdir}/include/curl/curlver.h`
XC_CHECK_PROG_CC
CURL_ATOMIC

dnl for --enable-code-coverage
CURL_COVERAGE
Expand Down Expand Up @@ -3444,6 +3445,7 @@ AC_CHECK_FUNCS([fnmatch \
if_nametoindex \
mach_absolute_time \
pipe \
sched_yield \
setlocale \
setmode \
setrlimit \
Expand Down
1 change: 1 addition & 0 deletions lib/Makefile.inc
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,7 @@ LIB_HFILES = \
doh.h \
dotdot.h \
dynbuf.h \
easy_lock.h \
easyif.h \
easyoptions.h \
escape.h \
Expand Down
52 changes: 47 additions & 5 deletions lib/easy.c
Original file line number Diff line number Diff line change
Expand Up @@ -84,11 +84,25 @@
#include "curl_printf.h"
#include "curl_memory.h"
#include "memdebug.h"
#include "easy_lock.h"

/* true globals -- for curl_global_init() and curl_global_cleanup() */
static unsigned int initialized;
static long init_flags;

#ifdef GLOBAL_INIT_IS_THREADSAFE

static curl_simple_lock s_lock = CURL_SIMPLE_LOCK_INIT;
#define global_init_lock() curl_simple_lock_lock(&s_lock)
#define global_init_unlock() curl_simple_lock_unlock(&s_lock)

#else

#define global_init_lock()
#define global_init_unlock()

#endif

/*
* strdup (and other memory functions) is redefined in complicated
* ways, but at this point it must be defined as the system-supplied strdup
Expand Down Expand Up @@ -207,7 +221,14 @@ static CURLcode global_init(long flags, bool memoryfuncs)
*/
CURLcode curl_global_init(long flags)
{
return global_init(flags, TRUE);
CURLcode result;
global_init_lock();

result = global_init(flags, TRUE);

global_init_unlock();

return result;
}

/*
Expand All @@ -218,15 +239,20 @@ CURLcode curl_global_init_mem(long flags, curl_malloc_callback m,
curl_free_callback f, curl_realloc_callback r,
curl_strdup_callback s, curl_calloc_callback c)
{
CURLcode result;

/* Invalid input, return immediately */
if(!m || !f || !r || !s || !c)
return CURLE_FAILED_INIT;

global_init_lock();

if(initialized) {
/* Already initialized, don't do it again, but bump the variable anyway to
work like curl_global_init() and require the same amount of cleanup
calls. */
initialized++;
global_init_unlock();
return CURLE_OK;
}

Expand All @@ -239,7 +265,11 @@ CURLcode curl_global_init_mem(long flags, curl_malloc_callback m,
Curl_ccalloc = c;

/* Call the actual init function, but without setting */
return global_init(flags, FALSE);
result = global_init(flags, FALSE);

global_init_unlock();

return result;
}

/**
Expand All @@ -248,11 +278,17 @@ CURLcode curl_global_init_mem(long flags, curl_malloc_callback m,
*/
void curl_global_cleanup(void)
{
if(!initialized)
global_init_lock();

if(!initialized) {
global_init_unlock();
return;
}

if(--initialized)
if(--initialized) {
global_init_unlock();
return;
}

Curl_ssl_cleanup();
Curl_resolver_global_cleanup();
Expand All @@ -273,6 +309,8 @@ void curl_global_cleanup(void)
#endif

init_flags = 0;

global_init_unlock();
}

/*
Expand All @@ -285,14 +323,18 @@ struct Curl_easy *curl_easy_init(void)
struct Curl_easy *data;

/* Make sure we inited the global SSL stuff */
global_init_lock();

if(!initialized) {
result = curl_global_init(CURL_GLOBAL_DEFAULT);
result = global_init(CURL_GLOBAL_DEFAULT, TRUE);
if(result) {
/* something in the global init failed, return nothing */
DEBUGF(fprintf(stderr, "Error: curl_global_init failed\n"));
global_init_unlock();
return NULL;
}
}
global_init_unlock();

/* We use curl_open() with undefined URL so far */
result = Curl_open(&data);
Expand Down
69 changes: 69 additions & 0 deletions lib/easy_lock.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at https://curl.se/docs/copyright.html.
*
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
* copies of the Software, and permit persons to whom the Software is
* furnished to do so, under the terms of the COPYING file.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
***************************************************************************/

#include "curl_setup.h"

#define GLOBAL_INIT_IS_THREADSAFE

#if defined(_WIN32_WINNT) && _WIN32_WINNT >= 0x600

#define curl_simple_lock SRWLOCK
#define CURL_SIMPLE_LOCK_INIT SRWLOCK_INIT

#define curl_simple_lock_lock(m) AcquireSRWLockExclusive(m)
#define curl_simple_lock_unlock(m) ReleaseSRWLockExclusive(m)

#elif defined (HAVE_ATOMIC)
#include <stdatomic.h>

#define curl_simple_lock atomic_bool
#define CURL_SIMPLE_LOCK_INIT ATOMIC_VAR_INIT(false)

static inline void curl_simple_lock_lock(curl_simple_lock *lock)
{
for(;;) {
if(!atomic_exchange_explicit(lock, true, memory_order_acquire))
break;
/* Reduce cache coherency traffic */
while(atomic_load_explicit(lock, memory_order_relaxed)) {
/* Reduce load (not mandatory) */
#if defined(__i386__) || defined(__x86_64__)
__builtin_ia32_pause();
#elif defined(__aarch64__)
asm volatile("yield" ::: "memory");
#elif defined(HAVE_SCHED_YIELD)
sched_yield();
#endif
}
}
}

static inline void curl_simple_lock_unlock(curl_simple_lock *lock)
{
atomic_store_explicit(lock, false, memory_order_release);
}

#else

#undef GLOBAL_INIT_IS_THREADSAFE

#endif
23 changes: 23 additions & 0 deletions m4/curl-functions.m4
Original file line number Diff line number Diff line change
Expand Up @@ -6566,3 +6566,26 @@ AC_DEFUN([CURL_COVERAGE],[
LIBS="$LIBS -lgcov"
fi
])
dnl CURL_ATOMIC
dnl --------------------------------------------------
dnl Check if _Atomic works
dnl
AC_DEFUN([CURL_ATOMIC],[
AC_MSG_CHECKING([if _Atomic is available])
AC_COMPILE_IFELSE([
AC_LANG_PROGRAM([[
$curl_includes_unistd
]],[[
_Atomic int i = 0;
]])
],[
AC_MSG_RESULT([yes])
AC_DEFINE_UNQUOTED(HAVE_ATOMIC, 1,
[Define to 1 if you have _Atomic support.])
tst_atomic="yes"
],[
AC_MSG_RESULT([no])
tst_atomic="no"
])
])

0 comments on commit 23af112

Please sign in to comment.