xref: /spdk/test/common/lib/ut_multithread.c (revision f8abbede89d30584d2a4f8427b13896f8591b873)
1 /*   SPDX-License-Identifier: BSD-3-Clause
2  *   Copyright (C) 2017 Intel Corporation.
3  *   All rights reserved.
4  */
5 
6 #include "spdk_internal/cunit.h"
7 #include "spdk/thread.h"
8 #include "spdk_internal/mock.h"
9 
10 #include "common/lib/test_env.c"
11 
12 static uint32_t g_ut_num_threads;
13 
14 int allocate_threads(int num_threads);
15 void free_threads(void);
16 void poll_threads(void);
17 bool poll_thread(uintptr_t thread_id);
18 bool poll_thread_times(uintptr_t thread_id, uint32_t max_polls);
19 
20 struct ut_msg {
21 	spdk_msg_fn		fn;
22 	void			*ctx;
23 	TAILQ_ENTRY(ut_msg)	link;
24 };
25 
26 struct ut_thread {
27 	struct spdk_thread	*thread;
28 	struct spdk_io_channel	*ch;
29 };
30 
31 struct ut_thread *g_ut_threads;
32 
33 #define INVALID_THREAD 0x1000
34 
35 static uint64_t g_ut_thread_id = INVALID_THREAD;
36 
37 static void
38 set_thread(uintptr_t thread_id)
39 {
40 	g_ut_thread_id = thread_id;
41 	if (thread_id == INVALID_THREAD) {
42 		spdk_set_thread(NULL);
43 	} else {
44 		spdk_set_thread(g_ut_threads[thread_id].thread);
45 	}
46 
47 }
48 
49 int
50 allocate_threads(int num_threads)
51 {
52 	struct spdk_thread *thread;
53 	uint32_t i;
54 
55 	spdk_thread_lib_init(NULL, 0);
56 
57 	g_ut_num_threads = num_threads;
58 
59 	g_ut_threads = calloc(num_threads, sizeof(*g_ut_threads));
60 	assert(g_ut_threads != NULL);
61 
62 	for (i = 0; i < g_ut_num_threads; i++) {
63 		set_thread(i);
64 		thread = spdk_thread_create(NULL, NULL);
65 		assert(thread != NULL);
66 		g_ut_threads[i].thread = thread;
67 	}
68 
69 	set_thread(INVALID_THREAD);
70 	return 0;
71 }
72 
73 void
74 free_threads(void)
75 {
76 	uint32_t i, num_threads;
77 	struct spdk_thread *thread;
78 
79 	for (i = 0; i < g_ut_num_threads; i++) {
80 		set_thread(i);
81 		thread = g_ut_threads[i].thread;
82 		spdk_thread_exit(thread);
83 	}
84 
85 	num_threads = g_ut_num_threads;
86 
87 	while (num_threads != 0) {
88 		for (i = 0; i < g_ut_num_threads; i++) {
89 			set_thread(i);
90 			thread = g_ut_threads[i].thread;
91 			if (thread == NULL) {
92 				continue;
93 			}
94 
95 			if (spdk_thread_is_exited(thread)) {
96 				g_ut_threads[i].thread = NULL;
97 				num_threads--;
98 				spdk_thread_destroy(thread);
99 			} else {
100 				spdk_thread_poll(thread, 0, 0);
101 			}
102 		}
103 	}
104 
105 	g_ut_num_threads = 0;
106 	free(g_ut_threads);
107 	g_ut_threads = NULL;
108 
109 	spdk_thread_lib_fini();
110 }
111 
112 bool
113 poll_thread_times(uintptr_t thread_id, uint32_t max_polls)
114 {
115 	bool busy = false;
116 	struct ut_thread *thread = &g_ut_threads[thread_id];
117 	uintptr_t original_thread_id;
118 	uint32_t polls_executed = 0;
119 	uint64_t now;
120 
121 	if (max_polls == 0) {
122 		/* If max_polls is set to 0,
123 		 * poll until no operation is pending. */
124 		return poll_thread(thread_id);
125 	}
126 	assert(thread_id != (uintptr_t)INVALID_THREAD);
127 	assert(thread_id < g_ut_num_threads);
128 
129 	original_thread_id = g_ut_thread_id;
130 	set_thread(INVALID_THREAD);
131 
132 	now = spdk_get_ticks();
133 	while (polls_executed < max_polls) {
134 		if (spdk_thread_poll(thread->thread, 1, now) > 0) {
135 			busy = true;
136 		}
137 		now = spdk_thread_get_last_tsc(thread->thread);
138 		polls_executed++;
139 	}
140 
141 	set_thread(original_thread_id);
142 
143 	return busy;
144 }
145 
146 bool
147 poll_thread(uintptr_t thread_id)
148 {
149 	bool busy = false;
150 	struct ut_thread *thread = &g_ut_threads[thread_id];
151 	uintptr_t original_thread_id;
152 	uint64_t now;
153 
154 	assert(thread_id != (uintptr_t)INVALID_THREAD);
155 	assert(thread_id < g_ut_num_threads);
156 
157 	original_thread_id = g_ut_thread_id;
158 	set_thread(INVALID_THREAD);
159 
160 	now = spdk_get_ticks();
161 	while (spdk_thread_poll(thread->thread, 0, now) > 0) {
162 		now = spdk_thread_get_last_tsc(thread->thread);
163 		busy = true;
164 	}
165 
166 	set_thread(original_thread_id);
167 
168 	return busy;
169 }
170 
171 void
172 poll_threads(void)
173 {
174 	while (true) {
175 		bool busy = false;
176 
177 		for (uint32_t i = 0; i < g_ut_num_threads; i++) {
178 			busy = busy || poll_thread(i);
179 		}
180 
181 		if (!busy) {
182 			break;
183 		}
184 	}
185 }
186