thread: Cache the closest timed poller into thread
When we introduce RB tree, getting the closest timed poller is not O(1) but O(log N). To mitigate such delay, cache the closest timed poller into thread, and update the cache when its content is changed. Add unit test cases for this change. They will also clarify the current behavior of spdk_poller_unregister() and spdk_poller_pause() for timed pollers. Signed-off-by: Shuhei Matsumoto <shuhei.matsumoto.xt@hitachi.com> Change-Id: Ibb98a54c261859a3210034038d3953e5c93ef8aa Reviewed-on: https://review.spdk.io/gerrit/c/spdk/spdk/+/7720 Tested-by: SPDK CI Jenkins <sys_sgci@intel.com> Reviewed-by: Jim Harris <james.r.harris@intel.com> Reviewed-by: Konrad Sztyber <konrad.sztyber@intel.com> Reviewed-by: Aleksey Marchuk <alexeymar@mellanox.com> Community-CI: Mellanox Build Bot
This commit is contained in:
parent
4f11fa5b6c
commit
4748ebef40
@ -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;
|
||||
}
|
||||
|
@ -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();
|
||||
|
Loading…
Reference in New Issue
Block a user