Coverage Report

Created: 2025-06-24 06:17

/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