From f76456b0dc14fb7c5fa55f815512b2ed07df4706 Mon Sep 17 00:00:00 2001 From: Andre Pereira Azevedo Pinto Date: Wed, 4 Dec 2019 17:56:54 -0800 Subject: [PATCH] Add support for priority inheritance Add support for posix mutex priority inheritance. This is important to avoid priority inversion in systems running with threads with different priorities. Signed-off-by: Andre Azevedo --- evthread_pthread.c | 23 +++++++++++++++++++++-- include/event2/thread.h | 15 +++++++++++++++ test/regress.h | 1 + test/regress_main.c | 8 +++++++- test/regress_thread.c | 3 +++ 5 files changed, 47 insertions(+), 3 deletions(-) diff --git a/evthread_pthread.c b/evthread_pthread.c index 4e11f749..a40f5b02 100644 --- a/evthread_pthread.c +++ b/evthread_pthread.c @@ -39,12 +39,13 @@ struct event_base; #include "mm-internal.h" #include "evthread-internal.h" +static pthread_mutexattr_t attr_default; static pthread_mutexattr_t attr_recursive; static void * evthread_posix_lock_alloc(unsigned locktype) { - pthread_mutexattr_t *attr = NULL; + pthread_mutexattr_t *attr = &attr_default; pthread_mutex_t *lock = mm_malloc(sizeof(pthread_mutex_t)); if (!lock) return NULL; @@ -161,7 +162,7 @@ evthread_posix_cond_wait(void *cond_, void *lock_, const struct timeval *tv) } int -evthread_use_pthreads(void) +evthread_use_pthreads_with_flags(int flags) { struct evthread_lock_callbacks cbs = { EVTHREAD_LOCK_API_VERSION, @@ -178,14 +179,32 @@ evthread_use_pthreads(void) evthread_posix_cond_signal, evthread_posix_cond_wait }; + + if (pthread_mutexattr_init(&attr_default)) + return -1; + /* Set ourselves up to get recursive locks. */ if (pthread_mutexattr_init(&attr_recursive)) return -1; if (pthread_mutexattr_settype(&attr_recursive, PTHREAD_MUTEX_RECURSIVE)) return -1; + if (flags & EVTHREAD_PTHREAD_PRIO_INHERIT) { + /* Set up priority inheritance */ + if (pthread_mutexattr_setprotocol(&attr_default, PTHREAD_PRIO_INHERIT)) + return -1; + if (pthread_mutexattr_setprotocol(&attr_recursive, PTHREAD_PRIO_INHERIT)) + return -1; + } + evthread_set_lock_callbacks(&cbs); evthread_set_condition_callbacks(&cond_cbs); evthread_set_id_callback(evthread_posix_get_id); return 0; } + +int +evthread_use_pthreads(void) +{ + return evthread_use_pthreads_with_flags(0); +} diff --git a/include/event2/thread.h b/include/event2/thread.h index fa1b09c0..bf06e3d3 100644 --- a/include/event2/thread.h +++ b/include/event2/thread.h @@ -212,6 +212,21 @@ int evthread_use_windows_threads(void); @return 0 on success, -1 on failure. */ EVENT2_EXPORT_SYMBOL int evthread_use_pthreads(void); + +/* Enables posix mutex priority inheritance. */ +#define EVTHREAD_PTHREAD_PRIO_INHERIT 0x01 + +/** + * Sets up Libevent for use with Pthreads locking and thread ID functions. + * Use evthred_use_pthreads_with_flags() to use Pthreads locking, taking the + * specified flags under consideration. + * + * @param flags the flags to apply when setting up Pthreads locking. @see EVTHREAD_PTHREAD_* + * @return 0 on success, -1 on failure. + **/ +EVENT2_EXPORT_SYMBOL +int evthread_use_pthreads_with_flags(int flags); + /** Defined if Libevent was built with support for evthread_use_pthreads() */ #define EVTHREAD_USE_PTHREADS_IMPLEMENTED 1 diff --git a/test/regress.h b/test/regress.h index dad80664..55a2fddb 100644 --- a/test/regress.h +++ b/test/regress.h @@ -97,6 +97,7 @@ extern int libevent_tests_running_in_debug_mode; #define TT_ENABLE_IOCP_FLAG (TT_FIRST_USER_FLAG<<6) #define TT_ENABLE_IOCP (TT_ENABLE_IOCP_FLAG|TT_NEED_THREADS) #define TT_ENABLE_DEBUG_MODE (TT_ENABLE_IOCP_FLAG<<7) +#define TT_ENABLE_PRIORITY_INHERITANCE (TT_ENABLE_IOCP_FLAG<<8) /* All the flags that a legacy test needs. */ #define TT_ISOLATED TT_FORK|TT_NEED_SOCKETPAIR|TT_NEED_BASE diff --git a/test/regress_main.c b/test/regress_main.c index c5bc1ae6..1b6ede7e 100644 --- a/test/regress_main.c +++ b/test/regress_main.c @@ -193,6 +193,12 @@ basic_test_setup(const struct testcase_t *testcase) evutil_socket_t spair[2] = { -1, -1 }; struct basic_test_data *data = NULL; +#if defined(EVTHREAD_USE_PTHREADS_IMPLEMENTED) + int evthread_flags = 0; + if (testcase->flags & TT_ENABLE_PRIORITY_INHERITANCE) + evthread_flags |= EVTHREAD_PTHREAD_PRIO_INHERIT; +#endif + #ifndef _WIN32 if (testcase->flags & TT_ENABLE_IOCP_FLAG) return (void*)TT_SKIP; @@ -208,7 +214,7 @@ basic_test_setup(const struct testcase_t *testcase) if (!(testcase->flags & TT_FORK)) return NULL; #if defined(EVTHREAD_USE_PTHREADS_IMPLEMENTED) - if (evthread_use_pthreads()) + if (evthread_use_pthreads_with_flags(evthread_flags)) exit(1); #elif defined(EVTHREAD_USE_WINDOWS_THREADS_IMPLEMENTED) if (evthread_use_windows_threads()) diff --git a/test/regress_thread.c b/test/regress_thread.c index 1e0ce41f..3f46764d 100644 --- a/test/regress_thread.c +++ b/test/regress_thread.c @@ -574,6 +574,9 @@ struct testcase_t thread_testcases[] = { #ifndef _WIN32 { "forking", thread_basic, TT_FORK|TT_NEED_THREADS|TT_NEED_BASE, &basic_setup, (char*)"forking" }, + { "priority_inheritance", thread_basic, + TT_FORK|TT_NEED_THREADS|TT_NEED_BASE|TT_ENABLE_PRIORITY_INHERITANCE, + &basic_setup, (char*)"priority_inheritance" }, #endif TEST(conditions_simple, TT_RETRIABLE), { "deferred_cb_skew", thread_deferred_cb_skew, -- 2.40.0