Fix flaky VibratorCallbackSchedulerTest
The test is sometimes timing out while asserting the scheduled callbacks
are triggered within the required test timeout.
Update CallbackScheduler to use std::condition_variable_any::wait_for
with calculated durations from using std::chrono::steady_clock, instead
of relying on the timestamps via wait_until.
Update the VibratorCallbackSchedulerTest to also use wait_for.
Bug: 293603710
Test: VibratorCallbackSchedulerTest
Change-Id: Ie84147a9ff686d666d8525a35914571f32d72719
diff --git a/services/vibratorservice/VibratorCallbackScheduler.cpp b/services/vibratorservice/VibratorCallbackScheduler.cpp
index f2870b0..7eda9ef 100644
--- a/services/vibratorservice/VibratorCallbackScheduler.cpp
+++ b/services/vibratorservice/VibratorCallbackScheduler.cpp
@@ -29,8 +29,11 @@
return mExpiration <= std::chrono::steady_clock::now();
}
-DelayedCallback::Timestamp DelayedCallback::getExpiration() const {
- return mExpiration;
+std::chrono::milliseconds DelayedCallback::getWaitForExpirationDuration() const {
+ std::chrono::milliseconds delta = std::chrono::duration_cast<std::chrono::milliseconds>(
+ mExpiration - std::chrono::steady_clock::now());
+ // Return zero if this is already expired.
+ return delta > delta.zero() ? delta : delta.zero();
}
void DelayedCallback::run() const {
@@ -88,7 +91,9 @@
mCondition.wait(mMutex);
} else {
// Wait until next callback expires, or a new one is scheduled.
- mCondition.wait_until(mMutex, mQueue.top().getExpiration());
+ // Use the monotonic steady clock to wait for the measured delay interval via wait_for
+ // instead of using a wall clock via wait_until.
+ mCondition.wait_for(mMutex, mQueue.top().getWaitForExpirationDuration());
}
}
}
diff --git a/services/vibratorservice/include/vibratorservice/VibratorCallbackScheduler.h b/services/vibratorservice/include/vibratorservice/VibratorCallbackScheduler.h
index 2c194b5..c8ec15d 100644
--- a/services/vibratorservice/include/vibratorservice/VibratorCallbackScheduler.h
+++ b/services/vibratorservice/include/vibratorservice/VibratorCallbackScheduler.h
@@ -30,15 +30,13 @@
// Wrapper for a callback to be executed after a delay.
class DelayedCallback {
public:
- using Timestamp = std::chrono::time_point<std::chrono::steady_clock>;
-
DelayedCallback(std::function<void()> callback, std::chrono::milliseconds delay)
: mCallback(callback), mExpiration(std::chrono::steady_clock::now() + delay) {}
~DelayedCallback() = default;
void run() const;
bool isExpired() const;
- Timestamp getExpiration() const;
+ std::chrono::milliseconds getWaitForExpirationDuration() const;
// Compare by expiration time, where A < B when A expires first.
bool operator<(const DelayedCallback& other) const;
@@ -46,7 +44,9 @@
private:
std::function<void()> mCallback;
- Timestamp mExpiration;
+ // Use a steady monotonic clock to calculate the duration until expiration.
+ // This clock is not related to wall clock time and is most suitable for measuring intervals.
+ std::chrono::time_point<std::chrono::steady_clock> mExpiration;
};
// Schedules callbacks to be executed after a delay.
diff --git a/services/vibratorservice/test/VibratorCallbackSchedulerTest.cpp b/services/vibratorservice/test/VibratorCallbackSchedulerTest.cpp
index 106ab9e..426cd42 100644
--- a/services/vibratorservice/test/VibratorCallbackSchedulerTest.cpp
+++ b/services/vibratorservice/test/VibratorCallbackSchedulerTest.cpp
@@ -71,15 +71,21 @@
}
int32_t waitForCallbacks(int32_t callbackCount, milliseconds timeout) {
- time_point<steady_clock> expiration = steady_clock::now() + timeout + TEST_TIMEOUT;
+ time_point<steady_clock> expirationTime = steady_clock::now() + timeout + TEST_TIMEOUT;
int32_t expiredCallbackCount = 0;
- while (steady_clock::now() < expiration) {
+ while (steady_clock::now() < expirationTime) {
std::lock_guard<std::mutex> lock(mMutex);
expiredCallbackCount = mExpiredCallbacks.size();
if (callbackCount <= expiredCallbackCount) {
return expiredCallbackCount;
}
- mCondition.wait_until(mMutex, expiration);
+ auto currentTimeout = std::chrono::duration_cast<std::chrono::milliseconds>(
+ expirationTime - steady_clock::now());
+ if (currentTimeout > currentTimeout.zero()) {
+ // Use the monotonic steady clock to wait for the requested timeout via wait_for
+ // instead of using a wall clock via wait_until.
+ mCondition.wait_for(mMutex, currentTimeout);
+ }
}
return expiredCallbackCount;
}