blob: b2b1988d97cd08c14167cccd5eac0686b6a36bd9 [file] [log] [blame]
Lais Andrade10d9dc72020-05-20 12:00:49 +00001/*
2 * Copyright (C) 2020 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include <chrono>
18#include <thread>
19
20#include <vibratorservice/VibratorCallbackScheduler.h>
21
22namespace android {
23
24namespace vibrator {
25
26// -------------------------------------------------------------------------------------------------
27
28bool DelayedCallback::isExpired() const {
29 return mExpiration <= std::chrono::steady_clock::now();
30}
31
Lais Andrade261ce7a2023-08-16 15:30:04 +010032std::chrono::milliseconds DelayedCallback::getWaitForExpirationDuration() const {
33 std::chrono::milliseconds delta = std::chrono::duration_cast<std::chrono::milliseconds>(
34 mExpiration - std::chrono::steady_clock::now());
35 // Return zero if this is already expired.
36 return delta > delta.zero() ? delta : delta.zero();
Lais Andrade10d9dc72020-05-20 12:00:49 +000037}
38
39void DelayedCallback::run() const {
40 mCallback();
41}
42
43bool DelayedCallback::operator<(const DelayedCallback& other) const {
44 return mExpiration < other.mExpiration;
45}
46
47bool DelayedCallback::operator>(const DelayedCallback& other) const {
48 return mExpiration > other.mExpiration;
49}
50
51// -------------------------------------------------------------------------------------------------
52
53CallbackScheduler::~CallbackScheduler() {
54 {
55 std::lock_guard<std::mutex> lock(mMutex);
56 mFinished = true;
57 }
58 mCondition.notify_all();
59 if (mCallbackThread && mCallbackThread->joinable()) {
60 mCallbackThread->join();
61 }
62}
63
64void CallbackScheduler::schedule(std::function<void()> callback, std::chrono::milliseconds delay) {
65 {
66 std::lock_guard<std::mutex> lock(mMutex);
67 if (mCallbackThread == nullptr) {
68 mCallbackThread = std::make_unique<std::thread>(&CallbackScheduler::loop, this);
69 }
70 mQueue.emplace(DelayedCallback(callback, delay));
71 }
72 mCondition.notify_all();
73}
74
75void CallbackScheduler::loop() {
76 while (true) {
Colin Cross0d5e1292020-08-27 04:12:26 +000077 std::unique_lock<std::mutex> lock(mMutex);
Lais Andrade10d9dc72020-05-20 12:00:49 +000078 if (mFinished) {
79 // Destructor was called, so let the callback thread die.
80 break;
81 }
82 while (!mQueue.empty() && mQueue.top().isExpired()) {
Colin Cross0d5e1292020-08-27 04:12:26 +000083 DelayedCallback callback = mQueue.top();
Lais Andrade10d9dc72020-05-20 12:00:49 +000084 mQueue.pop();
Colin Cross0d5e1292020-08-27 04:12:26 +000085 lock.unlock();
86 callback.run();
87 lock.lock();
Lais Andrade10d9dc72020-05-20 12:00:49 +000088 }
89 if (mQueue.empty()) {
Lais Andradec8069852024-04-26 14:57:13 +010090 // Wait until a new callback is scheduled or destructor was called.
91 mCondition.wait(lock, [this] { return mFinished || !mQueue.empty(); });
Lais Andrade10d9dc72020-05-20 12:00:49 +000092 } else {
Lais Andradec8069852024-04-26 14:57:13 +010093 // Wait until next callback expires or a new one is scheduled.
Lais Andrade261ce7a2023-08-16 15:30:04 +010094 // Use the monotonic steady clock to wait for the measured delay interval via wait_for
95 // instead of using a wall clock via wait_until.
Lais Andradec8069852024-04-26 14:57:13 +010096 mCondition.wait_for(lock, mQueue.top().getWaitForExpirationDuration());
Lais Andrade10d9dc72020-05-20 12:00:49 +000097 }
98 }
99}
100
101// -------------------------------------------------------------------------------------------------
102
103}; // namespace vibrator
104
105}; // namespace android