the AsyncScheduler uses an asio timer to dispatch work to its executor
with an optional delay. when no delay is requested, it waits on the
timer with an expiration time in the past (crimson::dmclock::TimeZero)
tests are failing here because poll() is returning without executing the
handlers of those expired timers
asio implements these timers with timerfd and epoll. debugging with
strace, i see that these timers armed with timerfd_settime() are not
always immediately ready according to epoll_wait():
eventfd2(0, EFD_CLOEXEC|EFD_NONBLOCK) = 3
epoll_create1(EPOLL_CLOEXEC) = 4
timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC) = 5
epoll_ctl(4, EPOLL_CTL_ADD, 3, {events=EPOLLIN|EPOLLERR|EPOLLET, data={u32=
14164052, u64=
14164052}}) = 0
epoll_ctl(4, EPOLL_CTL_ADD, 5, {events=EPOLLIN|EPOLLERR, data={u32=
14164064, u64=
14164064}}) = 0
timerfd_settime(5, TFD_TIMER_ABSTIME, {it_interval={tv_sec=0, tv_nsec=0}, it_value={tv_sec=0, tv_nsec=1}}, {it_interval={tv_sec=0, tv_nsec=0}, it_value={tv_sec=0, tv_nsec=0}}) = 0
epoll_wait(4, [{events=EPOLLIN, data={u32=
14164052, u64=
14164052}}], 128, 0) = 1
epoll_wait(4, [], 128, 0) = 0
epoll_wait(4, [], 128, 0) = 0
epoll_wait(4, [], 128, 0) = 0
epoll_wait(4, [], 128, 0) = 0
epoll_wait(4, [{events=EPOLLIN, data={u32=
14164064, u64=
14164064}}], 128, 0) = 1
in this example, it took 6 calls to context.poll() before it was ready
to execute the timer's handler
to work around this, replace calls to context.poll() with calls to
context.run_for() with a very short duration
Fixes: https://tracker.ceph.com/issues/42788
Signed-off-by: Casey Bodley <cbodley@redhat.com>
EXPECT_EQ(1u, counters(client_id::admin)->get(queue_counters::l_qlen));
EXPECT_EQ(1u, counters(client_id::auth)->get(queue_counters::l_qlen));
- context.poll();
+ context.run_for(std::chrono::milliseconds(1));
EXPECT_TRUE(context.stopped());
ASSERT_TRUE(ec1);
EXPECT_EQ(1u, counters(client_id::admin)->get(queue_counters::l_qlen));
EXPECT_EQ(1u, counters(client_id::auth)->get(queue_counters::l_qlen));
- context.poll();
+ context.run_for(std::chrono::milliseconds(1));
EXPECT_TRUE(context.stopped());
ASSERT_TRUE(ec1);
EXPECT_FALSE(ec1);
EXPECT_FALSE(ec2);
- context.poll();
+ context.run_for(std::chrono::milliseconds(1));
EXPECT_TRUE(context.stopped());
ASSERT_TRUE(ec1);
EXPECT_FALSE(ec1);
EXPECT_FALSE(ec2);
- context.poll();
+ context.run_for(std::chrono::milliseconds(1));
EXPECT_TRUE(context.stopped());
ASSERT_TRUE(ec1);
EXPECT_FALSE(ec1);
EXPECT_FALSE(ec2);
- context.poll();
+ context.run_for(std::chrono::milliseconds(1));
EXPECT_TRUE(context.stopped());
ASSERT_TRUE(ec1);
EXPECT_EQ(1u, counters(client_id::admin)->get(queue_counters::l_qlen));
EXPECT_EQ(1u, counters(client_id::auth)->get(queue_counters::l_qlen));
- callback_context.poll();
+ callback_context.run_for(std::chrono::milliseconds(1));
// maintains work on callback executor while in queue
EXPECT_FALSE(callback_context.stopped());
EXPECT_FALSE(ec1);
EXPECT_FALSE(ec2);
- queue_context.poll();
+ queue_context.run_for(std::chrono::milliseconds(1));
EXPECT_TRUE(queue_context.stopped());
EXPECT_FALSE(ec1); // no callbacks until callback executor runs
EXPECT_FALSE(ec2);
- callback_context.poll();
+ callback_context.run_for(std::chrono::milliseconds(1));
EXPECT_TRUE(callback_context.stopped());
ASSERT_TRUE(ec1);
EXPECT_EQ(PhaseType::priority, p2);
});
- context.poll();
+ context.run_for(std::chrono::milliseconds(1));
EXPECT_TRUE(context.stopped());
}