From 23af112f5556d6a785c17e09f2422ac931405f61 Mon Sep 17 00:00:00 2001 From: Thomas Guillem Date: Tue, 5 Apr 2022 15:46:03 +0200 Subject: [PATCH 1/1] lib: make curl_global_init() threadsafe when possible Use a posix pthread or a Windows SRWLOCK to lock curl_global_init*() and curl_global_cleanup(). Closes #8680 Conflict:update easy_lock.h to latest Reference:https://github.com/curl/curl/commit/23af112f5556d6a785c17e09f2422ac931405f61 --- configure.ac | 2 ++ lib/Makefile.inc | 1 + lib/easy.c | 52 +++++++++++++++++++++++++++++---- lib/easy_lock.h | 105 ++++++++++++++++++++++++++++++++++++++++++++ m4/curl-functions.m4 | 23 +++++++++++++++ 5 files changed, 178 insertions(+), 5 deletions(-) create mode 100644 lib/easy_lock.h diff --git a/configure.ac b/configure.ac index 3910ec631..009f32c32 100644 --- a/configure.ac +++ b/configure.ac @@ -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 @@ -3444,6 +3445,7 @@ AC_CHECK_FUNCS([fnmatch \ if_nametoindex \ mach_absolute_time \ pipe \ + sched_yield \ setlocale \ setmode \ setrlimit \ diff --git a/lib/Makefile.inc b/lib/Makefile.inc index 1ab007896..f756515d6 100644 --- a/lib/Makefile.inc +++ b/lib/Makefile.inc @@ -262,6 +262,7 @@ LIB_HFILES = \ doh.h \ dotdot.h \ dynbuf.h \ + easy_lock.h \ easyif.h \ easyoptions.h \ escape.h \ diff --git a/lib/easy.c b/lib/easy.c index 336cada87..7781c8e16 100644 --- a/lib/easy.c +++ b/lib/easy.c @@ -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 @@ -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; } /* @@ -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; } @@ -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; } /** @@ -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(); @@ -273,6 +309,8 @@ void curl_global_cleanup(void) #endif init_flags = 0; + + global_init_unlock(); } /* @@ -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); diff --git a/lib/easy_lock.h b/lib/easy_lock.h new file mode 100644 index 000000000..41627c4d4 --- /dev/null +++ b/lib/easy_lock.h @@ -0,0 +1,105 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , 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. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" + +#define GLOBAL_INIT_IS_THREADSAFE + +#if defined(_WIN32_WINNT) && _WIN32_WINNT >= 0x600 + +#ifdef __MINGW32__ +#ifndef __MINGW64_VERSION_MAJOR +#if (__MINGW32_MAJOR_VERSION < 5) || \ + (__MINGW32_MAJOR_VERSION == 5 && __MINGW32_MINOR_VERSION == 0) +/* mingw >= 5.0.1 defines SRWLOCK, and slightly different from MS define */ +typedef PVOID SRWLOCK, *PSRWLOCK; +#endif +#endif +#ifndef SRWLOCK_INIT +#define SRWLOCK_INIT NULL +#endif +#endif /* __MINGW32__ */ + +#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) && defined(HAVE_STDATOMIC_H) +#include +#if defined(HAVE_SCHED_YIELD) +#include +#endif + +#define curl_simple_lock atomic_int +#define CURL_SIMPLE_LOCK_INIT 0 + +/* a clang-thing */ +#ifndef __has_builtin +#define __has_builtin(x) 0 +#endif + +#ifndef __INTEL_COMPILER +/* The Intel compiler tries to look like GCC *and* clang *and* lies in its + __has_builtin() function, so override it. */ + +/* if GCC on i386/x86_64 or if the built-in is present */ +#if ( (defined(__GNUC__) && !defined(__clang__)) && \ + (defined(__i386__) || defined(__x86_64__))) || \ + __has_builtin(__builtin_ia32_pause) +#define HAVE_BUILTIN_IA32_PAUSE +#endif + +#endif + +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) */ +#ifdef HAVE_BUILTIN_IA32_PAUSE + __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 diff --git a/m4/curl-functions.m4 b/m4/curl-functions.m4 index b40d42798..eb85dce4a 100644 --- a/m4/curl-functions.m4 +++ b/m4/curl-functions.m4 @@ -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" + ]) +]) -- 2.33.0