From mboxrd@z Thu Jan  1 00:00:00 1970
Return-Path: <dev-bounces@dpdk.org>
Received: from mails.dpdk.org (mails.dpdk.org [217.70.189.124])
	by inbox.dpdk.org (Postfix) with ESMTP id 62C68A0C43;
	Sat,  9 Oct 2021 00:41:24 +0200 (CEST)
Received: from [217.70.189.124] (localhost [127.0.0.1])
	by mails.dpdk.org (Postfix) with ESMTP id 293E741124;
	Sat,  9 Oct 2021 00:40:54 +0200 (CEST)
Received: from linux.microsoft.com (linux.microsoft.com [13.77.154.182])
 by mails.dpdk.org (Postfix) with ESMTP id A0D0E40140
 for <dev@dpdk.org>; Sat,  9 Oct 2021 00:40:47 +0200 (CEST)
Received: by linux.microsoft.com (Postfix, from userid 1059)
 id 5D47020B8966; Fri,  8 Oct 2021 15:40:46 -0700 (PDT)
DKIM-Filter: OpenDKIM Filter v2.11.0 linux.microsoft.com 5D47020B8966
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linux.microsoft.com;
 s=default; t=1633732846;
 bh=xedD//S960RKxQ4b5i1KWsIatJYQdOwP/WZUUqPemTs=;
 h=From:To:Cc:Subject:Date:In-Reply-To:References:From;
 b=Xl6cLcyLyZR035MXbpzQuHZfwJR9bvtb6kqTvtjcm4PMsoIi/qziK4if65ZgGU81p
 mboqv5HR472yIxhT1rCOxxVRrCk0iZG1b77zKo64zMqLNTyWO345GqUmEMrM/bu/AK
 A6uBkj+VCtL2SE/7XTA57z5U6P8+xx2oFWiwGv7Y=
From: Narcisa Ana Maria Vasile <navasile@linux.microsoft.com>
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
Date: Fri,  8 Oct 2021 15:40:41 -0700
Message-Id: <1633732841-17873-10-git-send-email-navasile@linux.microsoft.com>
X-Mailer: git-send-email 1.8.3.1
In-Reply-To: <1633732841-17873-1-git-send-email-navasile@linux.microsoft.com>
References: <1629408694-31803-1-git-send-email-navasile@linux.microsoft.com>
 <1633732841-17873-1-git-send-email-navasile@linux.microsoft.com>
Subject: [dpdk-dev] [PATCH v15 9/9] Add unit tests for thread API
X-BeenThere: dev@dpdk.org
X-Mailman-Version: 2.1.29
Precedence: list
List-Id: DPDK patches and discussions <dev.dpdk.org>
List-Unsubscribe: <https://mails.dpdk.org/options/dev>,
 <mailto:dev-request@dpdk.org?subject=unsubscribe>
List-Archive: <http://mails.dpdk.org/archives/dev/>
List-Post: <mailto:dev@dpdk.org>
List-Help: <mailto:dev-request@dpdk.org?subject=help>
List-Subscribe: <https://mails.dpdk.org/listinfo/dev>,
 <mailto:dev-request@dpdk.org?subject=subscribe>
Errors-To: dev-bounces@dpdk.org
Sender: "dev" <dev-bounces@dpdk.org>

From: Narcisa Vasile <navasile@microsoft.com>

As a new API for threading is introduced,
a set of unit tests have been added to test the new interface.
The tests verify that:
 * mutexes and barriers behave as expected
 * thread properties are applied correctly
 * the thread id is retrieved correctly
 * thread creation/destruction works properly

Signed-off-by: Narcisa Vasile <navasile@microsoft.com>
---
 app/test/meson.build    |   2 +
 app/test/test_threads.c | 359 ++++++++++++++++++++++++++++++++++++++++
 2 files changed, 361 insertions(+)
 create mode 100644 app/test/test_threads.c

diff --git a/app/test/meson.build b/app/test/meson.build
index f144d8b8ed..019c7e27d0 100644
--- a/app/test/meson.build
+++ b/app/test/meson.build
@@ -141,6 +141,7 @@ test_sources = files(
         'test_table_tables.c',
         'test_tailq.c',
         'test_thash.c',
+        'test_threads.c',
         'test_timer.c',
         'test_timer_perf.c',
         'test_timer_racecond.c',
@@ -277,6 +278,7 @@ fast_tests = [
         ['reorder_autotest', true],
         ['service_autotest', true],
         ['thash_autotest', true],
+        ['threads_autotest', true],
         ['trace_autotest', true],
 ]
 
diff --git a/app/test/test_threads.c b/app/test/test_threads.c
new file mode 100644
index 0000000000..722d2689a8
--- /dev/null
+++ b/app/test/test_threads.c
@@ -0,0 +1,359 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2021 Microsoft.
+ */
+
+#include <rte_thread.h>
+#include <rte_debug.h>
+
+#include "test.h"
+
+#define THREADS_COUNT 20
+
+RTE_LOG_REGISTER(threads_logtype_test, test.threads, INFO);
+
+static void *
+thread_loop_self(void *arg)
+{
+	rte_thread_t *id = arg;
+
+	*id = rte_thread_self();
+
+	return NULL;
+}
+
+static int
+test_thread_self(void)
+{
+	rte_thread_t threads_ids[THREADS_COUNT];
+	rte_thread_t self_ids[THREADS_COUNT] = {};
+	int ret;
+	int i;
+
+	for (i = 0; i < THREADS_COUNT; ++i) {
+		ret = rte_thread_create(&threads_ids[i], NULL, thread_loop_self,
+				&self_ids[i]);
+		RTE_TEST_ASSERT(ret == 0, "Failed to create threads!");
+	}
+
+	for (i = 0; i < THREADS_COUNT; ++i) {
+		RTE_TEST_ASSERT(rte_thread_join(threads_ids[i], NULL) == 0, "Failed to join thread!");
+		RTE_TEST_ASSERT_EQUAL(threads_ids[i].opaque_id,
+				self_ids[i].opaque_id, "Unexpected thread id!");
+	}
+
+	return 0;
+}
+
+struct thread_context {
+	rte_thread_barrier *barrier;
+	int barrier_result;
+};
+
+static void *
+thread_loop_barrier(void *arg)
+{
+	struct thread_context *ctx = arg;
+
+	ctx->barrier_result = rte_thread_barrier_wait(ctx->barrier);
+	if (ctx->barrier_result > 0)
+		rte_log(RTE_LOG_DEBUG, threads_logtype_test, "Failed to wait at barrier!");
+
+	return NULL;
+}
+
+static int
+test_thread_barrier(void)
+{
+	rte_thread_t thread_id;
+	struct thread_context ctx;
+	rte_thread_barrier barrier;
+	int ret = 0;
+	int result = 0;
+
+	ret = rte_thread_barrier_init(&barrier, 2);
+	RTE_TEST_ASSERT(ret == 0, "Failed to initialize barrier!");
+
+	ctx.barrier = &barrier;
+	ret = rte_thread_create(&thread_id, NULL, thread_loop_barrier, &ctx);
+	RTE_TEST_ASSERT(ret == 0, "Failed to create thread!");
+
+	result = rte_thread_barrier_wait(&barrier);
+	RTE_TEST_ASSERT(result <= 0, "Failed to wait at the barrier!");
+
+	ret = rte_thread_join(thread_id, NULL);
+	RTE_TEST_ASSERT(ret == 0, "Failed to join threads!");
+
+	ret = rte_thread_barrier_destroy(&barrier);
+	RTE_TEST_ASSERT(ret == 0, "Failed to destroy barrier!");
+
+	RTE_TEST_ASSERT(ctx.barrier_result <= 0, "Child thread failed to wait at the barrier!");
+	RTE_TEST_ASSERT_NOT_EQUAL(ctx.barrier_result, result, "Threads were not blocked at the barrier!");
+
+	return 0;
+}
+
+RTE_STATIC_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);
+}
+
+struct thread_affinity_ctx {
+	size_t idx;
+	unsigned int result;
+};
+
+static void *
+thread_loop_attributes_affinity(void *arg)
+{
+	struct thread_affinity_ctx *ctx = arg;
+	rte_cpuset_t cpuset;
+	size_t i;
+
+	ctx->result = 0;
+
+	CPU_ZERO(&cpuset);
+	if (rte_thread_get_affinity_by_id(rte_thread_self(), &cpuset) != 0) {
+		ctx->result = 1;
+		rte_log(RTE_LOG_DEBUG, threads_logtype_test, "Failed to get thread affinity!");
+		return NULL;
+	}
+
+	if (!CPU_ISSET(ctx->idx, &cpuset)) {
+		ctx->result = 1;
+		rte_log(RTE_LOG_DEBUG, threads_logtype_test, "CPU %zu should be set for thread %zu\n",
+				ctx->idx, ctx->idx);
+		return NULL;
+	}
+
+	for (i = 0; i < CPU_SETSIZE; ++i) {
+		if (i != ctx->idx && CPU_ISSET(i, &cpuset)) {
+			ctx->result = 1;
+			rte_log(RTE_LOG_DEBUG, threads_logtype_test, "CPU %zu should not be set for thread %zu\n",
+					i, ctx->idx);
+			return NULL;
+		}
+	}
+
+	return NULL;
+}
+
+static int
+test_thread_attributes_affinity(void)
+{
+	rte_thread_t threads_ids[THREADS_COUNT];
+	struct thread_affinity_ctx ctx[THREADS_COUNT] = {};
+	rte_thread_attr_t attr;
+	rte_cpuset_t cpuset;
+	size_t i;
+	int ret = 0;
+
+	ret = rte_thread_attr_init(&attr);
+	RTE_TEST_ASSERT(ret == 0, "Failed to initialize thread attributes!");
+
+	for (i = 0; i < THREADS_COUNT; ++i) {
+		CPU_ZERO(&cpuset);
+		CPU_SET(i, &cpuset);
+
+		ret = rte_thread_attr_set_affinity(&attr, &cpuset);
+		RTE_TEST_ASSERT(ret == 0, "Failed to set thread attributes!");
+
+		ctx[i].idx = i;
+		ret = rte_thread_create(&threads_ids[i], &attr,
+				thread_loop_attributes_affinity, &ctx[i]);
+		RTE_TEST_ASSERT(ret == 0, "Failed to create threads!");
+	}
+
+	for (i = 0; i < THREADS_COUNT; ++i) {
+		ret = rte_thread_join(threads_ids[i], NULL);
+		RTE_TEST_ASSERT(ret == 0, "Failed to join threads!");
+
+		RTE_TEST_ASSERT_EQUAL(ctx[i].result, 0, "Unexpected thread affinity!");
+	}
+
+	return ret;
+}
+
+static void *
+thread_loop_return(void *arg)
+{
+	RTE_SET_USED(arg);
+	return NULL;
+}
+
+static int
+test_thread_attributes_priority(void)
+{
+	rte_thread_t threads_ids[THREADS_COUNT];
+	rte_thread_attr_t attr;
+	size_t i;
+	int ret = 0;
+	enum rte_thread_priority priority;
+
+	ret = rte_thread_attr_init(&attr);
+	RTE_TEST_ASSERT(ret == 0, "Failed to initialize  thread attributes!");
+
+	ret = rte_thread_attr_set_priority(&attr, RTE_THREAD_PRIORITY_NORMAL);
+	RTE_TEST_ASSERT(ret == 0, "Failed to set thread priority!");
+
+	for (i = 0; i < THREADS_COUNT; ++i) {
+		ret = rte_thread_create(&threads_ids[i], &attr,
+				thread_loop_return, NULL);
+			RTE_TEST_ASSERT(ret == 0, "Failed to create threads!");
+
+		ret = rte_thread_get_priority(threads_ids[i], &priority);
+		RTE_TEST_ASSERT(ret == 0, "Failed to get thread priority!");
+
+		RTE_TEST_ASSERT_EQUAL(priority, RTE_THREAD_PRIORITY_NORMAL, "Unexpected thread priority!");
+	}
+
+	for (i = 0; i < THREADS_COUNT; ++i) {
+		ret = rte_thread_join(threads_ids[i], NULL);
+		RTE_TEST_ASSERT(ret == 0, "Failed to join threads!");
+	}
+
+	return ret;
+}
+
+static int
+test_thread_detach(void)
+{
+	rte_thread_t threads_ids[THREADS_COUNT];
+	size_t i;
+	int ret = 0;
+
+	for (i = 0; i < THREADS_COUNT; ++i) {
+		ret = rte_thread_create(&threads_ids[i], NULL,
+				thread_loop_return, NULL);
+		RTE_TEST_ASSERT(ret == 0, "Failed to create threads!");
+	}
+
+	for (i = 0; i < THREADS_COUNT; ++i) {
+		ret = rte_thread_detach(threads_ids[i]);
+		RTE_TEST_ASSERT(ret == 0, "Failed to detach thread!");
+	}
+
+	return ret;
+}
+
+static struct unit_test_suite threads_test_suite = {
+	.suite_name = "threads autotest",
+	.setup = NULL,
+	.teardown = NULL,
+	.unit_test_cases = {
+			TEST_CASE(test_thread_self),
+			TEST_CASE(test_thread_barrier),
+			TEST_CASE(test_thread_mutex_static),
+			TEST_CASE(test_thread_mutex_dynamic),
+			TEST_CASE(test_thread_attributes_affinity),
+			TEST_CASE(test_thread_attributes_priority),
+			TEST_CASE(test_thread_detach),
+			TEST_CASES_END()
+	}
+};
+
+static int
+test_threads(void)
+{
+	return unit_test_suite_runner(&threads_test_suite);
+}
+
+REGISTER_TEST_COMMAND(threads_autotest, test_threads);
-- 
2.31.0.vfs.0.1