/src/connectedhomeip/src/system/SystemClock.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * |
3 | | * Copyright (c) 2020 Project CHIP Authors |
4 | | * Copyright (c) 2018 Nest Labs, Inc. |
5 | | * |
6 | | * Licensed under the Apache License, Version 2.0 (the "License"); |
7 | | * you may not use this file except in compliance with the License. |
8 | | * You may obtain a copy of the License at |
9 | | * |
10 | | * http://www.apache.org/licenses/LICENSE-2.0 |
11 | | * |
12 | | * Unless required by applicable law or agreed to in writing, software |
13 | | * distributed under the License is distributed on an "AS IS" BASIS, |
14 | | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
15 | | * See the License for the specific language governing permissions and |
16 | | * limitations under the License. |
17 | | */ |
18 | | |
19 | | /** |
20 | | * @file |
21 | | * Provides default implementations for the platform Get/SetClock_ functions |
22 | | * for POSIX and LwIP platforms. |
23 | | */ |
24 | | |
25 | | #include <system/SystemClock.h> |
26 | | |
27 | | #include <lib/support/CodeUtils.h> |
28 | | #include <lib/support/TimeUtils.h> |
29 | | #include <system/SystemError.h> |
30 | | |
31 | | #include <limits> |
32 | | #include <stdint.h> |
33 | | #include <stdlib.h> |
34 | | |
35 | | #if !CHIP_SYSTEM_CONFIG_PLATFORM_PROVIDES_TIME |
36 | | |
37 | | #if CHIP_SYSTEM_CONFIG_USE_POSIX_TIME_FUNCTS || CHIP_SYSTEM_CONFIG_USE_SOCKETS |
38 | | #include <errno.h> |
39 | | #include <time.h> |
40 | | #endif // CHIP_SYSTEM_CONFIG_USE_POSIX_TIME_FUNCTS || CHIP_SYSTEM_CONFIG_USE_SOCKETS |
41 | | |
42 | | #if CHIP_SYSTEM_CONFIG_USE_LWIP |
43 | | #include <lwip/sys.h> |
44 | | #endif // CHIP_SYSTEM_CONFIG_USE_LWIP |
45 | | |
46 | | #endif // !CHIP_SYSTEM_CONFIG_PLATFORM_PROVIDES_TIME |
47 | | |
48 | | namespace chip { |
49 | | namespace System { |
50 | | namespace Clock { |
51 | | |
52 | | namespace Internal { |
53 | | |
54 | | #if CHIP_SYSTEM_CONFIG_PLATFORM_PROVIDES_TIME |
55 | | extern ClockImpl gClockImpl; |
56 | | #else // CHIP_SYSTEM_CONFIG_PLATFORM_PROVIDES_TIME |
57 | | ClockImpl gClockImpl; |
58 | | #endif // CHIP_SYSTEM_CONFIG_PLATFORM_PROVIDES_TIME |
59 | | |
60 | | ClockBase * gClockBase = &gClockImpl; |
61 | | |
62 | | } // namespace Internal |
63 | | |
64 | | Timestamp ClockBase::GetMonotonicTimestamp() |
65 | 19.7k | { |
66 | | // Below implementation uses `__atomic_*` API which has wider support than |
67 | | // <atomic> on embedded platforms, so that embedded platforms can use |
68 | | // it by widening the #ifdefs later. |
69 | 19.7k | #if CHIP_DEVICE_LAYER_USE_ATOMICS_FOR_CLOCK |
70 | 19.7k | uint64_t prevTimestamp = __atomic_load_n(&mLastTimestamp, __ATOMIC_SEQ_CST); |
71 | 19.7k | static_assert(sizeof(prevTimestamp) == sizeof(Timestamp), "Must have scalar match between timestamp and uint64_t for atomics."); |
72 | | |
73 | | // Force a reorder barrier to prevent GetMonotonicMilliseconds64() from being |
74 | | // optimizer-called before prevTimestamp loading, so that newTimestamp acquisition happens-after |
75 | | // the prevTimestamp load. |
76 | 19.7k | __atomic_signal_fence(__ATOMIC_SEQ_CST); |
77 | | #else |
78 | | uint64_t prevTimestamp = mLastTimestamp; |
79 | | #endif // CHIP_DEVICE_LAYER_USE_ATOMICS_FOR_CLOCK |
80 | | |
81 | 19.7k | Timestamp newTimestamp = GetMonotonicMilliseconds64(); |
82 | | |
83 | | // Need to guarantee the invariant that monotonic clock never goes backwards, which would break multiple system |
84 | | // assumptions which use these clocks. |
85 | 19.7k | VerifyOrDie(newTimestamp.count() >= prevTimestamp); |
86 | | |
87 | 19.7k | #if CHIP_DEVICE_LAYER_USE_ATOMICS_FOR_CLOCK |
88 | | // newTimestamp guaranteed to never be < the last timestamp. |
89 | 19.7k | __atomic_store_n(&mLastTimestamp, newTimestamp.count(), __ATOMIC_SEQ_CST); |
90 | | #else |
91 | | mLastTimestamp = newTimestamp.count(); |
92 | | #endif // CHIP_DEVICE_LAYER_USE_ATOMICS_FOR_CLOCK |
93 | | |
94 | 19.7k | return newTimestamp; |
95 | 19.7k | } |
96 | | |
97 | | #if !CHIP_SYSTEM_CONFIG_PLATFORM_PROVIDES_TIME |
98 | | |
99 | | #if CHIP_SYSTEM_CONFIG_USE_POSIX_TIME_FUNCTS |
100 | | |
101 | | // -------------------- Default Get/SetClock Functions for POSIX Systems -------------------- |
102 | | |
103 | | #if !HAVE_CLOCK_GETTIME && !HAVE_GETTIMEOFDAY |
104 | | #error "CHIP_SYSTEM_CONFIG_USE_POSIX_TIME_FUNCTS requires either clock_gettime() or gettimeofday()" |
105 | | #endif |
106 | | |
107 | | #if HAVE_CLOCK_GETTIME |
108 | | |
109 | | #if defined(HAVE_DECL_CLOCK_BOOTTIME) && HAVE_DECL_CLOCK_BOOTTIME |
110 | | // CLOCK_BOOTTIME is a Linux-specific option to clock_gettime for a clock which compensates for system sleep. |
111 | | #define MONOTONIC_CLOCK_ID CLOCK_BOOTTIME |
112 | | #define MONOTONIC_RAW_CLOCK_ID CLOCK_MONOTONIC_RAW |
113 | | #else // HAVE_DECL_CLOCK_BOOTTIME |
114 | | // CLOCK_MONOTONIC is defined in POSIX and hence is the default choice |
115 | | #define MONOTONIC_CLOCK_ID CLOCK_MONOTONIC |
116 | | #endif |
117 | | |
118 | | CHIP_ERROR ClockImpl::GetClock_RealTime(Microseconds64 & aCurTime) |
119 | | { |
120 | | return CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE; |
121 | | } |
122 | | |
123 | | CHIP_ERROR ClockImpl::GetClock_RealTimeMS(Milliseconds64 & aCurTime) |
124 | | { |
125 | | return CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE; |
126 | | } |
127 | | |
128 | | CHIP_ERROR ClockImpl::SetClock_RealTime(Microseconds64 aNewCurTime) |
129 | | { |
130 | | return CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE; |
131 | | } |
132 | | |
133 | | Microseconds64 ClockImpl::GetMonotonicMicroseconds64() |
134 | | { |
135 | | struct timespec ts; |
136 | | int res = clock_gettime(MONOTONIC_CLOCK_ID, &ts); |
137 | | VerifyOrDie(res == 0); |
138 | | return Seconds64(ts.tv_sec) + |
139 | | std::chrono::duration_cast<Microseconds64>(std::chrono::duration<uint64_t, std::nano>(ts.tv_nsec)); |
140 | | } |
141 | | |
142 | | Milliseconds64 ClockImpl::GetMonotonicMilliseconds64() |
143 | | { |
144 | | return std::chrono::duration_cast<Milliseconds64>(GetMonotonicMicroseconds64()); |
145 | | } |
146 | | |
147 | | #endif // HAVE_CLOCK_GETTIME |
148 | | |
149 | | #if HAVE_GETTIMEOFDAY |
150 | | |
151 | | CHIP_ERROR ClockImpl::GetClock_RealTime(Microseconds64 & aCurTime) |
152 | | { |
153 | | return CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE; |
154 | | } |
155 | | |
156 | | CHIP_ERROR ClockImpl::GetClock_RealTimeMS(Milliseconds64 & aCurTime) |
157 | | { |
158 | | return CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE; |
159 | | } |
160 | | |
161 | | CHIP_ERROR ClockImpl::SetClock_RealTime(Microseconds64 aNewCurTime) |
162 | | { |
163 | | return CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE; |
164 | | } |
165 | | |
166 | | Microseconds64 ClockImpl::GetMonotonicMicroseconds64() |
167 | | { |
168 | | struct timeval tv; |
169 | | int res = gettimeofday(&tv, NULL); |
170 | | VerifyOrDie(res == 0); |
171 | | return TimevalToMicroseconds(tv); |
172 | | } |
173 | | |
174 | | Milliseconds64 ClockImpl::GetMonotonicMilliseconds64() |
175 | | { |
176 | | return std::chrono::duration_cast<Milliseconds64>(GetMonotonicMicroseconds64()); |
177 | | } |
178 | | |
179 | | #endif // HAVE_GETTIMEOFDAY |
180 | | |
181 | | #endif // CHIP_SYSTEM_CONFIG_USE_POSIX_TIME_FUNCTS |
182 | | |
183 | | #if CHIP_SYSTEM_CONFIG_USE_LWIP_MONOTONIC_TIME |
184 | | |
185 | | // -------------------- Default Get/SetClock Functions for LwIP Systems -------------------- |
186 | | |
187 | | CHIP_ERROR ClockImpl::GetClock_RealTime(Microseconds64 & aCurTime) |
188 | | { |
189 | | return CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE; |
190 | | } |
191 | | |
192 | | CHIP_ERROR ClockImpl::GetClock_RealTimeMS(Milliseconds64 & aCurTime) |
193 | | { |
194 | | return CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE; |
195 | | } |
196 | | |
197 | | CHIP_ERROR ClockImpl::SetClock_RealTime(Microseconds64 aNewCurTime) |
198 | | { |
199 | | return CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE; |
200 | | } |
201 | | |
202 | | Microseconds64 ClockImpl::GetMonotonicMicroseconds64() |
203 | | { |
204 | | return GetMonotonicMilliseconds64(); |
205 | | } |
206 | | |
207 | | Milliseconds64 ClockImpl::GetMonotonicMilliseconds64() |
208 | | { |
209 | | static volatile uint64_t overflow = 0; |
210 | | static volatile u32_t lastSample = 0; |
211 | | static volatile uint8_t lock = 0; |
212 | | static const uint64_t kOverflowIncrement = static_cast<uint64_t>(0x100000000); |
213 | | |
214 | | uint64_t overflowSample; |
215 | | u32_t sample; |
216 | | |
217 | | // Tracking timer wrap assumes that this function gets called with |
218 | | // a period that is less than 1/2 the timer range. |
219 | | if (__sync_bool_compare_and_swap(&lock, 0, 1)) |
220 | | { |
221 | | sample = sys_now(); |
222 | | |
223 | | if (lastSample > sample) |
224 | | { |
225 | | overflow += kOverflowIncrement; |
226 | | } |
227 | | |
228 | | lastSample = sample; |
229 | | overflowSample = overflow; |
230 | | |
231 | | __sync_bool_compare_and_swap(&lock, 1, 0); |
232 | | } |
233 | | else |
234 | | { |
235 | | // a lower priority task is in the block above. Depending where that |
236 | | // lower task is blocked can spell trouble in a timer wrap condition. |
237 | | // the question here is what this task should use as an overflow value. |
238 | | // To fix this race requires a platform api that can be used to |
239 | | // protect critical sections. |
240 | | overflowSample = overflow; |
241 | | sample = sys_now(); |
242 | | } |
243 | | |
244 | | return Milliseconds64(overflowSample | static_cast<uint64_t>(sample)); |
245 | | } |
246 | | |
247 | | #endif // CHIP_SYSTEM_CONFIG_USE_LWIP_MONOTONIC_TIME |
248 | | |
249 | | #endif // CHIP_SYSTEM_CONFIG_PLATFORM_PROVIDES_TIME |
250 | | |
251 | | #if CHIP_SYSTEM_CONFIG_USE_POSIX_TIME_FUNCTS || CHIP_SYSTEM_CONFIG_USE_SOCKETS |
252 | | |
253 | | Microseconds64 TimevalToMicroseconds(const timeval & tv) |
254 | 0 | { |
255 | 0 | return Seconds64(tv.tv_sec) + Microseconds64(tv.tv_usec); |
256 | 0 | } |
257 | | |
258 | | void ToTimeval(Microseconds64 in, timeval & out) |
259 | 1.90k | { |
260 | 1.90k | Seconds32 seconds = std::chrono::duration_cast<Seconds32>(in); |
261 | 1.90k | in -= seconds; |
262 | 1.90k | out.tv_sec = static_cast<time_t>(seconds.count()); |
263 | 1.90k | out.tv_usec = static_cast<suseconds_t>(in.count()); |
264 | 1.90k | } |
265 | | |
266 | | #endif // CHIP_SYSTEM_CONFIG_USE_POSIX_TIME_FUNCTS || CHIP_SYSTEM_CONFIG_USE_SOCKETS |
267 | | |
268 | | static_assert(std::numeric_limits<Microseconds64::rep>::is_integer, "Microseconds64 must be an integer type"); |
269 | | static_assert(std::numeric_limits<Microseconds32::rep>::is_integer, "Microseconds32 must be an integer type"); |
270 | | static_assert(std::numeric_limits<Milliseconds64::rep>::is_integer, "Milliseconds64 must be an integer type"); |
271 | | static_assert(std::numeric_limits<Milliseconds32::rep>::is_integer, "Milliseconds32 must be an integer type"); |
272 | | static_assert(std::numeric_limits<Seconds64::rep>::is_integer, "Seconds64 must be an integer type"); |
273 | | static_assert(std::numeric_limits<Seconds32::rep>::is_integer, "Seconds32 must be an integer type"); |
274 | | static_assert(std::numeric_limits<Seconds16::rep>::is_integer, "Seconds16 must be an integer type"); |
275 | | |
276 | | static_assert(std::numeric_limits<Microseconds64::rep>::digits >= 64, "Microseconds64 must be at least 64 bits"); |
277 | | static_assert(std::numeric_limits<Microseconds32::rep>::digits >= 32, "Microseconds32 must be at least 32 bits"); |
278 | | static_assert(std::numeric_limits<Milliseconds64::rep>::digits >= 64, "Milliseconds64 must be at least 64 bits"); |
279 | | static_assert(std::numeric_limits<Milliseconds32::rep>::digits >= 32, "Milliseconds32 must be at least 32 bits"); |
280 | | static_assert(std::numeric_limits<Seconds64::rep>::digits >= 64, "Seconds64 must be at least 64 bits"); |
281 | | static_assert(std::numeric_limits<Seconds32::rep>::digits >= 32, "Seconds32 must be at least 32 bits"); |
282 | | static_assert(std::numeric_limits<Seconds16::rep>::digits >= 16, "Seconds16 must be at least 16 bits"); |
283 | | |
284 | | CHIP_ERROR GetClock_MatterEpochS(uint32_t & aMatterEpoch) |
285 | 0 | { |
286 | 0 | aMatterEpoch = 0; |
287 | |
|
288 | 0 | Milliseconds64 cTMs; |
289 | 0 | CHIP_ERROR err = System::SystemClock().GetClock_RealTimeMS(cTMs); |
290 | |
|
291 | 0 | if (err != CHIP_NO_ERROR) |
292 | 0 | { |
293 | 0 | ChipLogError(DeviceLayer, "Unable to get current time - err:%" CHIP_ERROR_FORMAT, err.Format()); |
294 | 0 | return err; |
295 | 0 | } |
296 | | |
297 | 0 | auto unixEpoch = std::chrono::duration_cast<Seconds32>(cTMs).count(); |
298 | 0 | if (!UnixEpochToChipEpochTime(unixEpoch, aMatterEpoch)) |
299 | 0 | { |
300 | 0 | ChipLogError(DeviceLayer, "Unable to convert Unix Epoch time to Matter Epoch Time: unixEpoch (%u)", |
301 | 0 | static_cast<unsigned int>(unixEpoch)); |
302 | 0 | return CHIP_ERROR_INCORRECT_STATE; |
303 | 0 | } |
304 | | |
305 | 0 | return CHIP_NO_ERROR; |
306 | 0 | } |
307 | | |
308 | | } // namespace Clock |
309 | | } // namespace System |
310 | | } // namespace chip |