diff --git a/lib/thread/thread.c b/lib/thread/thread.c index 8184cc2f47..3ba3c09c94 100644 --- a/lib/thread/thread.c +++ b/lib/thread/thread.c @@ -123,6 +123,7 @@ struct spdk_thread { * Contains pollers running on this thread with a periodic timer. */ TAILQ_HEAD(timed_pollers_head, spdk_poller) timed_pollers; + struct spdk_poller *first_timed_poller; /* * Contains paused pollers. Pollers on this queue are waiting until * they are resumed (in which case they're put onto the active/timer @@ -676,12 +677,18 @@ poller_insert_timer(struct spdk_thread *thread, struct spdk_poller *poller, uint /* No earlier pollers were found, so this poller must be the new head */ TAILQ_INSERT_HEAD(&thread->timed_pollers, poller, tailq); + + thread->first_timed_poller = poller; } static inline void poller_remove_timer(struct spdk_thread *thread, struct spdk_poller *poller) { TAILQ_REMOVE(&thread->timed_pollers, poller, tailq); + + if (thread->first_timed_poller == poller) { + thread->first_timed_poller = TAILQ_FIRST(&thread->timed_pollers); + } } static void @@ -852,7 +859,7 @@ thread_poll(struct spdk_thread *thread, uint32_t max_msgs, uint64_t now) } } - poller = TAILQ_FIRST(&thread->timed_pollers); + poller = thread->first_timed_poller; while (poller != NULL) { int timer_rc = 0; @@ -927,7 +934,7 @@ spdk_thread_next_poller_expiration(struct spdk_thread *thread) { struct spdk_poller *poller; - poller = TAILQ_FIRST(&thread->timed_pollers); + poller = thread->first_timed_poller; if (poller) { return poller->next_run_tick; } diff --git a/test/unit/lib/thread/thread.c/thread_ut.c b/test/unit/lib/thread/thread.c/thread_ut.c index 11e3bf3c98..8cc6a6207f 100644 --- a/test/unit/lib/thread/thread.c/thread_ut.c +++ b/test/unit/lib/thread/thread.c/thread_ut.c @@ -1332,6 +1332,106 @@ device_unregister_and_thread_exit_race(void) free_threads(); } +static int +dummy_poller(void *arg) +{ + return SPDK_POLLER_IDLE; +} + +static void +cache_closest_timed_poller(void) +{ + struct spdk_thread *thread; + struct spdk_poller *poller1, *poller2, *poller3, *tmp; + + allocate_threads(1); + set_thread(0); + + thread = spdk_get_thread(); + SPDK_CU_ASSERT_FATAL(thread != NULL); + + poller1 = spdk_poller_register(dummy_poller, NULL, 1000); + SPDK_CU_ASSERT_FATAL(poller1 != NULL); + + poller2 = spdk_poller_register(dummy_poller, NULL, 1500); + SPDK_CU_ASSERT_FATAL(poller2 != NULL); + + poller3 = spdk_poller_register(dummy_poller, NULL, 1800); + SPDK_CU_ASSERT_FATAL(poller3 != NULL); + + poll_threads(); + + /* When multiple timed pollers are inserted, the cache should + * have the closest timed poller. + */ + CU_ASSERT(thread->first_timed_poller == poller1); + CU_ASSERT(TAILQ_FIRST(&thread->timed_pollers) == poller1); + + spdk_delay_us(1000); + poll_threads(); + + CU_ASSERT(thread->first_timed_poller == poller2); + CU_ASSERT(TAILQ_FIRST(&thread->timed_pollers) == poller2); + + /* If we unregister a timed poller by spdk_poller_unregister() + * when it is waiting, it is marked as being unregistereed and + * is actually unregistered when it is expired. + * + * Hence if we unregister the closest timed poller when it is waiting, + * the cache is not updated to the next timed poller until it is expired. + */ + tmp = poller2; + + spdk_poller_unregister(&poller2); + CU_ASSERT(poller2 == NULL); + + spdk_delay_us(499); + poll_threads(); + + CU_ASSERT(thread->first_timed_poller == tmp); + CU_ASSERT(TAILQ_FIRST(&thread->timed_pollers) == tmp); + + spdk_delay_us(1); + poll_threads(); + + CU_ASSERT(thread->first_timed_poller == poller3); + CU_ASSERT(TAILQ_FIRST(&thread->timed_pollers) == poller3); + + /* If we pause a timed poller by spdk_poller_pause() when it is waiting, + * it is marked as being paused and is actually paused when it is expired. + * + * Hence if we pause the closest timed poller when it is waiting, the cache + * is not updated to the next timed poller until it is expired. + */ + spdk_poller_pause(poller3); + + spdk_delay_us(299); + poll_threads(); + + CU_ASSERT(thread->first_timed_poller == poller3); + CU_ASSERT(TAILQ_FIRST(&thread->timed_pollers) == poller3); + + spdk_delay_us(1); + poll_threads(); + + CU_ASSERT(thread->first_timed_poller == poller1); + CU_ASSERT(TAILQ_FIRST(&thread->timed_pollers) == poller1); + + /* After unregistering all timed pollers, the cache should + * be NULL. + */ + spdk_poller_unregister(&poller1); + spdk_poller_unregister(&poller3); + + spdk_delay_us(200); + poll_threads(); + + CU_ASSERT(thread->first_timed_poller == NULL); + CU_ASSERT(TAILQ_EMPTY(&thread->timed_pollers)); + + free_threads(); +} + int main(int argc, char **argv) { @@ -1357,6 +1457,7 @@ main(int argc, char **argv) CU_ADD_TEST(suite, thread_update_stats_test); CU_ADD_TEST(suite, nested_channel); CU_ADD_TEST(suite, device_unregister_and_thread_exit_race); + CU_ADD_TEST(suite, cache_closest_timed_poller); CU_basic_set_mode(CU_BRM_VERBOSE); CU_basic_run_tests();