From patchwork Thu Nov 11 01:33:45 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Narcisa Ana Maria Vasile X-Patchwork-Id: 104141 X-Patchwork-Delegate: david.marchand@redhat.com Return-Path: X-Original-To: patchwork@inbox.dpdk.org Delivered-To: patchwork@inbox.dpdk.org Received: from mails.dpdk.org (mails.dpdk.org [217.70.189.124]) by inbox.dpdk.org (Postfix) with ESMTP id 590D9A0C4B; Thu, 11 Nov 2021 02:35:21 +0100 (CET) Received: from [217.70.189.124] (localhost [127.0.0.1]) by mails.dpdk.org (Postfix) with ESMTP id C55C441158; Thu, 11 Nov 2021 02:35:00 +0100 (CET) Received: from linux.microsoft.com (linux.microsoft.com [13.77.154.182]) by mails.dpdk.org (Postfix) with ESMTP id 3149D40DF5 for ; Thu, 11 Nov 2021 02:34:54 +0100 (CET) Received: by linux.microsoft.com (Postfix, from userid 1059) id ED54220C358B; Wed, 10 Nov 2021 17:34:52 -0800 (PST) DKIM-Filter: OpenDKIM Filter v2.11.0 linux.microsoft.com ED54220C358B DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linux.microsoft.com; s=default; t=1636594492; bh=arzBqOCEVzJfUYyLX/tPn4p8iFMgMhzQpz4cg0+/aeM=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=iN/Ukq9f74e2G25t9jNpb6N+9Qyblq995TKw2xrAVkAGcIqzQLev2+jAS7exSV/V3 6ij0JOr2AWppnrP2hMEVNaFYvgGpNmewIuCCa1jnKG8NnlbluhT/7z+V0h07Xyb5Jy 8nBbEnS2X52ZZg7F/fKDeu8eqJazpzpvRugibSCQ= From: Narcisa Ana Maria Vasile To: dev@dpdk.org, thomas@monjalon.net, dmitry.kozliuk@gmail.com, khot@microsoft.com, navasile@microsoft.com, dmitrym@microsoft.com, roretzla@microsoft.com, talshn@nvidia.com, ocardona@microsoft.com Cc: bruce.richardson@intel.com, david.marchand@redhat.com, pallavi.kadam@intel.com Subject: [PATCH v18 8/8] eal: implement functions for mutex management Date: Wed, 10 Nov 2021 17:33:45 -0800 Message-Id: <1636594425-9692-9-git-send-email-navasile@linux.microsoft.com> X-Mailer: git-send-email 1.8.3.1 In-Reply-To: <1636594425-9692-1-git-send-email-navasile@linux.microsoft.com> References: <1636513302-7359-1-git-send-email-navasile@linux.microsoft.com> <1636594425-9692-1-git-send-email-navasile@linux.microsoft.com> X-BeenThere: dev@dpdk.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: DPDK patches and discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dev-bounces@dpdk.org From: Narcisa Vasile Add functions for mutex init, destroy, lock, unlock, trylock. Windows does not have a static initializer. Initialization is only done through InitializeCriticalSection(). To overcome this, RTE_INIT_MUTEX macro is added to replace static initialization of mutexes. The macro calls rte_thread_mutex_init(). Add unit tests to verify that the mutex correctly locks/unlocks and protects the data. Check both static and dynamic mutexes. Signed-off-by: Narcisa Vasile --- app/test/test_threads.c | 106 +++++++++++++++++++++++++++++++++++ lib/eal/common/rte_thread.c | 69 +++++++++++++++++++++++ lib/eal/include/rte_thread.h | 85 ++++++++++++++++++++++++++++ lib/eal/version.map | 5 ++ lib/eal/windows/rte_thread.c | 64 +++++++++++++++++++++ 5 files changed, 329 insertions(+) diff --git a/app/test/test_threads.c b/app/test/test_threads.c index 00f604ab7e..91155a04e3 100644 --- a/app/test/test_threads.c +++ b/app/test/test_threads.c @@ -243,6 +243,110 @@ test_thread_barrier(void) return 0; } +RTE_INIT_MUTEX(static_mutex); + +struct mutex_loop_args { + rte_thread_barrier *barrier; + rte_thread_mutex *mutex; + unsigned long result_A; + unsigned long result_B; +}; + +static void * +thread_loop_mutex_B(void *arg) +{ + struct mutex_loop_args *args = arg; + + if (rte_thread_mutex_try_lock(args->mutex) == 0) { + rte_thread_barrier_wait(args->barrier); + rte_thread_mutex_unlock(args->mutex); + args->result_B = 1; + } else { + rte_thread_barrier_wait(args->barrier); + args->result_B = 2; + } + + return NULL; +} + +static void * +thread_loop_mutex_A(void *arg) +{ + struct mutex_loop_args *args = arg; + + if (rte_thread_mutex_try_lock(args->mutex) != 0) { + rte_thread_barrier_wait(args->barrier); + args->result_A = 2; + } else { + rte_thread_barrier_wait(args->barrier); + rte_thread_mutex_unlock(args->mutex); + args->result_A = 1; + } + + return NULL; +} + +static int +test_thread_mutex(rte_thread_mutex *pmutex) +{ + rte_thread_t thread_A; + rte_thread_t thread_B; + rte_thread_mutex mutex; + rte_thread_barrier barrier; + struct mutex_loop_args args; + int ret = 0; + + /* If mutex is not statically initialized */ + if (pmutex == NULL) { + ret = rte_thread_mutex_init(&mutex); + RTE_TEST_ASSERT(ret == 0, "Failed to initialize mutex!"); + } else + mutex = *pmutex; + + ret = rte_thread_barrier_init(&barrier, 2); + RTE_TEST_ASSERT(ret == 0, "Failed to initialize barrier!"); + + args.mutex = &mutex; + args.barrier = &barrier; + + ret = rte_thread_create(&thread_A, NULL, thread_loop_mutex_A, &args); + RTE_TEST_ASSERT(ret == 0, "Failed to create thread!"); + + ret = rte_thread_create(&thread_B, NULL, thread_loop_mutex_B, &args); + RTE_TEST_ASSERT(ret == 0, "Failed to create thread!"); + + ret = rte_thread_join(thread_A, NULL); + RTE_TEST_ASSERT(ret == 0, "Failed to join thread!"); + + ret = rte_thread_join(thread_B, NULL); + RTE_TEST_ASSERT(ret == 0, "Failed to join thread!"); + + RTE_TEST_ASSERT(args.result_A != args.result_B, "Mutex failed to be acquired or was acquired by both threads!"); + + /* Destroy if dynamically initialized */ + if (pmutex == NULL) { + ret = rte_thread_mutex_destroy(&mutex); + RTE_TEST_ASSERT(ret == 0, "Failed to destroy mutex!"); + } + + ret = rte_thread_barrier_destroy(&barrier); + RTE_TEST_ASSERT(ret == 0, "Failed to destroy barrier!"); + + return ret; +} + +static int +test_thread_mutex_static(void) +{ + return test_thread_mutex(&static_mutex); +} + +static int +test_thread_mutex_dynamic(void) +{ + return test_thread_mutex(NULL); +} + static struct unit_test_suite threads_test_suite = { .suite_name = "threads autotest", .setup = NULL, @@ -253,6 +357,8 @@ static struct unit_test_suite threads_test_suite = { TEST_CASE(test_thread_attributes_priority), TEST_CASE(test_thread_detach), TEST_CASE(test_thread_barrier), + TEST_CASE(test_thread_mutex_static), + TEST_CASE(test_thread_mutex_dynamic), TEST_CASES_END() } }; diff --git a/lib/eal/common/rte_thread.c b/lib/eal/common/rte_thread.c index d30a8a7ca3..4a9a1b6e07 100644 --- a/lib/eal/common/rte_thread.c +++ b/lib/eal/common/rte_thread.c @@ -309,6 +309,75 @@ rte_thread_detach(rte_thread_t thread_id) return pthread_detach((pthread_t)thread_id.opaque_id); } +int +rte_thread_mutex_init(rte_thread_mutex *mutex) +{ + int ret = 0; + pthread_mutex_t *m = NULL; + + RTE_VERIFY(mutex != NULL); + + m = calloc(1, sizeof(*m)); + if (m == NULL) { + RTE_LOG(DEBUG, EAL, "Unable to initialize mutex. Insufficient memory!\n"); + ret = ENOMEM; + goto cleanup; + } + + ret = pthread_mutex_init(m, NULL); + if (ret != 0) { + RTE_LOG(DEBUG, EAL, "Failed to init mutex. ret = %d\n", ret); + goto cleanup; + } + + mutex->mutex_id = m; + m = NULL; + +cleanup: + free(m); + return ret; +} + +int +rte_thread_mutex_lock(rte_thread_mutex *mutex) +{ + RTE_VERIFY(mutex != NULL); + + return pthread_mutex_lock((pthread_mutex_t *)mutex->mutex_id); +} + +int +rte_thread_mutex_unlock(rte_thread_mutex *mutex) +{ + RTE_VERIFY(mutex != NULL); + + return pthread_mutex_unlock((pthread_mutex_t *)mutex->mutex_id); +} + +int +rte_thread_mutex_try_lock(rte_thread_mutex *mutex) +{ + RTE_VERIFY(mutex != NULL); + + return pthread_mutex_trylock((pthread_mutex_t *)mutex->mutex_id); +} + +int +rte_thread_mutex_destroy(rte_thread_mutex *mutex) +{ + int ret = 0; + RTE_VERIFY(mutex != NULL); + + ret = pthread_mutex_destroy((pthread_mutex_t *)mutex->mutex_id); + if (ret != 0) + RTE_LOG(DEBUG, EAL, "Unable to destroy mutex, ret = %d\n", ret); + + free(mutex->mutex_id); + mutex->mutex_id = NULL; + + return ret; +} + int rte_thread_barrier_init(rte_thread_barrier *barrier, int count) { diff --git a/lib/eal/include/rte_thread.h b/lib/eal/include/rte_thread.h index 7c84e32988..09a5fd8add 100644 --- a/lib/eal/include/rte_thread.h +++ b/lib/eal/include/rte_thread.h @@ -54,6 +54,25 @@ typedef struct { #endif /* RTE_HAS_CPUSET */ +#define RTE_DECLARE_MUTEX(private_lock) rte_thread_mutex private_lock + +#define RTE_DEFINE_MUTEX(private_lock)\ +RTE_INIT(__rte_ ## private_lock ## _init)\ +{\ + RTE_VERIFY(rte_thread_mutex_init(&private_lock) == 0);\ +} + +#define RTE_INIT_MUTEX(private_lock)\ +static RTE_DECLARE_MUTEX(private_lock);\ +RTE_DEFINE_MUTEX(private_lock) + +/** + * Thread mutex representation. + */ +typedef struct rte_thread_mutex_tag { + void *mutex_id; /**< mutex identifier */ +} rte_thread_mutex; + /** * Returned by rte_thread_barrier_wait() when call is successful. */ @@ -314,6 +333,72 @@ __rte_experimental int rte_thread_set_priority(rte_thread_t thread_id, enum rte_thread_priority priority); +/** + * Initializes a mutex. + * + * @param mutex + * The mutex to be initialized. + * + * @return + * On success, return 0. + * On failure, return a positive errno-style error number. + */ +__rte_experimental +int rte_thread_mutex_init(rte_thread_mutex *mutex); + +/** + * Locks a mutex. + * + * @param mutex + * The mutex to be locked. + * + * @return + * On success, return 0. + * On failure, return a positive errno-style error number. + */ +__rte_experimental +int rte_thread_mutex_lock(rte_thread_mutex *mutex); + +/** + * Unlocks a mutex. + * + * @param mutex + * The mutex to be unlocked. + * + * @return + * On success, return 0. + * On failure, return a positive errno-style error number. + */ +__rte_experimental +int rte_thread_mutex_unlock(rte_thread_mutex *mutex); + +/** + * Tries to lock a mutex.If the mutex is already held by a different thread, + * the function returns without blocking. + * + * @param mutex + * The mutex that will be acquired, if not already locked. + * + * @return + * On success, if the mutex is acquired, return 0. + * On failure, return a positive errno-style error number. + */ +__rte_experimental +int rte_thread_mutex_try_lock(rte_thread_mutex *mutex); + +/** + * Releases all resources associated with a mutex. + * + * @param mutex + * The mutex to be uninitialized. + * + * @return + * On success, return 0. + * On failure, return a positive errno-style error number. + */ +__rte_experimental +int rte_thread_mutex_destroy(rte_thread_mutex *mutex); + /** * Initializes a synchronization barrier. * diff --git a/lib/eal/version.map b/lib/eal/version.map index 06e5f82da2..e80eea4316 100644 --- a/lib/eal/version.map +++ b/lib/eal/version.map @@ -431,6 +431,11 @@ EXPERIMENTAL { rte_thread_barrier_wait; rte_thread_barrier_destroy; rte_thread_get_affinity_by_id; + rte_thread_mutex_init; + rte_thread_mutex_lock; + rte_thread_mutex_unlock; + rte_thread_mutex_try_lock; + rte_thread_mutex_destroy; rte_thread_set_affinity_by_id; rte_thread_get_priority; rte_thread_set_priority; diff --git a/lib/eal/windows/rte_thread.c b/lib/eal/windows/rte_thread.c index 3f72bbf716..11b4863fe8 100644 --- a/lib/eal/windows/rte_thread.c +++ b/lib/eal/windows/rte_thread.c @@ -504,6 +504,70 @@ rte_thread_detach(rte_thread_t thread_id) return 0; } +int +rte_thread_mutex_init(rte_thread_mutex *mutex) +{ + int ret = 0; + CRITICAL_SECTION *m = NULL; + + RTE_VERIFY(mutex != NULL); + + m = calloc(1, sizeof(*m)); + if (m == NULL) { + RTE_LOG(DEBUG, EAL, "Unable to initialize mutex. Insufficient memory!\n"); + ret = ENOMEM; + goto cleanup; + } + + InitializeCriticalSection(m); + mutex->mutex_id = m; + m = NULL; + +cleanup: + return ret; +} + +int +rte_thread_mutex_lock(rte_thread_mutex *mutex) +{ + RTE_VERIFY(mutex != NULL); + + EnterCriticalSection(mutex->mutex_id); + return 0; +} + +int +rte_thread_mutex_unlock(rte_thread_mutex *mutex) +{ + RTE_VERIFY(mutex != NULL); + + LeaveCriticalSection(mutex->mutex_id); + return 0; +} + +int +rte_thread_mutex_try_lock(rte_thread_mutex *mutex) +{ + RTE_VERIFY(mutex != NULL); + + if (TryEnterCriticalSection(mutex->mutex_id) != 0) + return 0; + + return EBUSY; +} + +int +rte_thread_mutex_destroy(rte_thread_mutex *mutex) +{ + RTE_VERIFY(mutex != NULL); + + DeleteCriticalSection(mutex->mutex_id); + free(mutex->mutex_id); + mutex->mutex_id = NULL; + + return 0; +} + int rte_thread_barrier_init(rte_thread_barrier *barrier, int count) {