Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 261ce7a3 authored by Lais Andrade's avatar Lais Andrade
Browse files

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
parent 18f8c58c
Loading
Loading
Loading
Loading
+8 −3
Original line number Original line Diff line number Diff line
@@ -29,8 +29,11 @@ bool DelayedCallback::isExpired() const {
    return mExpiration <= std::chrono::steady_clock::now();
    return mExpiration <= std::chrono::steady_clock::now();
}
}


DelayedCallback::Timestamp DelayedCallback::getExpiration() const {
std::chrono::milliseconds DelayedCallback::getWaitForExpirationDuration() const {
    return mExpiration;
    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 {
void DelayedCallback::run() const {
@@ -88,7 +91,9 @@ void CallbackScheduler::loop() {
            mCondition.wait(mMutex);
            mCondition.wait(mMutex);
        } else {
        } else {
            // Wait until next callback expires, or a new one is scheduled.
            // 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());
        }
        }
    }
    }
}
}
+4 −4
Original line number Original line Diff line number Diff line
@@ -30,15 +30,13 @@ namespace vibrator {
// Wrapper for a callback to be executed after a delay.
// Wrapper for a callback to be executed after a delay.
class DelayedCallback {
class DelayedCallback {
public:
public:
    using Timestamp = std::chrono::time_point<std::chrono::steady_clock>;

    DelayedCallback(std::function<void()> callback, std::chrono::milliseconds delay)
    DelayedCallback(std::function<void()> callback, std::chrono::milliseconds delay)
          : mCallback(callback), mExpiration(std::chrono::steady_clock::now() + delay) {}
          : mCallback(callback), mExpiration(std::chrono::steady_clock::now() + delay) {}
    ~DelayedCallback() = default;
    ~DelayedCallback() = default;


    void run() const;
    void run() const;
    bool isExpired() const;
    bool isExpired() const;
    Timestamp getExpiration() const;
    std::chrono::milliseconds getWaitForExpirationDuration() const;


    // Compare by expiration time, where A < B when A expires first.
    // Compare by expiration time, where A < B when A expires first.
    bool operator<(const DelayedCallback& other) const;
    bool operator<(const DelayedCallback& other) const;
@@ -46,7 +44,9 @@ public:


private:
private:
    std::function<void()> mCallback;
    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.
// Schedules callbacks to be executed after a delay.
+9 −3
Original line number Original line Diff line number Diff line
@@ -71,15 +71,21 @@ protected:
    }
    }


    int32_t waitForCallbacks(int32_t callbackCount, milliseconds timeout) {
    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;
        int32_t expiredCallbackCount = 0;
        while (steady_clock::now() < expiration) {
        while (steady_clock::now() < expirationTime) {
            std::lock_guard<std::mutex> lock(mMutex);
            std::lock_guard<std::mutex> lock(mMutex);
            expiredCallbackCount = mExpiredCallbacks.size();
            expiredCallbackCount = mExpiredCallbacks.size();
            if (callbackCount <= expiredCallbackCount) {
            if (callbackCount <= expiredCallbackCount) {
                return 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;
        return expiredCallbackCount;
    }
    }