1 /*
2 * Copyright (C) 2016 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 "chre/platform/system_timer.h"
18
19 #include <errno.h>
20 #include <signal.h>
21 #include <string.h>
22
23 #include <cinttypes>
24 #include <mutex>
25 #include <unordered_set>
26
27 #include "chre/platform/log.h"
28 #include "chre/util/time.h"
29
30 namespace chre {
31
32 namespace {
33
34 constexpr uint64_t kOneSecondInNanoseconds = 1000000000;
35 std::unordered_set<SystemTimer *> gActiveTimerInstances;
36 std::mutex gGlobalTimerMutex;
37
NanosecondsToTimespec(uint64_t ns,struct timespec * ts)38 void NanosecondsToTimespec(uint64_t ns, struct timespec *ts) {
39 ts->tv_sec = ns / kOneSecondInNanoseconds;
40 ts->tv_nsec = ns % kOneSecondInNanoseconds;
41 }
42
43 } // anonymous namespace
44
systemTimerNotifyCallback(union sigval cookie)45 void SystemTimerBase::systemTimerNotifyCallback(union sigval cookie) {
46 SystemTimer *sysTimer = static_cast<SystemTimer *>(cookie.sival_ptr);
47 std::lock_guard<std::mutex> globalLock(gGlobalTimerMutex);
48 if (gActiveTimerInstances.find(sysTimer) != gActiveTimerInstances.end()) {
49 std::lock_guard<std::mutex> lock(sysTimer->mMutex);
50 sysTimer->mCallback(sysTimer->mData);
51 }
52 }
53
SystemTimer()54 SystemTimer::SystemTimer() {
55 std::lock_guard<std::mutex> globalLock(gGlobalTimerMutex);
56 gActiveTimerInstances.insert(this);
57 }
58
~SystemTimer()59 SystemTimer::~SystemTimer() {
60 std::lock_guard<std::mutex> globalLock(gGlobalTimerMutex);
61 gActiveTimerInstances.erase(this);
62 if (mInitialized) {
63 int ret = timer_delete(mTimerId);
64 if (ret != 0) {
65 LOGE("Couldn't delete timer: %s", strerror(errno));
66 }
67 mInitialized = false;
68 }
69 }
70
init()71 bool SystemTimer::init() {
72 if (mInitialized) {
73 LOGW("Tried re-initializing timer");
74 } else {
75 struct sigevent sigevt = {};
76 sigevt.sigev_notify = SIGEV_THREAD;
77 sigevt.sigev_value.sival_ptr = this;
78 sigevt.sigev_notify_function = systemTimerNotifyCallback;
79 sigevt.sigev_notify_attributes = nullptr;
80
81 int ret = timer_create(CLOCK_MONOTONIC, &sigevt, &mTimerId);
82 if (ret != 0) {
83 LOGE("Couldn't create timer: %s", strerror(errno));
84 } else {
85 mInitialized = true;
86 }
87 }
88
89 return mInitialized;
90 }
91
set(SystemTimerCallback * callback,void * data,Nanoseconds delay)92 bool SystemTimer::set(SystemTimerCallback *callback, void *data,
93 Nanoseconds delay) {
94 if (!mInitialized) {
95 return false;
96 }
97
98 // 0 has a special meaning in POSIX, i.e. cancel the timer. In our API, a
99 // value of 0 just means fire right away.
100 if (delay.toRawNanoseconds() == 0) {
101 delay = Nanoseconds(1);
102 }
103
104 {
105 std::lock_guard<std::mutex> lock(mMutex);
106 mCallback = callback;
107 mData = data;
108 }
109 return setInternal(delay.toRawNanoseconds());
110 }
111
cancel()112 bool SystemTimer::cancel() {
113 if (mInitialized) {
114 // Setting delay to 0 disarms the timer.
115 return setInternal(0);
116 }
117 return false;
118 }
119
isActive()120 bool SystemTimer::isActive() {
121 bool isActive = false;
122 if (mInitialized) {
123 struct itimerspec spec = {};
124 int ret = timer_gettime(mTimerId, &spec);
125 if (ret != 0) {
126 LOGE("Couldn't obtain current timer configuration: %s", strerror(errno));
127 }
128
129 isActive = (spec.it_value.tv_sec > 0 || spec.it_value.tv_nsec > 0);
130 }
131
132 return isActive;
133 }
134
setInternal(uint64_t delayNs)135 bool SystemTimerBase::setInternal(uint64_t delayNs) {
136 constexpr int kFlags = 0;
137 struct itimerspec spec = {};
138
139 NanosecondsToTimespec(delayNs, &spec.it_value);
140 NanosecondsToTimespec(0, &spec.it_interval);
141
142 int ret = timer_settime(mTimerId, kFlags, &spec, nullptr);
143 if (ret != 0) {
144 LOGE("Couldn't set timer: %s", strerror(errno));
145 return false;
146 }
147 return true;
148 }
149
150 } // namespace chre
151