From 108c485248f28557cd864dd13da9acd510758620 Mon Sep 17 00:00:00 2001 From: Markus Minichmayr Date: Mon, 10 Oct 2022 19:01:06 +0200 Subject: CMAKE option LIBICAL_SYNC_MODE_THREADLOCAL: Allow compiling all global variables with thread-local storage, thus avoiding the need for synchronization. --- CMakeLists.txt | 10 ++++++++++ config.h.cmake | 31 ++++++++++++++++++++++++++++++- src/libical/icalerror.c | 2 +- src/libical/icalmemory.c | 10 +++++----- src/libical/icalmemory.h | 4 ++-- src/libical/icalrecur.c | 10 +++++----- src/libical/icaltimezone.c | 10 +++++----- src/libical/icaltypes.c | 10 +++++----- src/libical/pvl.c | 2 +- src/test/builtin_timezones.c | 6 +++--- src/test/icaltm_test.c | 14 ++++++++++++++ 11 files changed, 81 insertions(+), 28 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index f5d5e52c..c568dd0b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -107,6 +107,13 @@ # Build with the undefined sanitizer (requires gcc or clang) # Default=false # +# -DLIBICAL_SYNCMODE_THREADLOCAL=[true|false] +# Experimental: If set to true, global variables are marked as thread-local. This allows accessing +# libical from multiple threads without the need for synchronization. However, any global settings +# must be applied per thread. +# If set to false, the default synchronization mechanism is applied. That is, if the pthreads library +# is available, it will be used, otherwise no synchronization is applied. +# Default: false. cmake_minimum_required(VERSION 3.11.0) #first line, to shutup a cygwin warning project(libical C) #CXX is optional for the bindings @@ -736,6 +743,9 @@ if(LIBICAL_DEVMODE_UNDEFINED_SANITIZER) endif() endif() +mark_as_advanced(LIBICAL_SYNCMODE_THREADLOCAL) +libical_option(LIBICAL_SYNCMODE_THREADLOCAL "Experimental: Mark global variables as thread-local." False) + libical_option(ENABLE_LTO_BUILD "Build a link-time optimized version." False) if(ENABLE_LTO_BUILD) if(CMAKE_C_COMPILER_IS_GCC) diff --git a/config.h.cmake b/config.h.cmake index 0e7306cc..93ac378d 100644 --- a/config.h.cmake +++ b/config.h.cmake @@ -583,5 +583,34 @@ typedef ssize_t IO_SSIZE_T; #define ICALMEMORY_DEFAULT_REALLOC realloc #define ICALMEMORY_DEFAULT_FREE free -/* ICAL_GLOBAL_VAR is applied to global variables, therefore allows specifying custom storage attributes. */ + +#cmakedefine LIBICAL_SYNCMODE_THREADLOCAL 1 + +#define ICAL_SYNC_MODE_NONE 1 +#define ICAL_SYNC_MODE_PTHREAD 2 +#define ICAL_SYNC_MODE_THREADLOCAL 3 + +#ifdef LIBICAL_SYNCMODE_THREADLOCAL +#define ICAL_SYNC_MODE ICAL_SYNC_MODE_THREADLOCAL +#elif HAVE_PTHREAD == 1 +#define ICAL_SYNC_MODE ICAL_SYNC_MODE_PTHREAD +#else +#define ICAL_SYNC_MODE ICAL_SYNC_MODE_NONE +#endif + +#if ICAL_SYNC_MODE == ICAL_SYNC_MODE_THREADLOCAL +#if defined(_MSC_VER) +#define ICAL_GLOBAL_VAR __declspec(thread) +#endif +#if defined(__GNUC__) +#define ICAL_GLOBAL_VAR __thread +#endif +#ifndef ICAL_GLOBAL_VAR +/* thread_local has been introduced with C11. Starting with C23 it will become a keyword and + we won't need to include threads.h then. */ +#include +#define ICAL_GLOBAL_VAR thread_local +#endif +#else #define ICAL_GLOBAL_VAR +#endif diff --git a/src/libical/icalerror.c b/src/libical/icalerror.c index 9692bd1e..d8a85b4c 100644 --- a/src/libical/icalerror.c +++ b/src/libical/icalerror.c @@ -22,7 +22,7 @@ #include #endif -#if defined(HAVE_PTHREAD) +#if ICAL_SYNC_MODE == ICAL_SYNC_MODE_PTHREAD #include static pthread_key_t icalerrno_key; diff --git a/src/libical/icalmemory.c b/src/libical/icalmemory.c index f12833b3..abf98618 100644 --- a/src/libical/icalmemory.c +++ b/src/libical/icalmemory.c @@ -39,7 +39,7 @@ typedef struct void *ring[BUFFER_RING_SIZE]; } buffer_ring; -#if !defined(HAVE_PTHREAD) +#if ICAL_SYNC_MODE != ICAL_SYNC_MODE_PTHREAD /** * @private */ @@ -63,7 +63,7 @@ static void icalmemory_free_ring_byval(buffer_ring * br) icalmemory_free_buffer(br); } -#if defined(HAVE_PTHREAD) +#if ICAL_SYNC_MODE == ICAL_SYNC_MODE_PTHREAD #include static pthread_key_t ring_key; @@ -116,7 +116,7 @@ static buffer_ring *buffer_ring_new(void) return (br); } -#if defined(HAVE_PTHREAD) +#if ICAL_SYNC_MODE == ICAL_SYNC_MODE_PTHREAD /** * @private */ @@ -156,7 +156,7 @@ static buffer_ring *get_buffer_ring_global(void) */ static buffer_ring *get_buffer_ring(void) { -#if defined(HAVE_PTHREAD) +#if ICAL_SYNC_MODE == ICAL_SYNC_MODE_PTHREAD return (get_buffer_ring_pthread()); #else return get_buffer_ring_global(); @@ -221,7 +221,7 @@ void icalmemory_free_ring(void) return; icalmemory_free_ring_byval(br); -#if defined(HAVE_PTHREAD) +#if ICAL_SYNC_MODE == ICAL_SYNC_MODE_PTHREAD pthread_setspecific(ring_key, 0); #else global_buffer_ring = 0; diff --git a/src/libical/icalmemory.h b/src/libical/icalmemory.h index 39541d2d..adb580da 100644 --- a/src/libical/icalmemory.h +++ b/src/libical/icalmemory.h @@ -120,8 +120,8 @@ LIBICAL_ICAL_EXPORT void icalmemory_add_tmp_buffer(void *buf); /** * @brief Frees all memory used in the ring * - * Frees all memory used in the ring. Depending on if HAVE_PTHREAD is set or - * not, the ring buffer is allocated on a per-thread basis, meaning that if all + * Frees all memory used in the ring. If PTHREAD is used or thread-local mode is configured, + * the ring buffer is allocated on a per-thread basis, meaning that if all * rings are to be released, it must be called once in every thread. * * @par Usage diff --git a/src/libical/icalrecur.c b/src/libical/icalrecur.c index b9c7b77d..6080bea5 100644 --- a/src/libical/icalrecur.c +++ b/src/libical/icalrecur.c @@ -134,7 +134,7 @@ #include /* For offsetof() macro */ #include -#if defined(HAVE_PTHREAD) +#if ICAL_SYNC_MODE == ICAL_SYNC_MODE_PTHREAD #include static pthread_mutex_t invalid_rrule_mutex = PTHREAD_MUTEX_INITIALIZER; #endif @@ -3642,13 +3642,13 @@ ical_invalid_rrule_handling ical_get_invalid_rrule_handling_setting(void) { ical_invalid_rrule_handling myHandling; -#if defined(HAVE_PTHREAD) +#if ICAL_SYNC_MODE == ICAL_SYNC_MODE_PTHREAD pthread_mutex_lock(&invalid_rrule_mutex); #endif myHandling = invalidRruleHandling; -#if defined(HAVE_PTHREAD) +#if ICAL_SYNC_MODE == ICAL_SYNC_MODE_PTHREAD pthread_mutex_unlock(&invalid_rrule_mutex); #endif @@ -3657,13 +3657,13 @@ ical_invalid_rrule_handling ical_get_invalid_rrule_handling_setting(void) void ical_set_invalid_rrule_handling_setting(ical_invalid_rrule_handling newSetting) { -#if defined(HAVE_PTHREAD) +#if ICAL_SYNC_MODE == ICAL_SYNC_MODE_PTHREAD pthread_mutex_lock(&invalid_rrule_mutex); #endif invalidRruleHandling = newSetting; -#if defined(HAVE_PTHREAD) +#if ICAL_SYNC_MODE == ICAL_SYNC_MODE_PTHREAD pthread_mutex_unlock(&invalid_rrule_mutex); #endif } diff --git a/src/libical/icaltimezone.c b/src/libical/icaltimezone.c index 60b26a0d..acaf39c2 100644 --- a/src/libical/icaltimezone.c +++ b/src/libical/icaltimezone.c @@ -26,7 +26,7 @@ #include #include -#if defined(HAVE_PTHREAD) +#if ICAL_SYNC_MODE == ICAL_SYNC_MODE_PTHREAD #include #if defined(PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP) // It seems the same thread can attempt to lock builtin_mutex multiple times @@ -166,28 +166,28 @@ static const char *get_zone_directory(void); static void icaltimezone_builtin_lock(void) { -#if defined(HAVE_PTHREAD) +#if ICAL_SYNC_MODE == ICAL_SYNC_MODE_PTHREAD pthread_mutex_lock(&builtin_mutex); #endif } static void icaltimezone_builtin_unlock(void) { -#if defined(HAVE_PTHREAD) +#if ICAL_SYNC_MODE == ICAL_SYNC_MODE_PTHREAD pthread_mutex_unlock(&builtin_mutex); #endif } static void icaltimezone_changes_lock(void) { -#if defined(HAVE_PTHREAD) +#if ICAL_SYNC_MODE == ICAL_SYNC_MODE_PTHREAD pthread_mutex_lock(&changes_mutex); #endif } static void icaltimezone_changes_unlock(void) { -#if defined(HAVE_PTHREAD) +#if ICAL_SYNC_MODE == ICAL_SYNC_MODE_PTHREAD pthread_mutex_unlock(&changes_mutex); #endif } diff --git a/src/libical/icaltypes.c b/src/libical/icaltypes.c index 95d18540..ad85ae52 100644 --- a/src/libical/icaltypes.c +++ b/src/libical/icaltypes.c @@ -17,7 +17,7 @@ #define TMP_BUF_SIZE 1024 -#if defined(HAVE_PTHREAD) +#if ICAL_SYNC_MODE == ICAL_SYNC_MODE_PTHREAD #include static pthread_mutex_t unk_token_mutex = PTHREAD_MUTEX_INITIALIZER; #endif @@ -174,13 +174,13 @@ ical_unknown_token_handling ical_get_unknown_token_handling_setting(void) { ical_unknown_token_handling myHandling; -#if defined(HAVE_PTHREAD) +#if ICAL_SYNC_MODE == ICAL_SYNC_MODE_PTHREAD pthread_mutex_lock(&unk_token_mutex); #endif myHandling = unknownTokenHandling; -#if defined(HAVE_PTHREAD) +#if ICAL_SYNC_MODE == ICAL_SYNC_MODE_PTHREAD pthread_mutex_unlock(&unk_token_mutex); #endif @@ -189,13 +189,13 @@ ical_unknown_token_handling ical_get_unknown_token_handling_setting(void) void ical_set_unknown_token_handling_setting(ical_unknown_token_handling newSetting) { -#if defined(HAVE_PTHREAD) +#if ICAL_SYNC_MODE == ICAL_SYNC_MODE_PTHREAD pthread_mutex_lock(&unk_token_mutex); #endif unknownTokenHandling = newSetting; -#if defined(HAVE_PTHREAD) +#if ICAL_SYNC_MODE == ICAL_SYNC_MODE_PTHREAD pthread_mutex_unlock(&unk_token_mutex); #endif } diff --git a/src/libical/pvl.c b/src/libical/pvl.c index 8faaa264..0d3e006b 100644 --- a/src/libical/pvl.c +++ b/src/libical/pvl.c @@ -21,7 +21,7 @@ #include /* To mute a ThreadSanitizer claim */ -#if defined(HAVE_PTHREAD) && defined(THREAD_SANITIZER) +#if (ICAL_SYNC_MODE == ICAL_SYNC_MODE_PTHREAD) && defined(THREAD_SANITIZER) #include static pthread_mutex_t pvl_mutex = PTHREAD_MUTEX_INITIALIZER; diff --git a/src/test/builtin_timezones.c b/src/test/builtin_timezones.c index 9271bd33..24b56f6d 100644 --- a/src/test/builtin_timezones.c +++ b/src/test/builtin_timezones.c @@ -12,7 +12,7 @@ #include #endif -#ifdef HAVE_PTHREAD_H +#if ICAL_SYNC_MODE == ICAL_SYNC_MODE_PTHREAD #include #include #endif @@ -21,7 +21,7 @@ #include -#if defined(HAVE_PTHREAD_H) && defined(HAVE_PTHREAD) && defined(HAVE_PTHREAD_CREATE) +#if ICAL_SYNC_MODE == ICAL_SYNC_MODE_PTHREAD #define N_THREADS 20 @@ -82,7 +82,7 @@ int main(void) set_zone_directory("../../zoneinfo"); icaltimezone_set_tzid_prefix("/softwarestudio.org/"); -#if defined(HAVE_PTHREAD_H) && defined(HAVE_PTHREAD) && defined(HAVE_PTHREAD_CREATE) +#if ICAL_SYNC_MODE == ICAL_SYNC_MODE_PTHREAD test_get_component_threadsafety(); #endif diff --git a/src/test/icaltm_test.c b/src/test/icaltm_test.c index 74bea858..e98e7f6a 100644 --- a/src/test/icaltm_test.c +++ b/src/test/icaltm_test.c @@ -16,7 +16,9 @@ #include +#if ICAL_SYNC_MODE != ICAL_SYNC_MODE_THREADLOCAL static icaltimezone *zone, *utc; +#endif static void *test_tread(void *user_data) { @@ -24,6 +26,14 @@ static void *test_tread(void *user_data) int ii; _unused(user_data); + +#if ICAL_SYNC_MODE == ICAL_SYNC_MODE_THREADLOCAL + // In thread-local mode all initialization must be done per thread, so we do it here, rather + // than in main(). + icaltimezone* zone = icaltimezone_get_builtin_timezone("America/New_York"); + icaltimezone* utc = icaltimezone_get_utc_timezone(); +#endif + itt = icaltime_from_string("19710203T040506"); itt.zone = zone; @@ -39,8 +49,12 @@ int main(void) pthread_t thread[2]; int ii; +#if ICAL_SYNC_MODE != ICAL_SYNC_MODE_THREADLOCAL + // In thread-local mode all initialization must be done per thread. + // In all other modes we do the initialization of built in timezones here. zone = icaltimezone_get_builtin_timezone("America/New_York"); utc = icaltimezone_get_utc_timezone(); +#endif for (ii = 0; ii < 2; ii++) { pthread_create(&thread[ii], NULL, test_tread, NULL); -- cgit v1.2.1