/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 |