1 /*
2  * Copyright 2018 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 #pragma once
18 
19 #include <semaphore.h>
20 #include <chrono>
21 #include <condition_variable>
22 #include <thread>
23 #include "../Clock.h"
24 
25 #include <android-base/thread_annotations.h>
26 #include <scheduler/Time.h>
27 
28 namespace android {
29 namespace scheduler {
30 
31 /*
32  * Class that sets off a timer for a given interval, and fires a callback when the
33  * interval expires.
34  */
35 class OneShotTimer {
36 public:
37     using Interval = std::chrono::milliseconds;
38     using ResetCallback = std::function<void()>;
39     using TimeoutCallback = std::function<void()>;
40 
41     OneShotTimer(std::string name, const Interval& interval, const ResetCallback& resetCallback,
42                  const TimeoutCallback& timeoutCallback,
43                  std::unique_ptr<android::Clock> clock = std::make_unique<SteadyClock>());
44     ~OneShotTimer();
45 
interval()46     Duration interval() const { return mInterval.load(); }
setInterval(Interval value)47     void setInterval(Interval value) { mInterval = value; }
48 
49     // Initializes and turns on the idle timer.
50     void start();
51     // Stops the idle timer and any held resources.
52     void stop();
53     // Resets the wakeup time and fires the reset callback.
54     void reset();
55     // Pauses the timer. reset calls will get ignored.
56     void pause();
57     // Resumes the timer.
58     void resume();
59 
60 private:
61     // Enum to track in what state is the timer.
62     enum class TimerState {
63         // The internal timer thread has been destroyed, and no state is
64         // tracked.
65         // Possible state transitions: RESET
66         STOPPED = 0,
67         // An external thread has just reset this timer.
68         // If there is a reset callback, then that callback is fired.
69         // Possible state transitions: STOPPED, WAITING
70         RESET = 1,
71         // This timer is waiting for the timeout interval to expire.
72         // Possible state transaitions: STOPPED, RESET, IDLE
73         WAITING = 2,
74         // The timeout interval has expired, so we are sleeping now.
75         // Possible state transaitions: STOPPED, RESET
76         IDLE = 3
77     };
78 
79     // Function that loops until the condition for stopping is met.
80     void loop();
81 
82     // Checks whether mResetTriggered and mStopTriggered were set and updates
83     // mState if so.
84     TimerState checkForResetAndStop(TimerState state);
85 
86     // Thread waiting for timer to expire.
87     std::thread mThread;
88 
89     // Clock object for the timer. Mocked in unit tests.
90     std::unique_ptr<android::Clock> mClock;
91 
92     // Semaphore to keep mThread synchronized.
93     sem_t mSemaphore;
94 
95     // Timer's name.
96     std::string mName;
97 
98     // Interval after which timer expires.
99     std::atomic<Interval> mInterval;
100 
101     // Callback that happens when timer resets.
102     const ResetCallback mResetCallback;
103 
104     // Callback that happens when timer expires.
105     const TimeoutCallback mTimeoutCallback;
106 
107     // After removing lock guarding mState, the state can be now accessed at
108     // any time. Keep a bool if the reset or stop were requested, and occasionally
109     // check in the main loop if they were.
110     std::atomic<bool> mResetTriggered = false;
111     std::atomic<bool> mStopTriggered = false;
112     std::atomic<bool> mWaiting = false;
113     std::atomic<bool> mPaused = false;
114     std::atomic<std::chrono::steady_clock::time_point> mLastResetTime;
115 };
116 
117 } // namespace scheduler
118 } // namespace android
119