1 /*
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
22 namespace android {
23
24 namespace vibrator {
25
26 // -------------------------------------------------------------------------------------------------
27
isExpired() const28 bool DelayedCallback::isExpired() const {
29 return mExpiration <= std::chrono::steady_clock::now();
30 }
31
getWaitForExpirationDuration() const32 std::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();
37 }
38
run() const39 void DelayedCallback::run() const {
40 mCallback();
41 }
42
operator <(const DelayedCallback & other) const43 bool DelayedCallback::operator<(const DelayedCallback& other) const {
44 return mExpiration < other.mExpiration;
45 }
46
operator >(const DelayedCallback & other) const47 bool DelayedCallback::operator>(const DelayedCallback& other) const {
48 return mExpiration > other.mExpiration;
49 }
50
51 // -------------------------------------------------------------------------------------------------
52
~CallbackScheduler()53 CallbackScheduler::~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
schedule(std::function<void ()> callback,std::chrono::milliseconds delay)64 void 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
loop()75 void CallbackScheduler::loop() {
76 while (true) {
77 std::unique_lock<std::mutex> lock(mMutex);
78 if (mFinished) {
79 // Destructor was called, so let the callback thread die.
80 break;
81 }
82 while (!mQueue.empty() && mQueue.top().isExpired()) {
83 DelayedCallback callback = mQueue.top();
84 mQueue.pop();
85 lock.unlock();
86 callback.run();
87 lock.lock();
88 }
89 if (mQueue.empty()) {
90 // Wait until a new callback is scheduled or destructor was called.
91 mCondition.wait(lock, [this] { return mFinished || !mQueue.empty(); });
92 } else {
93 // Wait until next callback expires or a new one is scheduled.
94 // 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.
96 mCondition.wait_for(lock, mQueue.top().getWaitForExpirationDuration());
97 }
98 }
99 }
100
101 // -------------------------------------------------------------------------------------------------
102
103 }; // namespace vibrator
104
105 }; // namespace android
106