Branch data Line data Source code
1 : : /* SPDX-License-Identifier: BSD-3-Clause
2 : : * Copyright (C) 2022 Intel Corporation.
3 : : * All rights reserved.
4 : : */
5 : :
6 : : #include "spdk/stdinc.h"
7 : :
8 : : #include "spdk/env.h"
9 : : #include "spdk/event.h"
10 : : #include "spdk/init.h"
11 : : #include "spdk/string.h"
12 : : #include "spdk/thread.h"
13 : : #include "spdk/bdev.h"
14 : : #include "spdk/rpc.h"
15 : : #include "spdk/likely.h"
16 : :
17 : : #include "spdk_internal/event.h"
18 : : #include "spdk_internal/thread.h"
19 : :
20 : : #define NAME_MAX_LENGTH 256
21 : : #define TIMED_POLLER_PERIOD 1000000
22 : : #define POLLING_TIME 6
23 : : #define MAX_POLLER_TYPE_STR_LEN 100
24 : :
25 : : #define POLLER_TYPE_ACTIVE "active"
26 : : #define POLLER_TYPE_TIMED "timed"
27 : :
28 : : struct lw_thread {
29 : : TAILQ_ENTRY(lw_thread) link;
30 : : bool resched;
31 : : };
32 : :
33 : : struct reactor {
34 : : uint32_t core;
35 : :
36 : : struct spdk_ring *threads;
37 : : TAILQ_ENTRY(reactor) link;
38 : : };
39 : :
40 : : struct poller_ctx {
41 : : char *poller_type;
42 : : uint64_t *run_count;
43 : : };
44 : :
45 : : static struct reactor g_main_reactor;
46 : : static struct spdk_thread *g_init_thread = NULL;
47 : : static pthread_mutex_t g_mutex = PTHREAD_MUTEX_INITIALIZER;
48 : : static uint64_t g_time_start;
49 : : static uint64_t g_counting_poller_counter;
50 : : static uint64_t g_printing_poller_counter;
51 : : static uint64_t g_for_each_thread_poller_counter;
52 : : static uint64_t g_for_each_channel_poller_counter;
53 : : static uint64_t g_thread_poll_cnt;
54 : : static uint64_t g_io_channel_cnt;
55 : : static struct spdk_poller *g_active_poller = NULL, *g_timed_poller = NULL;
56 : : static struct spdk_poller *g_timed_for_each_thread = NULL, *g_timed_for_each_channel = NULL;
57 : :
58 : : static int schedule_spdk_thread(struct spdk_thread *thread);
59 : :
60 : : static void
61 : 0 : usage(char *program_name)
62 : : {
63 [ # # ]: 0 : printf("%s options", program_name);
64 : 0 : printf("\n");
65 [ # # ]: 0 : printf("\t[-h show this usage message]\n");
66 : 0 : }
67 : :
68 : : static int
69 : 0 : parse_args(int argc, char **argv, struct spdk_env_opts *opts)
70 : : {
71 : : int op;
72 : :
73 [ # # # # : 0 : while ((op = getopt(argc, argv, "h")) != -1) {
# # ]
74 [ # # ]: 0 : switch (op) {
75 : 0 : case 'h':
76 : 0 : usage(argv[0]);
77 : 0 : exit(EXIT_SUCCESS);
78 : 0 : default:
79 : 0 : usage(argv[0]);
80 : 0 : return 1;
81 : : }
82 : : }
83 : :
84 : 0 : return 0;
85 : : }
86 : :
87 : : static void
88 : 0 : reactor_run(void)
89 : : {
90 : 0 : struct reactor *reactor = &g_main_reactor;
91 : 0 : struct lw_thread *lw_thread;
92 : 0 : struct spdk_thread *thread = NULL;
93 : :
94 : : /* Run all the SPDK threads in this reactor by FIFO. */
95 [ # # ]: 0 : if (spdk_ring_dequeue(reactor->threads, (void **)&lw_thread, 1)) {
96 : 0 : thread = spdk_thread_get_from_ctx(lw_thread);
97 [ # # ]: 0 : assert(thread != NULL);
98 : :
99 : 0 : spdk_thread_poll(thread, 0, 0);
100 : :
101 : : /* spdk_unlikely() is a branch prediction macro. Here it means the
102 : : * thread should not be exited and idle, but it is still possible. */
103 [ # # # # ]: 0 : if (spdk_unlikely(spdk_thread_is_exited(thread) &&
104 : : spdk_thread_is_idle(thread))) {
105 : 0 : spdk_thread_destroy(thread);
106 : : } else {
107 : 0 : spdk_ring_enqueue(reactor->threads, (void **)&lw_thread, 1, NULL);
108 : : }
109 : : }
110 : 0 : }
111 : :
112 : : static void
113 : 0 : reactor_run_fini(void)
114 : : {
115 : 0 : struct reactor *reactor = &g_main_reactor;
116 : 0 : struct lw_thread *lw_thread;
117 : 0 : struct spdk_thread *thread = NULL;
118 : :
119 : : /* Free all the lightweight threads. */
120 [ # # ]: 0 : while (spdk_ring_dequeue(reactor->threads, (void **)&lw_thread, 1)) {
121 : 0 : thread = spdk_thread_get_from_ctx(lw_thread);
122 [ # # ]: 0 : assert(thread != NULL);
123 : 0 : spdk_set_thread(thread);
124 : :
125 [ # # ]: 0 : if (spdk_thread_is_exited(thread)) {
126 : 0 : spdk_thread_destroy(thread);
127 : : } else {
128 : : /* This thread is not exited yet, and may need to communicate
129 : : * with other threads to be exited. So mark it as exiting,
130 : : * and check again after traversing other threads. */
131 : 0 : spdk_thread_exit(thread);
132 : 0 : spdk_thread_poll(thread, 0, 0);
133 : 0 : spdk_ring_enqueue(reactor->threads, (void **)&lw_thread, 1, NULL);
134 : : }
135 : : }
136 : 0 : }
137 : :
138 : : static int
139 : 0 : schedule_spdk_thread(struct spdk_thread *thread)
140 : : {
141 : : struct reactor *reactor;
142 : 0 : struct lw_thread *lw_thread;
143 : :
144 : 0 : lw_thread = spdk_thread_get_ctx(thread);
145 [ # # ]: 0 : assert(lw_thread != NULL);
146 [ # # ]: 0 : memset(lw_thread, 0, sizeof(*lw_thread));
147 : :
148 : : /* Assign lightweight threads to reactor(core). Here we use a mutex.
149 : : * The way the actual SPDK event framework solves this is by using
150 : : * internal rings for messages between reactors. */
151 [ # # ]: 0 : pthread_mutex_lock(&g_mutex);
152 : 0 : reactor = &g_main_reactor;
153 : :
154 : 0 : spdk_ring_enqueue(reactor->threads, (void **)&lw_thread, 1, NULL);
155 [ # # ]: 0 : pthread_mutex_unlock(&g_mutex);
156 : :
157 : 0 : return 0;
158 : : }
159 : :
160 : : static int
161 : 0 : reactor_thread_op(struct spdk_thread *thread, enum spdk_thread_op op)
162 : : {
163 [ # # ]: 0 : switch (op) {
164 : 0 : case SPDK_THREAD_OP_NEW:
165 : 0 : return schedule_spdk_thread(thread);
166 : 0 : default:
167 : 0 : return -ENOTSUP;
168 : : }
169 : : }
170 : :
171 : : static bool
172 : 0 : reactor_thread_op_supported(enum spdk_thread_op op)
173 : : {
174 [ # # ]: 0 : switch (op) {
175 : 0 : case SPDK_THREAD_OP_NEW:
176 : 0 : return true;
177 : 0 : default:
178 : 0 : return false;
179 : : }
180 : : }
181 : :
182 : : static int
183 : 0 : init_reactor(void)
184 : : {
185 : : int rc;
186 : 0 : char thread_name[32];
187 : 0 : struct spdk_cpuset cpumask;
188 : 0 : uint32_t main_core = spdk_env_get_current_core();
189 : :
190 [ # # ]: 0 : printf("Initializing thread library.\n");
191 : :
192 : : /* Whenever SPDK creates a new lightweight thread it will call
193 : : * schedule_spdk_thread() asking for the application to begin
194 : : * polling it via spdk_thread_poll(). Each lightweight thread in
195 : : * SPDK optionally allocates extra memory to be used by the application
196 : : * framework. The size of the extra memory allocated is the third parameter. */
197 : 0 : spdk_thread_lib_init_ext(reactor_thread_op, reactor_thread_op_supported,
198 : : sizeof(struct lw_thread), SPDK_DEFAULT_MSG_MEMPOOL_SIZE);
199 : :
200 : 0 : g_main_reactor.core = main_core;
201 : :
202 : 0 : g_main_reactor.threads = spdk_ring_create(SPDK_RING_TYPE_MP_SC, 1024, SPDK_ENV_SOCKET_ID_ANY);
203 [ # # ]: 0 : if (!g_main_reactor.threads) {
204 [ # # # # ]: 0 : fprintf(stderr, "ERROR: Failed to alloc thread ring!\n");
205 : 0 : rc = -ENOMEM;
206 : 0 : goto err_exit;
207 : : }
208 : :
209 : : /* Spawn an spdk_thread thread on the current core to manage this application. */
210 : 0 : spdk_cpuset_zero(&cpumask);
211 : 0 : spdk_cpuset_set_cpu(&cpumask, main_core, true);
212 [ # # ]: 0 : snprintf(thread_name, sizeof(thread_name), "example_main_thread");
213 : 0 : g_init_thread = spdk_thread_create(thread_name, &cpumask);
214 [ # # ]: 0 : if (!g_init_thread) {
215 [ # # # # ]: 0 : fprintf(stderr, "ERROR: Failed to create SPDK thread!\n");
216 : 0 : return -1;
217 : : }
218 : :
219 [ # # # # ]: 0 : fprintf(stdout, "SPDK threads initialized successfully.\n");
220 : 0 : return 0;
221 : :
222 : 0 : err_exit:
223 : 0 : return rc;
224 : : }
225 : :
226 : : static void
227 : 0 : destroy_threads(void)
228 : : {
229 : 0 : struct reactor *reactor = &g_main_reactor;
230 : :
231 : 0 : spdk_ring_free(reactor->threads);
232 : :
233 [ # # ]: 0 : pthread_mutex_destroy(&g_mutex);
234 : 0 : spdk_thread_lib_fini();
235 [ # # ]: 0 : printf("Threads destroyed successfully\n");
236 : 0 : }
237 : :
238 : : static void
239 : 0 : thread_fn(void *ctx)
240 : : {
241 : 0 : struct spdk_thread *thread = ctx;
242 : :
243 [ # # ]: 0 : printf("Hello from new SPDK thread! Thread name: %s\n", spdk_thread_get_name(thread));
244 : 0 : }
245 : :
246 : : static struct spdk_thread *
247 : 0 : register_thread(char *thread_num)
248 : : {
249 : 0 : struct spdk_thread *thread = NULL;
250 : 0 : char thread_name[16] = "example_thread";
251 : 0 : struct spdk_cpuset tmp_cpumask = {};
252 : :
253 [ # # # # ]: 0 : strncat(thread_name, thread_num, 1);
254 : :
255 [ # # ]: 0 : printf("Initializing new SPDK thread: %s\n", thread_name);
256 : :
257 : 0 : spdk_cpuset_zero(&tmp_cpumask);
258 : 0 : spdk_cpuset_set_cpu(&tmp_cpumask, spdk_env_get_first_core(), true);
259 : :
260 : 0 : thread = spdk_thread_create(thread_name, &tmp_cpumask);
261 [ # # ]: 0 : assert(thread != NULL);
262 : :
263 : 0 : spdk_thread_send_msg(thread, thread_fn, thread);
264 : :
265 : 0 : return thread;
266 : : }
267 : :
268 : : static int
269 : 0 : create_cb(void *io_device, void *ctx_buf)
270 : : {
271 : 0 : int *ch_count = io_device;
272 : :
273 : 0 : (*ch_count)++;
274 : :
275 [ # # ]: 0 : printf("Hello from IO device register callback!\n");
276 : :
277 : 0 : return 0;
278 : : }
279 : :
280 : : static void
281 : 0 : destroy_cb(void *io_device, void *ctx_buf)
282 : : {
283 : 0 : int *ch_count = io_device;
284 : :
285 : 0 : (*ch_count)--;
286 : :
287 [ # # ]: 0 : printf("Hello from IO device destroy callback!\n");
288 : 0 : }
289 : :
290 : : static void
291 : 0 : app_thread_register_io_device(void *arg)
292 : : {
293 : 0 : struct spdk_io_channel *ch0 = NULL;
294 : :
295 [ # # ]: 0 : printf("Registering a new IO device.\n");
296 : 0 : spdk_io_device_register(&g_io_channel_cnt, create_cb, destroy_cb,
297 : : sizeof(int), NULL);
298 : :
299 : : /* Get a reference pointer to IO channel. */
300 : 0 : ch0 = spdk_get_io_channel(&g_io_channel_cnt);
301 [ # # ]: 0 : assert(ch0 != NULL);
302 : : /* Put (away) the reference pointer. */
303 : 0 : spdk_put_io_channel(ch0);
304 : 0 : }
305 : :
306 : : static void
307 : 0 : unregister_cb(void *io_device)
308 : : {
309 : : int *ch_count __attribute__((unused));
310 : :
311 : 0 : ch_count = io_device;
312 [ # # ]: 0 : assert(*ch_count == 0);
313 : :
314 [ # # ]: 0 : printf("Hello from IO device unregister callback!\n");
315 : 0 : }
316 : :
317 : : static void
318 : 0 : app_thread_unregister_io_device(void *arg)
319 : : {
320 [ # # ]: 0 : printf("Unregistering IO device...\n");
321 : :
322 : 0 : spdk_io_device_unregister(&g_io_channel_cnt, unregister_cb);
323 : 0 : }
324 : :
325 : : static int
326 : 0 : poller_count(void *arg)
327 : : {
328 : 0 : struct poller_ctx *ctx = arg;
329 : : uint64_t time_diff;
330 : :
331 [ # # ]: 0 : time_diff = (spdk_get_ticks() - g_time_start) / spdk_get_ticks_hz();
332 : :
333 : 0 : (*ctx->run_count)++;
334 : :
335 : : /* After POLLING_TIME seconds pass, let the poller unregister itself. */
336 [ # # ]: 0 : if (time_diff >= POLLING_TIME) {
337 : 0 : spdk_poller_unregister(&g_active_poller);
338 : : }
339 : :
340 : 0 : return 0;
341 : : }
342 : :
343 : : static void
344 : 0 : thread1_counting_poller(void *arg)
345 : : {
346 : 0 : struct poller_ctx *ctx = arg;
347 : :
348 [ # # ]: 0 : printf("Registering new active poller...\n");
349 : : /* Register an ACTIVE poller for this SPDK thread.
350 : : * Active poller runs continuously, in other words:
351 : : * it's execution period is set to 0. */
352 : 0 : g_active_poller = SPDK_POLLER_REGISTER(poller_count, ctx, 0);
353 [ # # ]: 0 : assert(g_active_poller != NULL);
354 : 0 : }
355 : :
356 : : static int
357 : 0 : poller_print_msg(void *arg)
358 : : {
359 : 0 : struct poller_ctx *ctx = arg;
360 : : uint64_t time_diff;
361 : :
362 [ # # ]: 0 : time_diff = (spdk_get_ticks() - g_time_start) / spdk_get_ticks_hz();
363 : 0 : (*ctx->run_count)++;
364 : :
365 [ # # ]: 0 : printf("Hello from %s poller! Time elapsed: %ld, Current run count: %ld\n", ctx->poller_type,
366 : 0 : time_diff, *ctx->run_count);
367 : :
368 : : /* After POLLING_TIME seconds pass, let the poller unregister itself. */
369 [ # # ]: 0 : if (time_diff >= POLLING_TIME) {
370 : 0 : spdk_poller_unregister(&g_timed_poller);
371 : : }
372 : :
373 : 0 : return 0;
374 : : }
375 : :
376 : : static void
377 : 0 : thread2_printing_poller(void *arg)
378 : : {
379 : 0 : struct poller_ctx *ctx = arg;
380 : :
381 [ # # ]: 0 : printf("Registering new timed poller...\n");
382 : : /* Timed pollers run every set time period defined in microseconds.
383 : : * This one is set to execute every "TIMED_POLLER_PERIOD". */
384 : 0 : g_timed_poller = SPDK_POLLER_REGISTER(poller_print_msg, ctx, TIMED_POLLER_PERIOD);
385 [ # # ]: 0 : assert(g_timed_poller != NULL);
386 : 0 : }
387 : :
388 : : static void
389 : 0 : thread_msg_fn(void *arg)
390 : : {
391 : 0 : uint64_t *thread_poll_cnt = arg;
392 : 0 : struct spdk_thread *thread = spdk_get_thread();
393 : :
394 : 0 : (*thread_poll_cnt)++;
395 : :
396 [ # # ]: 0 : printf("Message received by thread: %s, current thread poll count: %ld\n",
397 : : spdk_thread_get_name(thread), *thread_poll_cnt);
398 : 0 : }
399 : :
400 : : static void
401 : 0 : thread_msg_cpl_fn(void *arg)
402 : : {
403 [ # # ]: 0 : printf("Finished iterating over SPDK threads!\n");
404 : 0 : }
405 : :
406 : : static int
407 : 0 : poller_for_each_thread(void *arg)
408 : : {
409 : 0 : struct poller_ctx *ctx = arg;
410 : : uint64_t time_diff;
411 : :
412 [ # # ]: 0 : time_diff = (spdk_get_ticks() - g_time_start) / spdk_get_ticks_hz();
413 : 0 : (*ctx->run_count)++;
414 : :
415 [ # # ]: 0 : printf("Calling all threads from %s poller! Time elapsed: %ld, Current run count: %ld\n",
416 : 0 : ctx->poller_type, time_diff, *ctx->run_count);
417 : :
418 : : /* Send a message to each thread. */
419 : 0 : spdk_for_each_thread(thread_msg_fn, &g_thread_poll_cnt, thread_msg_cpl_fn);
420 : :
421 : : /* After POLLING_TIME seconds pass, let the poller unregister itself. */
422 [ # # ]: 0 : if (time_diff >= POLLING_TIME) {
423 : 0 : spdk_poller_unregister(&g_timed_for_each_thread);
424 : : }
425 : :
426 : 0 : return 0;
427 : : }
428 : :
429 : : static void
430 : 0 : thread2_for_each_thread_poller(void *arg)
431 : : {
432 : 0 : struct poller_ctx *ctx = arg;
433 : :
434 [ # # ]: 0 : printf("Registering new timed poller...\n");
435 : : /* Register a poller to send a message to all available threads via
436 : : * spdk_for_each_thread(). */
437 : 0 : g_timed_for_each_thread = SPDK_POLLER_REGISTER(poller_for_each_thread, ctx, TIMED_POLLER_PERIOD);
438 [ # # ]: 0 : assert(g_timed_for_each_thread != NULL);
439 : 0 : }
440 : :
441 : : static void
442 : 0 : io_device_send_msg_fn(struct spdk_io_channel_iter *i)
443 : : {
444 : 0 : struct spdk_io_channel *ch = spdk_io_channel_iter_get_channel(i);
445 : 0 : struct spdk_thread *thread = spdk_io_channel_get_thread(ch);
446 : :
447 [ # # ]: 0 : printf("Iterating over IO channels. Currently on thread: %s and IO device: %s\n",
448 : : spdk_thread_get_name(thread), spdk_io_channel_get_io_device_name(ch));
449 : 0 : spdk_for_each_channel_continue(i, 0);
450 : 0 : }
451 : :
452 : : static void
453 : 0 : io_device_msg_cpl_fn(struct spdk_io_channel_iter *i, int status)
454 : : {
455 [ # # ]: 0 : printf("Completed iterating over IO channels with status: %d.\n", status);
456 : 0 : }
457 : :
458 : : static int
459 : 0 : poller_for_each_channel(void *arg)
460 : : {
461 : 0 : struct poller_ctx *ctx = arg;
462 : : uint64_t time_diff;
463 : :
464 [ # # ]: 0 : time_diff = (spdk_get_ticks() - g_time_start) / spdk_get_ticks_hz();
465 : 0 : (*ctx->run_count)++;
466 : :
467 [ # # ]: 0 : printf("Calling all IO channels from %s poller! Time elapsed: %ld, Current run count: %ld\n",
468 : 0 : ctx->poller_type, time_diff, *ctx->run_count);
469 : :
470 : : /* Send a message to all io devices. */
471 : 0 : spdk_for_each_channel(&g_io_channel_cnt, io_device_send_msg_fn, NULL, io_device_msg_cpl_fn);
472 : :
473 : : /* After POLLING_TIME seconds pass, let the poller unregister itself. */
474 [ # # ]: 0 : if (time_diff >= POLLING_TIME) {
475 : 0 : spdk_poller_unregister(&g_timed_for_each_channel);
476 : : }
477 : :
478 : 0 : return 0;
479 : : }
480 : :
481 : : static void
482 : 0 : thread2_for_each_channel_poller(void *arg)
483 : : {
484 : 0 : struct poller_ctx *ctx = arg;
485 : :
486 [ # # ]: 0 : printf("Registering new timed poller...\n");
487 : : /* Register a poller to send a message to all available IO channels via
488 : : * spdk_for_each_channel(). */
489 : 0 : g_timed_for_each_channel = SPDK_POLLER_REGISTER(poller_for_each_channel, ctx, TIMED_POLLER_PERIOD);
490 [ # # ]: 0 : assert(g_timed_for_each_channel != NULL);
491 : 0 : }
492 : :
493 : : int
494 : 0 : main(int argc, char **argv)
495 : : {
496 : : int rc;
497 : 0 : struct spdk_env_opts opts;
498 : : struct spdk_thread *example_thread1, *example_thread2;
499 : 0 : uint64_t time_diff = 0;
500 : 0 : struct poller_ctx ctx_counting, ctx_printing, ctx_for_each_thread, ctx_for_each_channel;
501 : :
502 : 0 : spdk_env_opts_init_ext(&opts, sizeof(opts));
503 : 0 : opts.name = "thread-example";
504 : 0 : opts.core_mask = "0x1";
505 : :
506 : 0 : rc = parse_args(argc, argv, &opts);
507 [ # # ]: 0 : if (rc != 0) {
508 [ # # # # ]: 0 : fprintf(stderr, "ERROR: Unable to parse program args! Code: %d\n", rc);
509 : 0 : return rc;
510 : : }
511 : :
512 [ # # ]: 0 : if (spdk_env_init_ext(&opts) < 0) {
513 [ # # # # ]: 0 : fprintf(stderr, "ERROR: Unable to initialize SPDK env!\n");
514 : 0 : return -EINVAL;
515 : : }
516 : :
517 : : /* Initialize a reactor and an SPDK thread to manage the application. */
518 : 0 : rc = init_reactor();
519 [ # # ]: 0 : if (rc != 0) {
520 [ # # # # ]: 0 : fprintf(stderr, "ERROR: Unable to initialize reactor! Code: %d\n", rc);
521 : 0 : return rc;
522 : : }
523 : :
524 : : /* Get a time reference to print elapsed time in poller functions. */
525 : 0 : g_time_start = spdk_get_ticks();
526 : :
527 : : /* Register a mock IO device on app_thread (main application thread). */
528 : 0 : spdk_thread_send_msg(spdk_thread_get_app_thread(), app_thread_register_io_device, NULL);
529 : :
530 : : /* Register two new SPDK threads. */
531 : 0 : example_thread1 = register_thread("1");
532 : 0 : example_thread2 = register_thread("2");
533 : :
534 : : /* Signal the first thread to register and execute an ACTIVE poller, which will run as often as possible. */
535 : 0 : ctx_counting.poller_type = POLLER_TYPE_ACTIVE;
536 : 0 : ctx_counting.run_count = &g_counting_poller_counter;
537 : 0 : spdk_thread_send_msg(example_thread1, thread1_counting_poller, &ctx_counting);
538 : :
539 : : /* Signal the second thread to register and execute TIMED pollers, which will run periodically. */
540 : 0 : ctx_printing.poller_type = POLLER_TYPE_TIMED;
541 : 0 : ctx_printing.run_count = &g_printing_poller_counter;
542 : 0 : spdk_thread_send_msg(example_thread2, thread2_printing_poller, &ctx_printing);
543 : :
544 : 0 : ctx_for_each_thread.poller_type = POLLER_TYPE_TIMED;
545 : 0 : ctx_for_each_thread.run_count = &g_for_each_thread_poller_counter;
546 : 0 : spdk_thread_send_msg(example_thread2, thread2_for_each_thread_poller, &ctx_for_each_thread);
547 : :
548 : 0 : ctx_for_each_channel.poller_type = POLLER_TYPE_TIMED;
549 : 0 : ctx_for_each_channel.run_count = &g_for_each_channel_poller_counter;
550 : 0 : spdk_thread_send_msg(example_thread2, thread2_for_each_channel_poller, &ctx_for_each_channel);
551 : :
552 : : /* Poll SPDK threads and IO devices for POLLING_TIME + 1 seconds - to avoid a race
553 : : * between all the pollers and IO device unregistering, let below while loop
554 : : * poll for one second longer than all the pollers. */
555 [ # # ]: 0 : while (time_diff < POLLING_TIME + 1) {
556 [ # # ]: 0 : time_diff = (spdk_get_ticks() - g_time_start) / spdk_get_ticks_hz();
557 : 0 : reactor_run();
558 : : }
559 : :
560 [ # # ]: 0 : printf("ACTIVE (counting) poller ran %lu times.\n", g_counting_poller_counter);
561 [ # # ]: 0 : printf("TIMED (printing) poller ran %lu times.\n", g_printing_poller_counter);
562 [ # # ]: 0 : printf("TIMED (for each thread) poller ran %lu times.\n", g_for_each_thread_poller_counter);
563 [ # # ]: 0 : printf("TIMED (for each channel) poller ran %lu times.\n", g_for_each_channel_poller_counter);
564 : :
565 : : /* Unregister the mock IO device. */
566 : 0 : spdk_thread_send_msg(spdk_thread_get_app_thread(), app_thread_unregister_io_device, NULL);
567 : :
568 : : /* Disable the reactor and free all SPDK threads. */
569 : 0 : reactor_run_fini();
570 : 0 : destroy_threads();
571 : :
572 : : /* Stop SPDK environment. */
573 : 0 : spdk_env_fini();
574 : :
575 : 0 : return 0;
576 : : }
|