Coverage Report

Created: 2026-06-30 06:52

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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