Coverage Report

Created: 2025-03-09 06:50

/src/openweave-core/src/system/SystemTimer.cpp
Line
Count
Source (jump to first uncovered line)
1
/*
2
 *
3
 *    Copyright (c) 2016-2017 Nest Labs, Inc.
4
 *    All rights reserved.
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
 *      This file defines the member functions and private data for
22
 *      the nl::Weave::System::Timer class, which is used for
23
 *      representing an in-progress one-shot timer.
24
 */
25
26
// Include module header
27
#include <SystemLayer/SystemTimer.h>
28
29
// Include common private header
30
#include "SystemLayerPrivate.h"
31
32
// Include local headers
33
#include <string.h>
34
35
#include <SystemLayer/SystemError.h>
36
#include <SystemLayer/SystemLayer.h>
37
#include <SystemLayer/SystemFaultInjection.h>
38
39
#include <Weave/Support/CodeUtils.h>
40
41
/*******************************************************************************
42
 * Timer state
43
 *
44
 * There are two fundamental state-change variables: Object::mSystemLayer and
45
 * Timer::OnComplete. These must be checked and changed atomically. The state
46
 * of the timer is governed by the following state machine:
47
 *
48
 *  INITIAL STATE: mSystemLayer == NULL, OnComplete == NULL
49
 *      |
50
 *      V
51
 *  UNALLOCATED<-----------------------------+
52
 *      |                                    |
53
 *  (set mSystemLayer != NULL)               |
54
 *      |                                    |
55
 *      V                                    |
56
 *  ALLOCATED-------(set mSystemLayer NULL)--+
57
 *      |    \-----------------------------+
58
 *      |                                  |
59
 *  (set OnComplete != NULL)               |
60
 *      |                                  |
61
 *      V                                  |
62
 *    ARMED ---------( clear OnComplete )--+
63
 *
64
 * When in the ARMED state:
65
 *
66
 *     * None of the member variables may mutate.
67
 *     * OnComplete must only be cleared by Cancel() or HandleComplete()
68
 *     * Cancel() and HandleComplete() will test that they are the one to
69
 *       successfully set OnComplete NULL. And if so, that will be the
70
 *       thread that must call Object::Release().
71
 *
72
 *******************************************************************************
73
 */
74
75
namespace nl {
76
namespace Weave {
77
namespace System {
78
79
ObjectPool<Timer, WEAVE_SYSTEM_CONFIG_NUM_TIMERS> Timer::sPool;
80
81
/**
82
 *  This method returns the current epoch, corrected by system sleep with the system timescale, in milliseconds.
83
 *
84
 *  DEPRECATED -- Please use System::Layer::GetClock_MonotonicMS() instead.
85
 *
86
 *  @return A timestamp in milliseconds.
87
 */
88
Timer::Epoch Timer::GetCurrentEpoch(void)
89
0
{
90
0
    return Platform::Layer::GetClock_MonotonicMS();
91
0
}
92
93
/**
94
 *  Compares two Timer::Epoch values and returns true if the first value is earlier than the second value.
95
 *
96
 *  @brief
97
 *      A static API that gets called to compare 2 time values.  This API attempts to account for timer wrap by assuming that the
98
 *      difference between the 2 input values will only be more than half the Epoch scalar range if a timer wrap has occurred
99
 *      between the 2 samples.
100
 *
101
 *  @note
102
 *      This implementation assumes that Timer::Epoch is an unsigned scalar type.
103
 *
104
 *  @return true if the first param is earlier than the second, false otherwise.
105
 */
106
bool Timer::IsEarlierEpoch(const Timer::Epoch &inFirst, const Timer::Epoch &inSecond)
107
0
{
108
0
    static const Epoch kMaxTime_2 = static_cast<Epoch>((static_cast<Epoch>(0) - static_cast<Epoch>(1)) / 2);
109
110
    // account for timer wrap with the assumption that no two input times will "naturally"
111
    // be more than half the timer range apart.
112
0
    return (((inFirst < inSecond) && (inSecond - inFirst < kMaxTime_2)) ||
113
0
            ((inFirst > inSecond) && (inFirst - inSecond > kMaxTime_2)));
114
0
}
115
116
/**
117
 *  This method registers an one-shot timer with the underlying timer mechanism provided by the platform.
118
 *
119
 *  @param[in]  aDelayMilliseconds  The number of milliseconds before this timer fires
120
 *  @param[in]  aOnComplete          A pointer to the callback function when this timer fires
121
 *  @param[in]  aAppState            An arbitrary pointer to be passed into onComplete when this timer fires
122
 *
123
 *  @retval #WEAVE_SYSTEM_NO_ERROR Unconditionally.
124
 *
125
 */
126
Error Timer::Start(uint32_t aDelayMilliseconds, OnCompleteFunct aOnComplete, void* aAppState)
127
0
{
128
0
    Layer& lLayer = this->SystemLayer();
129
130
0
    WEAVE_SYSTEM_FAULT_INJECT(FaultInjection::kFault_TimeoutImmediate, aDelayMilliseconds = 0);
131
132
0
    this->AppState = aAppState;
133
0
    this->mAwakenEpoch = Timer::GetCurrentEpoch() + static_cast<Epoch>(aDelayMilliseconds);
134
0
    if (!__sync_bool_compare_and_swap(&this->OnComplete, NULL, aOnComplete))
135
0
    {
136
0
        WeaveDie();
137
0
    }
138
139
#if WEAVE_SYSTEM_CONFIG_USE_LWIP
140
    // add to the sorted list of timers. Earliest timer appears first.
141
    if (lLayer.mTimerList == NULL ||
142
        this->IsEarlierEpoch(this->mAwakenEpoch, lLayer.mTimerList->mAwakenEpoch))
143
    {
144
        this->mNextTimer = lLayer.mTimerList;
145
        lLayer.mTimerList = this;
146
147
        // this is the new eariest timer and so the timer needs (re-)starting provided that
148
        // the system is not currently processing expired timers, in which case it is left to
149
        // HandleExpiredTimers() to re-start the timer.
150
        if (!lLayer.mTimerComplete)
151
        {
152
            lLayer.StartPlatformTimer(aDelayMilliseconds);
153
        }
154
    }
155
    else
156
    {
157
        Timer* lTimer = lLayer.mTimerList;
158
159
        while (lTimer->mNextTimer)
160
        {
161
            if (this->IsEarlierEpoch(this->mAwakenEpoch, lTimer->mNextTimer->mAwakenEpoch))
162
            {
163
                // found the insert location.
164
                break;
165
            }
166
167
            lTimer = lTimer->mNextTimer;
168
        }
169
170
        this->mNextTimer = lTimer->mNextTimer;
171
        lTimer->mNextTimer = this;
172
    }
173
#endif // WEAVE_SYSTEM_CONFIG_USE_LWIP
174
0
#if WEAVE_SYSTEM_CONFIG_USE_SOCKETS
175
0
    lLayer.WakeSelect();
176
0
#endif // WEAVE_SYSTEM_CONFIG_USE_SOCKETS
177
178
0
    return WEAVE_SYSTEM_NO_ERROR;
179
0
}
180
181
Error Timer::ScheduleWork(OnCompleteFunct aOnComplete, void* aAppState)
182
0
{
183
0
    Error err = WEAVE_SYSTEM_NO_ERROR;
184
0
    Layer& lLayer = this->SystemLayer();
185
186
0
    this->AppState = aAppState;
187
0
    this->mAwakenEpoch = Timer::GetCurrentEpoch();
188
0
    if (!__sync_bool_compare_and_swap(&this->OnComplete, NULL, aOnComplete))
189
0
    {
190
0
        WeaveDie();
191
0
    }
192
193
#if WEAVE_SYSTEM_CONFIG_USE_LWIP
194
    err = lLayer.PostEvent(*this, Weave::System::kEvent_ScheduleWork, 0);
195
#endif // WEAVE_SYSTEM_CONFIG_USE_LWIP
196
0
#if WEAVE_SYSTEM_CONFIG_USE_SOCKETS
197
0
    lLayer.WakeSelect();
198
0
#endif // WEAVE_SYSTEM_CONFIG_USE_SOCKETS
199
200
0
    return err;
201
0
}
202
203
/**
204
 *  This method de-initializes the timer object, and prevents this timer from firing if it hasn't done so.
205
 *
206
 *  @retval #WEAVE_SYSTEM_NO_ERROR Unconditionally.
207
 */
208
Error Timer::Cancel()
209
0
{
210
#if WEAVE_SYSTEM_CONFIG_USE_LWIP
211
    Layer& lLayer = this->SystemLayer();
212
#endif // WEAVE_SYSTEM_CONFIG_USE_LWIP
213
0
    OnCompleteFunct lOnComplete = this->OnComplete;
214
215
    // Check if the timer is armed
216
0
    VerifyOrExit(lOnComplete != NULL, );
217
    // Atomically disarm if the value has not changed
218
0
    VerifyOrExit(__sync_bool_compare_and_swap(&this->OnComplete, lOnComplete, NULL), );
219
220
    // Since this thread changed the state of OnComplete, release the timer.
221
0
    this->AppState = NULL;
222
223
#if WEAVE_SYSTEM_CONFIG_USE_LWIP
224
    if (lLayer.mTimerList)
225
    {
226
        if (this == lLayer.mTimerList)
227
        {
228
            lLayer.mTimerList = this->mNextTimer;
229
        }
230
        else
231
        {
232
            Timer* lTimer = lLayer.mTimerList;
233
234
            while (lTimer->mNextTimer)
235
            {
236
                if (this == lTimer->mNextTimer)
237
                {
238
                    lTimer->mNextTimer = this->mNextTimer;
239
                    break;
240
                }
241
242
                lTimer = lTimer->mNextTimer;
243
            }
244
        }
245
246
        this->mNextTimer = NULL;
247
    }
248
#endif // WEAVE_SYSTEM_CONFIG_USE_LWIP
249
250
0
    this->Release();
251
0
exit:
252
0
    return WEAVE_SYSTEM_NO_ERROR;
253
0
}
254
255
/**
256
 *  This method is called by the underlying timer mechanism provided by the platform when the timer fires.
257
 */
258
void Timer::HandleComplete()
259
0
{
260
    // Save information needed to perform the callback.
261
0
    Layer& lLayer = this->SystemLayer();
262
0
    const OnCompleteFunct lOnComplete = this->OnComplete;
263
0
    void* lAppState = this->AppState;
264
265
    // Check if timer is armed
266
0
    VerifyOrExit(lOnComplete != NULL, );
267
    // Atomically disarm if the value has not changed.
268
0
    VerifyOrExit(__sync_bool_compare_and_swap(&this->OnComplete, lOnComplete, NULL), );
269
270
    // Since this thread changed the state of OnComplete, release the timer.
271
0
    AppState = NULL;
272
0
    this->Release();
273
274
    // Invoke the app's callback, if it's still valid.
275
0
    if (lOnComplete != NULL)
276
0
        lOnComplete(&lLayer, lAppState, WEAVE_SYSTEM_NO_ERROR);
277
278
0
exit:
279
0
    return;
280
0
}
281
282
#if WEAVE_SYSTEM_CONFIG_USE_LWIP
283
/**
284
 * Completes any timers that have expired.
285
 *
286
 *  @brief
287
 *      A static API that gets called when the platform timer expires. Any expired timers are completed and removed from the list
288
 *      of active timers in the layer object. If unexpired timers remain on completion, StartPlatformTimer will be called to
289
 *      restart the platform timer.
290
 *
291
 *  @note
292
 *      It's harmless if this API gets called and there are no expired timers.
293
 *
294
 *  @return WEAVE_SYSTEM_NO_ERROR on success, error code otherwise.
295
 *
296
 */
297
Error Timer::HandleExpiredTimers(Layer& aLayer)
298
{
299
    size_t timersHandled = 0;
300
301
    // Expire each timer in turn until an unexpired timer is reached or the timerlist is emptied.  We set the current expiration
302
    // time outside the loop; that way timers set after the current tick will not be executed within this expiration window
303
    // regardless how long the processing of the currently expired timers took
304
    Epoch currentEpoch = Timer::GetCurrentEpoch();
305
306
    while (aLayer.mTimerList)
307
    {
308
        // limit the number of timers handled before the control is returned to the event queue.  The bound is similar to
309
        // (though not exactly same) as that on the sockets-based systems.
310
311
        // The platform timer API has MSEC resolution so expire any timer with less than 1 msec remaining.
312
        if ((timersHandled < Timer::sPool.Size()) && Timer::IsEarlierEpoch(aLayer.mTimerList->mAwakenEpoch, currentEpoch + 1))
313
        {
314
            Timer& lTimer = *aLayer.mTimerList;
315
            aLayer.mTimerList = lTimer.mNextTimer;
316
            lTimer.mNextTimer = NULL;
317
318
            aLayer.mTimerComplete = true;
319
            lTimer.HandleComplete();
320
            aLayer.mTimerComplete = false;
321
322
            timersHandled++;
323
        }
324
        else
325
        {
326
            // timers still exist so restart the platform timer.
327
            uint64_t delayMilliseconds = 0ULL;
328
329
            currentEpoch = Timer::GetCurrentEpoch();
330
331
            // the next timer expires in the future, so set the delayMilliseconds to a non-zero value
332
            if (currentEpoch < aLayer.mTimerList->mAwakenEpoch)
333
            {
334
                delayMilliseconds = aLayer.mTimerList->mAwakenEpoch - currentEpoch;
335
            }
336
            /*
337
             * StartPlatformTimer() accepts a 32bit value in milliseconds.  Epochs are 64bit numbers.  The only way in which this could
338
             * overflow is if time went backwards (e.g. as a result of a time adjustment from time synchronization).  Verify that the
339
             * timer can still be executed (even if it is very late) and exit if that is the case.  Note: if the time sync ever ends up
340
             * adjusting the clock, we should implement a method that deals with all the timers in the system.
341
             */
342
            VerifyOrDie(delayMilliseconds <= UINT32_MAX);
343
344
            aLayer.StartPlatformTimer(static_cast<uint32_t>(delayMilliseconds));
345
            break; // all remaining timers are still ticking.
346
        }
347
    }
348
349
    return WEAVE_SYSTEM_NO_ERROR;
350
}
351
#endif // WEAVE_SYSTEM_CONFIG_USE_LWIP
352
353
} // namespace System
354
} // namespace Weave
355
} // namespace nl