/src/connectedhomeip/src/system/SystemLayerImplSelect.cpp
Line | Count | Source |
1 | | /* |
2 | | * |
3 | | * Copyright (c) 2020-2021 Project CHIP Authors |
4 | | * Copyright (c) 2014-2017 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 | | * This file implements Layer using select(). |
22 | | */ |
23 | | |
24 | | #include <lib/support/CodeUtils.h> |
25 | | #include <lib/support/TimeUtils.h> |
26 | | #include <platform/LockTracker.h> |
27 | | #include <system/SystemFaultInjection.h> |
28 | | #include <system/SystemLayer.h> |
29 | | #include <system/SystemLayerImplSelect.h> |
30 | | |
31 | | #include <algorithm> |
32 | | #include <errno.h> |
33 | | |
34 | | // Choose an approximation of PTHREAD_NULL if pthread.h doesn't define one. |
35 | | #if CHIP_SYSTEM_CONFIG_POSIX_LOCKING && !defined(PTHREAD_NULL) |
36 | 10.3k | #define PTHREAD_NULL 0 |
37 | | #endif // CHIP_SYSTEM_CONFIG_POSIX_LOCKING && !defined(PTHREAD_NULL) |
38 | | |
39 | | #if CHIP_SYSTEM_CONFIG_USE_LIBEV |
40 | | // older libev do not yet have ev_io_modify |
41 | | #ifndef ev_io_modify |
42 | | #define ev_io_modify(ev, events_) \ |
43 | | do \ |
44 | | { \ |
45 | | (ev)->events = ((ev)->events & EV__IOFDSET) | (events_); \ |
46 | | } while (0) |
47 | | #endif // ev_io_modify |
48 | | #endif // CHIP_SYSTEM_CONFIG_USE_LIBEV |
49 | | |
50 | | namespace chip { |
51 | | namespace System { |
52 | | |
53 | | constexpr Clock::Seconds64 kDefaultMinSleepPeriod = Clock::Seconds64(60 * 60 * 24 * 30); // Month [sec] |
54 | | |
55 | | CriticalFailure LayerImplSelect::Init() |
56 | 3.12k | { |
57 | 3.12k | VerifyOrReturnError(mLayerState.SetInitializing(), CHIP_ERROR_INCORRECT_STATE); |
58 | | |
59 | 3.12k | RegisterPOSIXErrorFormatter(); |
60 | | |
61 | 3.12k | #if CHIP_SYSTEM_CONFIG_USE_SOCKETS |
62 | 3.12k | for (auto & w : mSocketWatchPool) |
63 | 200k | { |
64 | 200k | w.Clear(); |
65 | 200k | } |
66 | 3.12k | #endif |
67 | | |
68 | 3.12k | #if CHIP_SYSTEM_CONFIG_POSIX_LOCKING |
69 | 3.12k | mHandleSelectThread = PTHREAD_NULL; |
70 | 3.12k | #endif // CHIP_SYSTEM_CONFIG_POSIX_LOCKING |
71 | | |
72 | 3.12k | #if !CHIP_SYSTEM_CONFIG_USE_LIBEV |
73 | | // Create an event to allow an arbitrary thread to wake the thread in the select loop. |
74 | 3.12k | ReturnErrorOnFailure(mWakeEvent.Open()); |
75 | 3.12k | #endif // !CHIP_SYSTEM_CONFIG_USE_LIBEV |
76 | | |
77 | 3.12k | VerifyOrReturnError(mLayerState.SetInitialized(), CHIP_ERROR_INCORRECT_STATE); |
78 | 3.12k | return CHIP_NO_ERROR; |
79 | 3.12k | } |
80 | | |
81 | | void LayerImplSelect::Shutdown() |
82 | 3.12k | { |
83 | 3.12k | VerifyOrReturn(mLayerState.SetShuttingDown()); |
84 | | |
85 | 3.12k | EventSourceClear(); |
86 | | #if CHIP_SYSTEM_CONFIG_USE_LIBEV |
87 | | TimerList::Node * timer; |
88 | | while ((timer = mTimerList.PopEarliest()) != nullptr) |
89 | | { |
90 | | if (ev_is_active(&timer->mLibEvTimer)) |
91 | | { |
92 | | ev_timer_stop(mLibEvLoopP, &timer->mLibEvTimer); |
93 | | } |
94 | | } |
95 | | mTimerPool.ReleaseAll(); |
96 | | |
97 | | #if CHIP_SYSTEM_CONFIG_USE_SOCKETS |
98 | | for (auto & w : mSocketWatchPool) |
99 | | { |
100 | | w.DisableAndClear(); |
101 | | } |
102 | | #endif |
103 | | #else |
104 | 3.12k | mTimerList.Clear(); |
105 | 3.12k | mTimerPool.ReleaseAll(); |
106 | 3.12k | mWakeEvent.Close(); |
107 | 3.12k | #endif // CHIP_SYSTEM_CONFIG_USE_LIBEV |
108 | | |
109 | 3.12k | mLayerState.ResetFromShuttingDown(); // Return to uninitialized state to permit re-initialization. |
110 | 3.12k | } |
111 | | |
112 | | void LayerImplSelect::Signal() |
113 | 27.2k | { |
114 | | #if CHIP_SYSTEM_CONFIG_USE_LIBEV |
115 | | ChipLogError(DeviceLayer, "Signal() should not be called in CHIP_SYSTEM_CONFIG_USE_LIBEV builds (might be ok in tests)"); |
116 | | #else |
117 | | /* |
118 | | * Wake up the I/O thread by writing a single byte to the wake pipe. |
119 | | * |
120 | | * If this is being called from within an I/O event callback, then writing to the wake pipe can be skipped, |
121 | | * since the I/O thread is already awake. |
122 | | * |
123 | | * Furthermore, we don't care if this write fails as the only reasonably likely failure is that the pipe is full, in which |
124 | | * case the select calling thread is going to wake up anyway. |
125 | | */ |
126 | 27.2k | #if CHIP_SYSTEM_CONFIG_POSIX_LOCKING |
127 | 27.2k | if (pthread_equal(mHandleSelectThread, pthread_self())) |
128 | 14.7k | { |
129 | 14.7k | return; |
130 | 14.7k | } |
131 | 12.4k | #endif // CHIP_SYSTEM_CONFIG_POSIX_LOCKING |
132 | | |
133 | | // Send notification to wake up the select call. |
134 | 12.4k | CHIP_ERROR status = mWakeEvent.Notify(); |
135 | 12.4k | if (status != CHIP_NO_ERROR) |
136 | 614 | { |
137 | | |
138 | 614 | ChipLogError(chipSystemLayer, "System wake event notify failed: %" CHIP_ERROR_FORMAT, status.Format()); |
139 | 614 | } |
140 | 12.4k | #endif // !CHIP_SYSTEM_CONFIG_USE_LIBEV |
141 | 12.4k | } |
142 | | |
143 | | CriticalFailure LayerImplSelect::StartTimer(Clock::Timeout delay, TimerCompleteCallback onComplete, void * appState) |
144 | 21.0k | { |
145 | 21.0k | assertChipStackLockedByCurrentThread(); |
146 | | |
147 | 21.0k | VerifyOrReturnError(mLayerState.IsInitialized(), CHIP_ERROR_INCORRECT_STATE); |
148 | | |
149 | 21.0k | CHIP_SYSTEM_FAULT_INJECT(FaultInjection::kFault_TimeoutImmediate, delay = System::Clock::kZero); |
150 | | |
151 | 21.0k | CancelTimer(onComplete, appState); |
152 | | |
153 | 21.0k | TimerList::Node * timer = mTimerPool.Create(*this, SystemClock().GetMonotonicTimestamp() + delay, onComplete, appState); |
154 | 21.0k | VerifyOrReturnError(timer != nullptr, CHIP_ERROR_NO_MEMORY); |
155 | | |
156 | | #if CHIP_SYSTEM_CONFIG_USE_LIBEV |
157 | | VerifyOrDie(mLibEvLoopP != nullptr); |
158 | | ev_timer_init(&timer->mLibEvTimer, &LayerImplSelect::HandleLibEvTimer, 1, 0); |
159 | | timer->mLibEvTimer.data = timer; |
160 | | auto t = Clock::Milliseconds64(delay).count(); |
161 | | // Note: libev uses the time when events started processing as the "now" reference for relative timers, |
162 | | // for efficiency reasons. This point in time is represented by ev_now(). |
163 | | // The real time is represented by ev_time(). |
164 | | // Without correction, this leads to timers firing a bit too early relative to the time StartTimer() |
165 | | // is called. So the relative value passed to ev_timer_set() is adjusted (increased) here. |
166 | | // Note: Still, slightly early (and of course, late) firing timers are something the caller MUST be prepared for, |
167 | | // because edge cases like system clock adjustments may cause them even with the correction applied here. |
168 | | ev_timer_set(&timer->mLibEvTimer, (static_cast<double>(t) / 1E3) + ev_time() - ev_now(mLibEvLoopP), 0.); |
169 | | (void) mTimerList.Add(timer); |
170 | | ev_timer_start(mLibEvLoopP, &timer->mLibEvTimer); |
171 | | #else |
172 | 21.0k | if (mTimerList.Add(timer) == timer) |
173 | 3.81k | { |
174 | | // The new timer is the earliest, so the time until the next event has probably changed. |
175 | 3.81k | Signal(); |
176 | 3.81k | } |
177 | 21.0k | #endif // CHIP_SYSTEM_CONFIG_USE_LIBEV |
178 | | |
179 | 21.0k | return CHIP_NO_ERROR; |
180 | 21.0k | } |
181 | | |
182 | | CHIP_ERROR LayerImplSelect::ExtendTimerTo(Clock::Timeout delay, TimerCompleteCallback onComplete, void * appState) |
183 | 0 | { |
184 | 0 | VerifyOrReturnError(delay.count() > 0, CHIP_ERROR_INVALID_ARGUMENT); |
185 | | |
186 | 0 | assertChipStackLockedByCurrentThread(); |
187 | |
|
188 | 0 | Clock::Timeout remainingTime = mTimerList.GetRemainingTime(onComplete, appState); |
189 | 0 | if (remainingTime.count() < delay.count()) |
190 | 0 | { |
191 | | // Just call StartTimer; it will invoke CancelTimer(), then start a new timer. That handles |
192 | | // all the various "timer was about to fire" edge cases correctly too. |
193 | 0 | return StartTimer(delay, onComplete, appState); |
194 | 0 | } |
195 | | |
196 | 0 | return CHIP_NO_ERROR; |
197 | 0 | } |
198 | | |
199 | | bool LayerImplSelect::IsTimerActive(TimerCompleteCallback onComplete, void * appState) |
200 | 0 | { |
201 | 0 | bool timerIsActive = (mTimerList.GetRemainingTime(onComplete, appState) > Clock::kZero); |
202 | |
|
203 | 0 | if (!timerIsActive) |
204 | 0 | { |
205 | | // check if the timer is in the mExpiredTimers list about to be fired. |
206 | 0 | for (TimerList::Node * timer = mExpiredTimers.Earliest(); timer != nullptr; timer = timer->mNextTimer) |
207 | 0 | { |
208 | 0 | if (timer->GetCallback().GetOnComplete() == onComplete && timer->GetCallback().GetAppState() == appState) |
209 | 0 | { |
210 | 0 | return true; |
211 | 0 | } |
212 | 0 | } |
213 | 0 | } |
214 | | |
215 | 0 | return timerIsActive; |
216 | 0 | } |
217 | | |
218 | | Clock::Timeout LayerImplSelect::GetRemainingTime(TimerCompleteCallback onComplete, void * appState) |
219 | 0 | { |
220 | 0 | return mTimerList.GetRemainingTime(onComplete, appState); |
221 | 0 | } |
222 | | |
223 | | void LayerImplSelect::CancelTimer(TimerCompleteCallback onComplete, void * appState) |
224 | 42.9k | { |
225 | 42.9k | assertChipStackLockedByCurrentThread(); |
226 | | |
227 | 42.9k | VerifyOrReturn(mLayerState.IsInitialized()); |
228 | | |
229 | 42.9k | TimerList::Node * timer = mTimerList.Remove(onComplete, appState); |
230 | 42.9k | if (timer == nullptr) |
231 | 24.1k | { |
232 | | // The timer was not in our "will fire in the future" list, but it might |
233 | | // be in the "we're about to fire these" chunk we already grabbed from |
234 | | // that list. Check for it there too, and if found there we still want |
235 | | // to cancel it. |
236 | 24.1k | timer = mExpiredTimers.Remove(onComplete, appState); |
237 | 24.1k | } |
238 | 42.9k | VerifyOrReturn(timer != nullptr); |
239 | | |
240 | | #if CHIP_SYSTEM_CONFIG_USE_LIBEV |
241 | | VerifyOrDie(mLibEvLoopP != nullptr); |
242 | | ev_timer_stop(mLibEvLoopP, &timer->mLibEvTimer); |
243 | | #endif // CHIP_SYSTEM_CONFIG_USE_LIBEV |
244 | | |
245 | 18.8k | mTimerPool.Release(timer); |
246 | 18.8k | #if !CHIP_SYSTEM_CONFIG_USE_LIBEV |
247 | | // LIBEV builds does not include an I/O wakeup thread, so must not call Signal(). |
248 | 18.8k | Signal(); |
249 | 18.8k | #endif |
250 | 18.8k | } |
251 | | |
252 | | CriticalFailure LayerImplSelect::ScheduleWork(TimerCompleteCallback onComplete, void * appState) |
253 | 6.17k | { |
254 | 6.17k | assertChipStackLockedByCurrentThread(); |
255 | | |
256 | 6.17k | VerifyOrReturnError(mLayerState.IsInitialized(), CHIP_ERROR_INCORRECT_STATE); |
257 | | |
258 | | #if CHIP_SYSTEM_CONFIG_USE_LIBEV |
259 | | // schedule as timer with no delay, but do NOT cancel previous timers with same onComplete/appState! |
260 | | TimerList::Node * timer = mTimerPool.Create(*this, SystemClock().GetMonotonicTimestamp(), onComplete, appState); |
261 | | VerifyOrReturnError(timer != nullptr, CHIP_ERROR_NO_MEMORY); |
262 | | VerifyOrDie(mLibEvLoopP != nullptr); |
263 | | ev_timer_init(&timer->mLibEvTimer, &LayerImplSelect::HandleLibEvTimer, 1, 0); |
264 | | timer->mLibEvTimer.data = timer; |
265 | | auto t = Clock::Milliseconds64(0).count(); |
266 | | ev_timer_set(&timer->mLibEvTimer, static_cast<double>(t) / 1E3, 0.); |
267 | | (void) mTimerList.Add(timer); |
268 | | ev_timer_start(mLibEvLoopP, &timer->mLibEvTimer); |
269 | | #else |
270 | | // Ideally we would not use a timer here at all, but if we try to just |
271 | | // ScheduleLambda the lambda needs to capture the following: |
272 | | // 1) onComplete |
273 | | // 2) appState |
274 | | // 3) The `this` pointer, because onComplete needs to be passed a pointer to |
275 | | // the System::Layer. |
276 | | // |
277 | | // On a 64-bit system that's 24 bytes, but lambdas passed to ScheduleLambda |
278 | | // are capped at CHIP_CONFIG_LAMBDA_EVENT_SIZE which is 16 bytes. |
279 | | // |
280 | | // So for now use a timer as a poor-man's closure that captures `this` and |
281 | | // onComplete and appState in a single pointer, so we fit inside the size |
282 | | // limit. |
283 | | // |
284 | | // TODO: We could do something here where we compile-time condition on the |
285 | | // sizes of things and use a direct ScheduleLambda if it would fit and this |
286 | | // setup otherwise. |
287 | | // |
288 | | // TODO: But also, unit tests seem to do SystemLayer::ScheduleWork without |
289 | | // actually running a useful event loop (in the PlatformManager sense), |
290 | | // which breaks if we use ScheduleLambda here, since that does rely on the |
291 | | // PlatformManager event loop. So for now, keep scheduling an expires-ASAP |
292 | | // timer, but just make sure we don't cancel existing timers with the same |
293 | | // callback and appState, so ScheduleWork invocations don't stomp on each |
294 | | // other. |
295 | 6.17k | TimerList::Node * timer = mTimerPool.Create(*this, SystemClock().GetMonotonicTimestamp(), onComplete, appState); |
296 | 6.17k | VerifyOrReturnError(timer != nullptr, CHIP_ERROR_NO_MEMORY); |
297 | | |
298 | 6.17k | if (mTimerList.Add(timer) == timer) |
299 | 3.96k | { |
300 | | // The new timer is the earliest, so the time until the next event has probably changed. |
301 | 3.96k | Signal(); |
302 | 3.96k | } |
303 | 6.17k | #endif // CHIP_SYSTEM_CONFIG_USE_LIBEV |
304 | | |
305 | 6.17k | return CHIP_NO_ERROR; |
306 | 6.17k | } |
307 | | |
308 | | #if CHIP_SYSTEM_CONFIG_USE_SOCKETS |
309 | | CHIP_ERROR LayerImplSelect::StartWatchingSocket(int fd, SocketWatchToken * tokenOut) |
310 | 0 | { |
311 | | // Find a free slot. |
312 | 0 | SocketWatch * watch = nullptr; |
313 | 0 | for (auto & w : mSocketWatchPool) |
314 | 0 | { |
315 | 0 | if (w.mFD == fd) |
316 | 0 | { |
317 | | // Already registered, return the existing token |
318 | 0 | *tokenOut = reinterpret_cast<SocketWatchToken>(&w); |
319 | 0 | return CHIP_NO_ERROR; |
320 | 0 | } |
321 | 0 | if ((w.mFD == kInvalidFd) && (watch == nullptr)) |
322 | 0 | { |
323 | 0 | watch = &w; |
324 | 0 | } |
325 | 0 | } |
326 | 0 | VerifyOrReturnError(watch != nullptr, CHIP_ERROR_ENDPOINT_POOL_FULL); |
327 | | |
328 | 0 | watch->mFD = fd; |
329 | | #if CHIP_SYSTEM_CONFIG_USE_LIBEV |
330 | | ev_io_init(&watch->mIoWatcher, &LayerImplSelect::HandleLibEvIoWatcher, 0, 0); |
331 | | watch->mIoWatcher.data = watch; |
332 | | watch->mLayerImplSelectP = this; |
333 | | #endif |
334 | |
|
335 | 0 | *tokenOut = reinterpret_cast<SocketWatchToken>(watch); |
336 | 0 | return CHIP_NO_ERROR; |
337 | 0 | } |
338 | | |
339 | | CHIP_ERROR LayerImplSelect::SetCallback(SocketWatchToken token, SocketWatchCallback callback, intptr_t data) |
340 | 0 | { |
341 | 0 | SocketWatch * watch = reinterpret_cast<SocketWatch *>(token); |
342 | 0 | VerifyOrReturnError(watch != nullptr, CHIP_ERROR_INVALID_ARGUMENT); |
343 | | |
344 | 0 | watch->mCallback = callback; |
345 | 0 | watch->mCallbackData = data; |
346 | 0 | return CHIP_NO_ERROR; |
347 | 0 | } |
348 | | |
349 | | CHIP_ERROR LayerImplSelect::RequestCallbackOnPendingRead(SocketWatchToken token) |
350 | 0 | { |
351 | 0 | SocketWatch * watch = reinterpret_cast<SocketWatch *>(token); |
352 | 0 | VerifyOrReturnError(watch != nullptr, CHIP_ERROR_INVALID_ARGUMENT); |
353 | | |
354 | 0 | watch->mPendingIO.Set(SocketEventFlags::kRead); |
355 | |
|
356 | | #if CHIP_SYSTEM_CONFIG_USE_LIBEV |
357 | | VerifyOrDie(mLibEvLoopP != nullptr); |
358 | | int evs = (watch->mPendingIO.Has(SocketEventFlags::kRead) ? EV_READ : 0) | |
359 | | (watch->mPendingIO.Has(SocketEventFlags::kWrite) ? EV_WRITE : 0); |
360 | | if (!ev_is_active(&watch->mIoWatcher)) |
361 | | { |
362 | | // First time actually using that watch |
363 | | ev_io_set(&watch->mIoWatcher, watch->mFD, evs); |
364 | | ev_io_start(mLibEvLoopP, &watch->mIoWatcher); |
365 | | } |
366 | | else |
367 | | { |
368 | | // already active, just change flags |
369 | | // Note: changing flags only reliably works when the watcher is stopped |
370 | | ev_io_stop(mLibEvLoopP, &watch->mIoWatcher); |
371 | | ev_io_modify(&watch->mIoWatcher, evs); |
372 | | ev_io_start(mLibEvLoopP, &watch->mIoWatcher); |
373 | | } |
374 | | #endif // CHIP_SYSTEM_CONFIG_USE_LIBEV |
375 | |
|
376 | 0 | return CHIP_NO_ERROR; |
377 | 0 | } |
378 | | |
379 | | CHIP_ERROR LayerImplSelect::RequestCallbackOnPendingWrite(SocketWatchToken token) |
380 | 0 | { |
381 | 0 | SocketWatch * watch = reinterpret_cast<SocketWatch *>(token); |
382 | 0 | VerifyOrReturnError(watch != nullptr, CHIP_ERROR_INVALID_ARGUMENT); |
383 | | |
384 | 0 | watch->mPendingIO.Set(SocketEventFlags::kWrite); |
385 | |
|
386 | | #if CHIP_SYSTEM_CONFIG_USE_LIBEV |
387 | | VerifyOrDie(mLibEvLoopP != nullptr); |
388 | | int evs = (watch->mPendingIO.Has(SocketEventFlags::kRead) ? EV_READ : 0) | |
389 | | (watch->mPendingIO.Has(SocketEventFlags::kWrite) ? EV_WRITE : 0); |
390 | | if (!ev_is_active(&watch->mIoWatcher)) |
391 | | { |
392 | | // First time actually using that watch |
393 | | ev_io_set(&watch->mIoWatcher, watch->mFD, evs); |
394 | | ev_io_start(mLibEvLoopP, &watch->mIoWatcher); |
395 | | } |
396 | | else |
397 | | { |
398 | | // already active, just change flags |
399 | | // Note: changing flags only reliably works when the watcher is stopped |
400 | | ev_io_stop(mLibEvLoopP, &watch->mIoWatcher); |
401 | | ev_io_modify(&watch->mIoWatcher, evs); |
402 | | ev_io_start(mLibEvLoopP, &watch->mIoWatcher); |
403 | | } |
404 | | #endif // CHIP_SYSTEM_CONFIG_USE_LIBEV |
405 | |
|
406 | 0 | return CHIP_NO_ERROR; |
407 | 0 | } |
408 | | |
409 | | CHIP_ERROR LayerImplSelect::ClearCallbackOnPendingRead(SocketWatchToken token) |
410 | 0 | { |
411 | 0 | SocketWatch * watch = reinterpret_cast<SocketWatch *>(token); |
412 | 0 | VerifyOrReturnError(watch != nullptr, CHIP_ERROR_INVALID_ARGUMENT); |
413 | | |
414 | 0 | watch->mPendingIO.Clear(SocketEventFlags::kRead); |
415 | |
|
416 | | #if CHIP_SYSTEM_CONFIG_USE_LIBEV |
417 | | if (ev_is_active(&watch->mIoWatcher) && watch->mPendingIO.Raw() == 0) |
418 | | { |
419 | | // all flags cleared now, stop watching |
420 | | ev_io_stop(mLibEvLoopP, &watch->mIoWatcher); |
421 | | } |
422 | | #endif |
423 | |
|
424 | 0 | return CHIP_NO_ERROR; |
425 | 0 | } |
426 | | |
427 | | CHIP_ERROR LayerImplSelect::ClearCallbackOnPendingWrite(SocketWatchToken token) |
428 | 0 | { |
429 | 0 | SocketWatch * watch = reinterpret_cast<SocketWatch *>(token); |
430 | 0 | VerifyOrReturnError(watch != nullptr, CHIP_ERROR_INVALID_ARGUMENT); |
431 | | |
432 | 0 | watch->mPendingIO.Clear(SocketEventFlags::kWrite); |
433 | |
|
434 | | #if CHIP_SYSTEM_CONFIG_USE_LIBEV |
435 | | if (ev_is_active(&watch->mIoWatcher) && watch->mPendingIO.Raw() == 0) |
436 | | { |
437 | | // all flags cleared now, stop watching |
438 | | ev_io_stop(mLibEvLoopP, &watch->mIoWatcher); |
439 | | } |
440 | | #endif |
441 | |
|
442 | 0 | return CHIP_NO_ERROR; |
443 | 0 | } |
444 | | |
445 | | CHIP_ERROR LayerImplSelect::StopWatchingSocket(SocketWatchToken * tokenInOut) |
446 | 0 | { |
447 | 0 | VerifyOrReturnError(tokenInOut != nullptr, CHIP_ERROR_INVALID_ARGUMENT); |
448 | | |
449 | 0 | SocketWatch * watch = reinterpret_cast<SocketWatch *>(*tokenInOut); |
450 | 0 | *tokenInOut = InvalidSocketWatchToken(); |
451 | |
|
452 | 0 | VerifyOrReturnError(watch != nullptr, CHIP_ERROR_INVALID_ARGUMENT); |
453 | 0 | VerifyOrReturnError(watch->mFD >= 0, CHIP_ERROR_INCORRECT_STATE); |
454 | | |
455 | | #if CHIP_SYSTEM_CONFIG_USE_LIBEV |
456 | | watch->DisableAndClear(); |
457 | | #else |
458 | 0 | watch->Clear(); |
459 | | |
460 | | // Wake the thread calling select so that it stops selecting on the socket. |
461 | 0 | Signal(); |
462 | 0 | #endif |
463 | |
|
464 | 0 | return CHIP_NO_ERROR; |
465 | 0 | } |
466 | | |
467 | | /** |
468 | | * Set the read, write or exception bit flags for the specified socket based on its status in |
469 | | * the corresponding file descriptor sets. |
470 | | * |
471 | | * @param[in] socket The file descriptor for which the bit flags are being set. |
472 | | * |
473 | | * @param[in] readfds A pointer to the set of readable file descriptors. |
474 | | * |
475 | | * @param[in] writefds A pointer to the set of writable file descriptors. |
476 | | * |
477 | | * @param[in] exceptfds A pointer to the set of file descriptors with errors. |
478 | | */ |
479 | | SocketEvents LayerImplSelect::SocketEventsFromFDs(int socket, const fd_set & readfds, const fd_set & writefds, |
480 | | const fd_set & exceptfds) |
481 | 0 | { |
482 | 0 | SocketEvents res; |
483 | |
|
484 | 0 | if (socket >= 0) |
485 | 0 | { |
486 | | // POSIX does not define the fd_set parameter of FD_ISSET() as const, even though it isn't modified. |
487 | 0 | if (FD_ISSET(socket, const_cast<fd_set *>(&readfds))) |
488 | 0 | res.Set(SocketEventFlags::kRead); |
489 | 0 | if (FD_ISSET(socket, const_cast<fd_set *>(&writefds))) |
490 | 0 | res.Set(SocketEventFlags::kWrite); |
491 | 0 | if (FD_ISSET(socket, const_cast<fd_set *>(&exceptfds))) |
492 | 0 | res.Set(SocketEventFlags::kExcept); |
493 | 0 | } |
494 | |
|
495 | 0 | return res; |
496 | 0 | } |
497 | | #endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS |
498 | | |
499 | | enum : intptr_t |
500 | | { |
501 | | kLoopHandlerInactive = 0, // default value for EventLoopHandler::mState |
502 | | kLoopHandlerPending, |
503 | | kLoopHandlerActive, |
504 | | }; |
505 | | |
506 | | void LayerImplSelect::AddLoopHandler(EventLoopHandler & handler) |
507 | 0 | { |
508 | | // Add the handler as pending because this method can be called at any point |
509 | | // in a PrepareEvents() / WaitForEvents() / HandleEvents() sequence. |
510 | | // It will be marked active when we call PrepareEvents() on it for the first time. |
511 | 0 | auto & state = LoopHandlerState(handler); |
512 | 0 | VerifyOrDie(state == kLoopHandlerInactive); |
513 | 0 | state = kLoopHandlerPending; |
514 | 0 | mLoopHandlers.PushBack(&handler); |
515 | 0 | } |
516 | | |
517 | | void LayerImplSelect::RemoveLoopHandler(EventLoopHandler & handler) |
518 | 0 | { |
519 | 0 | mLoopHandlers.Remove(&handler); |
520 | 0 | LoopHandlerState(handler) = kLoopHandlerInactive; |
521 | 0 | } |
522 | | |
523 | | void LayerImplSelect::EventSourceAdd(EventSource * source) |
524 | 0 | { |
525 | 0 | assertChipStackLockedByCurrentThread(); |
526 | 0 | if (mSources.Contains(source)) |
527 | 0 | { |
528 | 0 | ChipLogDetail(DeviceLayer, "Warning: the EventSource is already added"); |
529 | 0 | return; |
530 | 0 | } |
531 | 0 | mSources.PushBack(source); |
532 | 0 | } |
533 | | |
534 | | void LayerImplSelect::EventSourceRemove(EventSource * source) |
535 | 0 | { |
536 | 0 | assertChipStackLockedByCurrentThread(); |
537 | 0 | if (mSources.Contains(source)) |
538 | 0 | { |
539 | 0 | mSources.Remove(source); |
540 | 0 | } |
541 | 0 | } |
542 | | |
543 | | void LayerImplSelect::EventSourceClear() |
544 | 3.12k | { |
545 | 3.12k | assertChipStackLockedByCurrentThread(); |
546 | 3.12k | mSources.Clear(); |
547 | 3.12k | } |
548 | | |
549 | | void LayerImplSelect::PrepareEvents() |
550 | 7.24k | { |
551 | 7.24k | assertChipStackLockedByCurrentThread(); |
552 | | |
553 | 7.24k | const Clock::Timestamp currentTime = SystemClock().GetMonotonicTimestamp(); |
554 | 7.24k | Clock::Timestamp awakenTime = currentTime + kDefaultMinSleepPeriod; |
555 | | |
556 | 7.24k | TimerList::Node * timer = mTimerList.Earliest(); |
557 | 7.24k | if (timer) |
558 | 7.24k | { |
559 | 7.24k | awakenTime = std::min(awakenTime, timer->AwakenTime()); |
560 | 7.24k | } |
561 | | |
562 | | // Activate added EventLoopHandlers and call PrepareEvents on active handlers. |
563 | 7.24k | auto loopIter = mLoopHandlers.begin(); |
564 | 7.24k | while (loopIter != mLoopHandlers.end()) |
565 | 0 | { |
566 | 0 | auto & loop = *loopIter++; // advance before calling out, in case a list modification clobbers the `next` pointer |
567 | 0 | switch (auto & state = LoopHandlerState(loop)) |
568 | 0 | { |
569 | 0 | case kLoopHandlerPending: |
570 | 0 | state = kLoopHandlerActive; |
571 | 0 | [[fallthrough]]; |
572 | 0 | case kLoopHandlerActive: |
573 | 0 | awakenTime = std::min(awakenTime, loop.PrepareEvents(currentTime)); |
574 | 0 | break; |
575 | 0 | } |
576 | 0 | } |
577 | | |
578 | 7.24k | const Clock::Timestamp sleepTime = (awakenTime > currentTime) ? (awakenTime - currentTime) : Clock::kZero; |
579 | 7.24k | Clock::ToTimeval(sleepTime, mNextTimeout); |
580 | | |
581 | 7.24k | mMaxFd = -1; |
582 | | |
583 | | // NOLINTBEGIN(clang-analyzer-security.insecureAPI.bzero) |
584 | | // |
585 | | // NOTE: darwin uses bzero to clear out FD sets. This is not a security concern. |
586 | 7.24k | FD_ZERO(&mSelected.mReadSet); |
587 | 7.24k | FD_ZERO(&mSelected.mWriteSet); |
588 | 7.24k | FD_ZERO(&mSelected.mErrorSet); |
589 | | // NOLINTEND(clang-analyzer-security.insecureAPI.bzero) |
590 | | |
591 | 7.24k | #if !CHIP_SYSTEM_CONFIG_USE_LIBEV |
592 | 7.24k | FD_SET(mWakeEvent.GetReadFD(), &mSelected.mReadSet); |
593 | 7.24k | mMaxFd = mWakeEvent.GetReadFD(); |
594 | 7.24k | #endif |
595 | | |
596 | 7.24k | for (auto & source : mSources) |
597 | 0 | { |
598 | 0 | source.PrepareEvents(mMaxFd, mSelected.mReadSet, mSelected.mWriteSet, mSelected.mErrorSet, mNextTimeout); |
599 | 0 | } |
600 | | |
601 | 7.24k | #if CHIP_SYSTEM_CONFIG_USE_SOCKETS |
602 | 7.24k | for (auto & w : mSocketWatchPool) |
603 | 463k | { |
604 | 463k | if (w.mFD != kInvalidFd) |
605 | 0 | { |
606 | 0 | if (mMaxFd < w.mFD) |
607 | 0 | { |
608 | 0 | mMaxFd = w.mFD; |
609 | 0 | } |
610 | 0 | if (w.mPendingIO.Has(SocketEventFlags::kRead)) |
611 | 0 | { |
612 | 0 | FD_SET(w.mFD, &mSelected.mReadSet); |
613 | 0 | } |
614 | 0 | if (w.mPendingIO.Has(SocketEventFlags::kWrite)) |
615 | 0 | { |
616 | 0 | FD_SET(w.mFD, &mSelected.mWriteSet); |
617 | 0 | } |
618 | 0 | } |
619 | 463k | } |
620 | 7.24k | #endif |
621 | 7.24k | } |
622 | | |
623 | | void LayerImplSelect::WaitForEvents() |
624 | 7.24k | { |
625 | 7.24k | mSelectResult = select(mMaxFd + 1, &mSelected.mReadSet, &mSelected.mWriteSet, &mSelected.mErrorSet, &mNextTimeout); |
626 | 7.24k | } |
627 | | |
628 | | void LayerImplSelect::HandleEvents() |
629 | 7.24k | { |
630 | 7.24k | assertChipStackLockedByCurrentThread(); |
631 | | |
632 | 7.24k | if (!IsSelectResultValid()) |
633 | 0 | { |
634 | 0 | VerifyOrReturn(errno != EINTR); // EINTR is not really an error (and we don't use it for signal handling) |
635 | 0 | ChipLogError(DeviceLayer, "Select failed: %" CHIP_ERROR_FORMAT, CHIP_ERROR_POSIX(errno).Format()); |
636 | 0 | return; |
637 | 0 | } |
638 | | |
639 | 7.24k | #if !CHIP_SYSTEM_CONFIG_USE_LIBEV |
640 | 7.24k | if (mSelectResult > 0 && FD_ISSET(mWakeEvent.GetReadFD(), &mSelected.mReadSet)) |
641 | 7.24k | { |
642 | 7.24k | mWakeEvent.Confirm(); |
643 | 7.24k | } |
644 | 7.24k | #endif |
645 | | |
646 | 7.24k | #if CHIP_SYSTEM_CONFIG_POSIX_LOCKING |
647 | 7.24k | mHandleSelectThread = pthread_self(); |
648 | 7.24k | #endif // CHIP_SYSTEM_CONFIG_POSIX_LOCKING |
649 | | |
650 | | // Obtain the list of currently expired timers. Any new timers added by timer callback are NOT handled on this pass, |
651 | | // since that could result in infinite handling of new timers blocking any other progress. |
652 | 7.24k | VerifyOrDieWithMsg(mExpiredTimers.Empty(), DeviceLayer, "Re-entry into HandleEvents from a timer callback?"); |
653 | 7.24k | mExpiredTimers = mTimerList.ExtractEarlier(Clock::Timeout(1) + SystemClock().GetMonotonicTimestamp()); |
654 | 7.24k | TimerList::Node * timer = nullptr; |
655 | 13.4k | while ((timer = mExpiredTimers.PopEarliest()) != nullptr) |
656 | 6.17k | { |
657 | 6.17k | mTimerPool.Invoke(timer); |
658 | 6.17k | } |
659 | | |
660 | 7.24k | #if CHIP_SYSTEM_CONFIG_USE_SOCKETS |
661 | | // Process socket events, if any |
662 | 7.24k | if (mSelectResult > 0) |
663 | 7.24k | { |
664 | 7.24k | for (auto & w : mSocketWatchPool) |
665 | 463k | { |
666 | 463k | if (w.mFD != kInvalidFd && w.mCallback != nullptr) |
667 | 0 | { |
668 | 0 | SocketEvents events = SocketEventsFromFDs(w.mFD, mSelected.mReadSet, mSelected.mWriteSet, mSelected.mErrorSet); |
669 | 0 | if (events.HasAny()) |
670 | 0 | { |
671 | 0 | w.mCallback(events, w.mCallbackData); |
672 | 0 | } |
673 | 0 | } |
674 | 463k | } |
675 | 7.24k | } |
676 | 7.24k | #endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS |
677 | | |
678 | 7.24k | if (mSelectResult >= 0) |
679 | 7.24k | { |
680 | 7.24k | for (auto & source : mSources) |
681 | 0 | { |
682 | 0 | source.ProcessEvents(mSelected.mReadSet, mSelected.mWriteSet, mSelected.mErrorSet); |
683 | 0 | } |
684 | 7.24k | } |
685 | | |
686 | | // Call HandleEvents for active loop handlers |
687 | 7.24k | auto loopIter = mLoopHandlers.begin(); |
688 | 7.24k | while (loopIter != mLoopHandlers.end()) |
689 | 0 | { |
690 | 0 | auto & loop = *loopIter++; // advance before calling out, in case a list modification clobbers the `next` pointer |
691 | 0 | if (LoopHandlerState(loop) == kLoopHandlerActive) |
692 | 0 | { |
693 | 0 | loop.HandleEvents(); |
694 | 0 | } |
695 | 0 | } |
696 | | |
697 | 7.24k | #if CHIP_SYSTEM_CONFIG_POSIX_LOCKING |
698 | 7.24k | mHandleSelectThread = PTHREAD_NULL; |
699 | 7.24k | #endif // CHIP_SYSTEM_CONFIG_POSIX_LOCKING |
700 | 7.24k | } |
701 | | |
702 | | #if CHIP_SYSTEM_CONFIG_USE_LIBEV |
703 | | |
704 | | void LayerImplSelect::HandleLibEvTimer(EV_P_ struct ev_timer * t, int revents) |
705 | | { |
706 | | TimerList::Node * timer = static_cast<TimerList::Node *>(t->data); |
707 | | VerifyOrDie(timer != nullptr); |
708 | | LayerImplSelect * layerP = dynamic_cast<LayerImplSelect *>(timer->mCallback.mSystemLayer); |
709 | | VerifyOrDie(layerP != nullptr); |
710 | | layerP->mTimerList.Remove(timer); |
711 | | layerP->mTimerPool.Invoke(timer); |
712 | | } |
713 | | |
714 | | void LayerImplSelect::HandleLibEvIoWatcher(EV_P_ struct ev_io * i, int revents) |
715 | | { |
716 | | SocketWatch * watch = static_cast<SocketWatch *>(i->data); |
717 | | if (watch != nullptr && watch->mCallback != nullptr && watch->mLayerImplSelectP != nullptr) |
718 | | { |
719 | | SocketEvents events; |
720 | | if (revents & EV_READ) |
721 | | { |
722 | | events.Set(SocketEventFlags::kRead); |
723 | | } |
724 | | if (revents & EV_WRITE) |
725 | | { |
726 | | events.Set(SocketEventFlags::kWrite); |
727 | | } |
728 | | if (events.HasAny()) |
729 | | { |
730 | | watch->mCallback(events, watch->mCallbackData); |
731 | | } |
732 | | } |
733 | | } |
734 | | |
735 | | #endif // CHIP_SYSTEM_CONFIG_USE_LIBEV |
736 | | |
737 | | #if CHIP_SYSTEM_CONFIG_USE_SOCKETS |
738 | | void LayerImplSelect::SocketWatch::Clear() |
739 | 200k | { |
740 | 200k | mFD = kInvalidFd; |
741 | 200k | mPendingIO.ClearAll(); |
742 | 200k | mCallback = nullptr; |
743 | 200k | mCallbackData = 0; |
744 | | #if CHIP_SYSTEM_CONFIG_USE_LIBEV |
745 | | mLayerImplSelectP = nullptr; |
746 | | #endif |
747 | 200k | } |
748 | | |
749 | | #if CHIP_SYSTEM_CONFIG_USE_LIBEV |
750 | | void LayerImplSelect::SocketWatch::DisableAndClear() |
751 | | { |
752 | | if (mLayerImplSelectP != nullptr && mLayerImplSelectP->mLibEvLoopP != nullptr) |
753 | | { |
754 | | ev_io_stop(mLayerImplSelectP->mLibEvLoopP, &mIoWatcher); |
755 | | } |
756 | | Clear(); |
757 | | } |
758 | | #endif // CHIP_SYSTEM_CONFIG_USE_LIBEV |
759 | | #endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS |
760 | | |
761 | | } // namespace System |
762 | | } // namespace chip |