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 : 0 : }
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, but it is still possible. */
103 [ # # ]: 0 : if (spdk_unlikely(spdk_thread_is_exited(thread))) {
104 : 0 : spdk_thread_destroy(thread);
105 : 0 : } else {
106 [ # # # # ]: 0 : spdk_ring_enqueue(reactor->threads, (void **)&lw_thread, 1, NULL);
107 : : }
108 : 0 : }
109 : 0 : }
110 : :
111 : : static void
112 : 0 : reactor_run_fini(void)
113 : : {
114 : 0 : struct reactor *reactor = &g_main_reactor;
115 : 0 : struct lw_thread *lw_thread;
116 : 0 : struct spdk_thread *thread = NULL;
117 : :
118 : : /* Free all the lightweight threads. */
119 [ # # # # : 0 : while (spdk_ring_dequeue(reactor->threads, (void **)&lw_thread, 1)) {
# # ]
120 : 0 : thread = spdk_thread_get_from_ctx(lw_thread);
121 [ # # # # ]: 0 : assert(thread != NULL);
122 : 0 : spdk_set_thread(thread);
123 : :
124 [ # # ]: 0 : if (spdk_thread_is_exited(thread)) {
125 : 0 : spdk_thread_destroy(thread);
126 : 0 : } else {
127 : : /* This thread is not exited yet, and may need to communicate
128 : : * with other threads to be exited. So mark it as exiting,
129 : : * and check again after traversing other threads. */
130 : 0 : spdk_thread_exit(thread);
131 : 0 : spdk_thread_poll(thread, 0, 0);
132 [ # # # # ]: 0 : spdk_ring_enqueue(reactor->threads, (void **)&lw_thread, 1, NULL);
133 : : }
134 : : }
135 : 0 : }
136 : :
137 : : static int
138 : 0 : schedule_spdk_thread(struct spdk_thread *thread)
139 : : {
140 : : struct reactor *reactor;
141 : 0 : struct lw_thread *lw_thread;
142 : :
143 : 0 : lw_thread = spdk_thread_get_ctx(thread);
144 [ # # # # ]: 0 : assert(lw_thread != NULL);
145 [ # # ]: 0 : memset(lw_thread, 0, sizeof(*lw_thread));
146 : :
147 : : /* Assign lightweight threads to reactor(core). Here we use a mutex.
148 : : * The way the actual SPDK event framework solves this is by using
149 : : * internal rings for messages between reactors. */
150 [ # # ]: 0 : pthread_mutex_lock(&g_mutex);
151 : 0 : reactor = &g_main_reactor;
152 : :
153 [ # # # # ]: 0 : spdk_ring_enqueue(reactor->threads, (void **)&lw_thread, 1, NULL);
154 [ # # ]: 0 : pthread_mutex_unlock(&g_mutex);
155 : :
156 : 0 : return 0;
157 : : }
158 : :
159 : : static int
160 : 0 : reactor_thread_op(struct spdk_thread *thread, enum spdk_thread_op op)
161 : : {
162 [ # # ]: 0 : switch (op) {
163 : 0 : case SPDK_THREAD_OP_NEW:
164 : 0 : return schedule_spdk_thread(thread);
165 : 0 : default:
166 : 0 : return -ENOTSUP;
167 : : }
168 : 0 : }
169 : :
170 : : static bool
171 : 0 : reactor_thread_op_supported(enum spdk_thread_op op)
172 : : {
173 [ # # ]: 0 : switch (op) {
174 : 0 : case SPDK_THREAD_OP_NEW:
175 : 0 : return true;
176 : 0 : default:
177 : 0 : return false;
178 : : }
179 : 0 : }
180 : :
181 : : static int
182 : 0 : init_reactor(void)
183 : : {
184 : : int rc;
185 : 0 : char thread_name[32];
186 : 0 : struct spdk_cpuset cpumask;
187 : 0 : uint32_t main_core = spdk_env_get_current_core();
188 : :
189 [ # # ]: 0 : printf("Initializing thread library.\n");
190 : :
191 : : /* Whenever SPDK creates a new lightweight thread it will call
192 : : * schedule_spdk_thread() asking for the application to begin
193 : : * polling it via spdk_thread_poll(). Each lightweight thread in
194 : : * SPDK optionally allocates extra memory to be used by the application
195 : : * framework. The size of the extra memory allocated is the third parameter. */
196 : 0 : spdk_thread_lib_init_ext(reactor_thread_op, reactor_thread_op_supported,
197 : : sizeof(struct lw_thread), SPDK_DEFAULT_MSG_MEMPOOL_SIZE);
198 : :
199 : 0 : g_main_reactor.core = main_core;
200 : :
201 [ # # ]: 0 : g_main_reactor.threads = spdk_ring_create(SPDK_RING_TYPE_MP_SC, 1024, SPDK_ENV_NUMA_ID_ANY);
202 [ # # # # ]: 0 : if (!g_main_reactor.threads) {
203 [ # # # # ]: 0 : fprintf(stderr, "ERROR: Failed to alloc thread ring!\n");
204 : 0 : rc = -ENOMEM;
205 : 0 : goto err_exit;
206 : : }
207 : :
208 : : /* Spawn an spdk_thread thread on the current core to manage this application. */
209 : 0 : spdk_cpuset_zero(&cpumask);
210 : 0 : spdk_cpuset_set_cpu(&cpumask, main_core, true);
211 [ # # ]: 0 : snprintf(thread_name, sizeof(thread_name), "example_main_thread");
212 : 0 : g_init_thread = spdk_thread_create(thread_name, &cpumask);
213 [ # # ]: 0 : if (!g_init_thread) {
214 [ # # # # ]: 0 : fprintf(stderr, "ERROR: Failed to create SPDK thread!\n");
215 : 0 : return -1;
216 : : }
217 : :
218 [ # # # # ]: 0 : fprintf(stdout, "SPDK threads initialized successfully.\n");
219 : 0 : return 0;
220 : :
221 : 0 : err_exit:
222 : 0 : return rc;
223 : 0 : }
224 : :
225 : : static void
226 : 0 : destroy_threads(void)
227 : : {
228 : 0 : struct reactor *reactor = &g_main_reactor;
229 : :
230 [ # # # # ]: 0 : spdk_ring_free(reactor->threads);
231 : :
232 [ # # ]: 0 : pthread_mutex_destroy(&g_mutex);
233 : 0 : spdk_thread_lib_fini();
234 [ # # ]: 0 : printf("Threads destroyed successfully\n");
235 : 0 : }
236 : :
237 : : static void
238 : 0 : thread_fn(void *ctx)
239 : : {
240 : 0 : struct spdk_thread *thread = ctx;
241 : :
242 [ # # ]: 0 : printf("Hello from new SPDK thread! Thread name: %s\n", spdk_thread_get_name(thread));
243 : 0 : }
244 : :
245 : : static struct spdk_thread *
246 : 0 : register_thread(char *thread_num)
247 : : {
248 : 0 : struct spdk_thread *thread = NULL;
249 : 0 : char thread_name[16] = "example_thread";
250 : 0 : struct spdk_cpuset tmp_cpumask = {};
251 : :
252 [ # # # # ]: 0 : strncat(thread_name, thread_num, 1);
253 : :
254 [ # # ]: 0 : printf("Initializing new SPDK thread: %s\n", thread_name);
255 : :
256 : 0 : spdk_cpuset_zero(&tmp_cpumask);
257 : 0 : spdk_cpuset_set_cpu(&tmp_cpumask, spdk_env_get_first_core(), true);
258 : :
259 : 0 : thread = spdk_thread_create(thread_name, &tmp_cpumask);
260 [ # # # # ]: 0 : assert(thread != NULL);
261 : :
262 : 0 : spdk_thread_send_msg(thread, thread_fn, thread);
263 : :
264 : 0 : return thread;
265 : : }
266 : :
267 : : static int
268 : 0 : create_cb(void *io_device, void *ctx_buf)
269 : : {
270 : 0 : int *ch_count = io_device;
271 : :
272 [ # # ]: 0 : (*ch_count)++;
273 : :
274 [ # # ]: 0 : printf("Hello from IO device register callback!\n");
275 : :
276 : 0 : return 0;
277 : : }
278 : :
279 : : static void
280 : 0 : destroy_cb(void *io_device, void *ctx_buf)
281 : : {
282 : 0 : int *ch_count = io_device;
283 : :
284 [ # # ]: 0 : (*ch_count)--;
285 : :
286 [ # # ]: 0 : printf("Hello from IO device destroy callback!\n");
287 : 0 : }
288 : :
289 : : static void
290 : 0 : app_thread_register_io_device(void *arg)
291 : : {
292 : 0 : struct spdk_io_channel *ch0 = NULL;
293 : :
294 [ # # ]: 0 : printf("Registering a new IO device.\n");
295 : 0 : spdk_io_device_register(&g_io_channel_cnt, create_cb, destroy_cb,
296 : : sizeof(int), NULL);
297 : :
298 : : /* Get a reference pointer to IO channel. */
299 : 0 : ch0 = spdk_get_io_channel(&g_io_channel_cnt);
300 [ # # # # ]: 0 : assert(ch0 != NULL);
301 : : /* Put (away) the reference pointer. */
302 : 0 : spdk_put_io_channel(ch0);
303 : 0 : }
304 : :
305 : : static void
306 : 0 : unregister_cb(void *io_device)
307 : : {
308 : : int *ch_count __attribute__((unused));
309 : :
310 : 0 : ch_count = io_device;
311 [ # # # # : 0 : assert(*ch_count == 0);
# # ]
312 : :
313 [ # # ]: 0 : printf("Hello from IO device unregister callback!\n");
314 : 0 : }
315 : :
316 : : static void
317 : 0 : app_thread_unregister_io_device(void *arg)
318 : : {
319 [ # # ]: 0 : printf("Unregistering IO device...\n");
320 : :
321 : 0 : spdk_io_device_unregister(&g_io_channel_cnt, unregister_cb);
322 : 0 : }
323 : :
324 : : static int
325 : 0 : poller_count(void *arg)
326 : : {
327 : 0 : struct poller_ctx *ctx = arg;
328 : : uint64_t time_diff;
329 : :
330 [ # # ]: 0 : time_diff = (spdk_get_ticks() - g_time_start) / spdk_get_ticks_hz();
331 : :
332 [ # # # # ]: 0 : (*ctx->run_count)++;
333 : :
334 : : /* After POLLING_TIME seconds pass, let the poller unregister itself. */
335 [ # # ]: 0 : if (time_diff >= POLLING_TIME) {
336 : 0 : spdk_poller_unregister(&g_active_poller);
337 : 0 : }
338 : :
339 : 0 : return 0;
340 : : }
341 : :
342 : : static void
343 : 0 : thread1_counting_poller(void *arg)
344 : : {
345 : 0 : struct poller_ctx *ctx = arg;
346 : :
347 [ # # ]: 0 : printf("Registering new active poller...\n");
348 : : /* Register an ACTIVE poller for this SPDK thread.
349 : : * Active poller runs continuously, in other words:
350 : : * it's execution period is set to 0. */
351 : 0 : g_active_poller = SPDK_POLLER_REGISTER(poller_count, ctx, 0);
352 [ # # # # ]: 0 : assert(g_active_poller != NULL);
353 : 0 : }
354 : :
355 : : static int
356 : 0 : poller_print_msg(void *arg)
357 : : {
358 : 0 : struct poller_ctx *ctx = arg;
359 : : uint64_t time_diff;
360 : :
361 [ # # ]: 0 : time_diff = (spdk_get_ticks() - g_time_start) / spdk_get_ticks_hz();
362 [ # # # # ]: 0 : (*ctx->run_count)++;
363 : :
364 [ # # # # ]: 0 : printf("Hello from %s poller! Time elapsed: %ld, Current run count: %ld\n", ctx->poller_type,
365 [ # # # # : 0 : time_diff, *ctx->run_count);
# # ]
366 : :
367 : : /* After POLLING_TIME seconds pass, let the poller unregister itself. */
368 [ # # ]: 0 : if (time_diff >= POLLING_TIME) {
369 : 0 : spdk_poller_unregister(&g_timed_poller);
370 : 0 : }
371 : :
372 : 0 : return 0;
373 : : }
374 : :
375 : : static void
376 : 0 : thread2_printing_poller(void *arg)
377 : : {
378 : 0 : struct poller_ctx *ctx = arg;
379 : :
380 [ # # ]: 0 : printf("Registering new timed poller...\n");
381 : : /* Timed pollers run every set time period defined in microseconds.
382 : : * This one is set to execute every "TIMED_POLLER_PERIOD". */
383 : 0 : g_timed_poller = SPDK_POLLER_REGISTER(poller_print_msg, ctx, TIMED_POLLER_PERIOD);
384 [ # # # # ]: 0 : assert(g_timed_poller != NULL);
385 : 0 : }
386 : :
387 : : static void
388 : 0 : thread_msg_fn(void *arg)
389 : : {
390 : 0 : uint64_t *thread_poll_cnt = arg;
391 : 0 : struct spdk_thread *thread = spdk_get_thread();
392 : :
393 : 0 : (*thread_poll_cnt)++;
394 : :
395 [ # # ]: 0 : printf("Message received by thread: %s, current thread poll count: %ld\n",
396 [ # # ]: 0 : spdk_thread_get_name(thread), *thread_poll_cnt);
397 : 0 : }
398 : :
399 : : static void
400 : 0 : thread_msg_cpl_fn(void *arg)
401 : : {
402 [ # # ]: 0 : printf("Finished iterating over SPDK threads!\n");
403 : 0 : }
404 : :
405 : : static int
406 : 0 : poller_for_each_thread(void *arg)
407 : : {
408 : 0 : struct poller_ctx *ctx = arg;
409 : : uint64_t time_diff;
410 : :
411 [ # # ]: 0 : time_diff = (spdk_get_ticks() - g_time_start) / spdk_get_ticks_hz();
412 [ # # # # ]: 0 : (*ctx->run_count)++;
413 : :
414 [ # # ]: 0 : printf("Calling all threads from %s poller! Time elapsed: %ld, Current run count: %ld\n",
415 [ # # # # : 0 : ctx->poller_type, time_diff, *ctx->run_count);
# # # # #
# ]
416 : :
417 : : /* Send a message to each thread. */
418 : 0 : spdk_for_each_thread(thread_msg_fn, &g_thread_poll_cnt, thread_msg_cpl_fn);
419 : :
420 : : /* After POLLING_TIME seconds pass, let the poller unregister itself. */
421 [ # # ]: 0 : if (time_diff >= POLLING_TIME) {
422 : 0 : spdk_poller_unregister(&g_timed_for_each_thread);
423 : 0 : }
424 : :
425 : 0 : return 0;
426 : : }
427 : :
428 : : static void
429 : 0 : thread2_for_each_thread_poller(void *arg)
430 : : {
431 : 0 : struct poller_ctx *ctx = arg;
432 : :
433 [ # # ]: 0 : printf("Registering new timed poller...\n");
434 : : /* Register a poller to send a message to all available threads via
435 : : * spdk_for_each_thread(). */
436 : 0 : g_timed_for_each_thread = SPDK_POLLER_REGISTER(poller_for_each_thread, ctx, TIMED_POLLER_PERIOD);
437 [ # # # # ]: 0 : assert(g_timed_for_each_thread != NULL);
438 : 0 : }
439 : :
440 : : static void
441 : 0 : io_device_send_msg_fn(struct spdk_io_channel_iter *i)
442 : : {
443 : 0 : struct spdk_io_channel *ch = spdk_io_channel_iter_get_channel(i);
444 : 0 : struct spdk_thread *thread = spdk_io_channel_get_thread(ch);
445 : :
446 [ # # ]: 0 : printf("Iterating over IO channels. Currently on thread: %s and IO device: %s\n",
447 : 0 : spdk_thread_get_name(thread), spdk_io_channel_get_io_device_name(ch));
448 : 0 : spdk_for_each_channel_continue(i, 0);
449 : 0 : }
450 : :
451 : : static void
452 : 0 : io_device_msg_cpl_fn(struct spdk_io_channel_iter *i, int status)
453 : : {
454 [ # # ]: 0 : printf("Completed iterating over IO channels with status: %d.\n", status);
455 : 0 : }
456 : :
457 : : static int
458 : 0 : poller_for_each_channel(void *arg)
459 : : {
460 : 0 : struct poller_ctx *ctx = arg;
461 : : uint64_t time_diff;
462 : :
463 [ # # ]: 0 : time_diff = (spdk_get_ticks() - g_time_start) / spdk_get_ticks_hz();
464 [ # # # # ]: 0 : (*ctx->run_count)++;
465 : :
466 [ # # ]: 0 : printf("Calling all IO channels from %s poller! Time elapsed: %ld, Current run count: %ld\n",
467 [ # # # # : 0 : ctx->poller_type, time_diff, *ctx->run_count);
# # # # #
# ]
468 : :
469 : : /* Send a message to all io devices. */
470 : 0 : spdk_for_each_channel(&g_io_channel_cnt, io_device_send_msg_fn, NULL, io_device_msg_cpl_fn);
471 : :
472 : : /* After POLLING_TIME seconds pass, let the poller unregister itself. */
473 [ # # ]: 0 : if (time_diff >= POLLING_TIME) {
474 : 0 : spdk_poller_unregister(&g_timed_for_each_channel);
475 : 0 : }
476 : :
477 : 0 : return 0;
478 : : }
479 : :
480 : : static void
481 : 0 : thread2_for_each_channel_poller(void *arg)
482 : : {
483 : 0 : struct poller_ctx *ctx = arg;
484 : :
485 [ # # ]: 0 : printf("Registering new timed poller...\n");
486 : : /* Register a poller to send a message to all available IO channels via
487 : : * spdk_for_each_channel(). */
488 : 0 : g_timed_for_each_channel = SPDK_POLLER_REGISTER(poller_for_each_channel, ctx, TIMED_POLLER_PERIOD);
489 [ # # # # ]: 0 : assert(g_timed_for_each_channel != NULL);
490 : 0 : }
491 : :
492 : : int
493 : 0 : main(int argc, char **argv)
494 : : {
495 : : int rc;
496 : 0 : struct spdk_env_opts opts;
497 : : struct spdk_thread *example_thread1, *example_thread2;
498 : 0 : uint64_t time_diff = 0;
499 : 0 : struct poller_ctx ctx_counting, ctx_printing, ctx_for_each_thread, ctx_for_each_channel;
500 : :
501 [ # # ]: 0 : opts.opts_size = sizeof(opts);
502 : 0 : spdk_env_opts_init(&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(&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 : 0 : }
|