Coverage Report

Created: 2026-08-13 07:17

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/pistache/src/common/os.cc
Line
Count
Source
1
/*
2
 * SPDX-FileCopyrightText: 2015 Mathieu Stefani
3
 *
4
 * SPDX-License-Identifier: Apache-2.0
5
 */
6
7
/* os.cc
8
   Mathieu Stefani, 13 August 2015
9
10
*/
11
12
#include <pistache/winornix.h>
13
#include <pistache/pist_quote.h>
14
15
#include <pistache/common.h>
16
#include <pistache/config.h>
17
#include <pistache/os.h>
18
19
#include PST_FCNTL_HDR
20
#include PIST_SOCKFNS_HDR
21
22
#include <pistache/pist_timelog.h>
23
24
#include <pistache/eventmeth.h>
25
26
#ifndef _USE_LIBEVENT
27
#include <sys/epoll.h>
28
#endif
29
30
#include PST_MISC_IO_HDR // unistd.h e.g. close
31
32
#include <algorithm>
33
#include <fstream>
34
#include <iterator>
35
#include <thread>
36
37
namespace Pistache
38
{
39
0
    unsigned int hardware_concurrency() { return std::thread::hardware_concurrency(); }
40
41
    bool make_non_blocking(em_socket_t fd)
42
0
    {
43
0
        PS_TIMEDBG_START;
44
45
0
        int flags = PST_FCNTL(fd, PST_F_GETFL, 0);
46
0
        if (flags == -1)
47
0
        {
48
0
            PS_LOG_WARNING_ARGS("make_non_blocking fail for fd %" PIST_QUOTE(PS_FD_PRNTFCD), fd);
49
0
            return false;
50
0
        }
51
52
0
        if (flags == PST_FCNTL_GETFL_UNKNOWN)
53
0
            flags = PST_O_NONBLOCK;
54
0
        else
55
0
            flags |= PST_O_NONBLOCK;
56
0
        int ret = PST_FCNTL(fd, PST_F_SETFL, flags);
57
#ifdef DEBUG
58
        if (ret == -1)
59
        {
60
            PS_LOG_WARNING_ARGS("make_non_blocking fail for fd %" PIST_QUOTE(PS_FD_PRNTFCD), fd);
61
        }
62
#endif
63
64
0
        return ret != -1;
65
0
    }
66
67
0
    CpuSet::CpuSet() { bits.reset(); }
68
69
0
    CpuSet::CpuSet(std::initializer_list<size_t> cpus) { set(cpus); }
70
71
0
    void CpuSet::clear() { bits.reset(); }
72
73
    CpuSet& CpuSet::set(size_t cpu)
74
0
    {
75
0
        if (cpu >= Size)
76
0
        {
77
0
            throw std::invalid_argument("Trying to set invalid cpu number");
78
0
        }
79
80
0
        bits.set(cpu);
81
0
        return *this;
82
0
    }
83
84
    CpuSet& CpuSet::unset(size_t cpu)
85
0
    {
86
0
        if (cpu >= Size)
87
0
        {
88
0
            throw std::invalid_argument("Trying to unset invalid cpu number");
89
0
        }
90
91
0
        bits.set(cpu, false);
92
0
        return *this;
93
0
    }
94
95
    CpuSet& CpuSet::set(std::initializer_list<size_t> cpus)
96
0
    {
97
0
        for (auto cpu : cpus)
98
0
            set(cpu);
99
0
        return *this;
100
0
    }
101
102
    CpuSet& CpuSet::unset(std::initializer_list<size_t> cpus)
103
0
    {
104
0
        for (auto cpu : cpus)
105
0
            unset(cpu);
106
0
        return *this;
107
0
    }
108
109
    CpuSet& CpuSet::setRange(size_t begin, size_t end)
110
0
    {
111
0
        if (begin > end)
112
0
        {
113
0
            throw std::range_error("Invalid range, begin > end");
114
0
        }
115
116
0
        for (size_t cpu = begin; cpu < end; ++cpu)
117
0
        {
118
0
            set(cpu);
119
0
        }
120
121
0
        return *this;
122
0
    }
123
124
    CpuSet& CpuSet::unsetRange(size_t begin, size_t end)
125
0
    {
126
0
        if (begin > end)
127
0
        {
128
0
            throw std::range_error("Invalid range, begin > end");
129
0
        }
130
131
0
        for (size_t cpu = begin; cpu < end; ++cpu)
132
0
        {
133
0
            unset(cpu);
134
0
        }
135
136
0
        return *this;
137
0
    }
138
139
    bool CpuSet::isSet(size_t cpu) const
140
0
    {
141
0
        if (cpu >= Size)
142
0
        {
143
0
            throw std::invalid_argument("Trying to test invalid cpu number");
144
0
        }
145
146
0
        return bits.test(cpu);
147
0
    }
148
149
0
    size_t CpuSet::count() const { return bits.count(); }
150
151
#ifdef _POSIX_C_SOURCE
152
    cpu_set_t CpuSet::toPosix() const
153
0
    {
154
0
        cpu_set_t cpu_set;
155
0
        CPU_ZERO(&cpu_set);
156
157
0
        for (size_t cpu = 0; cpu < Size; ++cpu)
158
0
        {
159
0
            if (bits.test(cpu))
160
0
                CPU_SET(cpu, &cpu_set);
161
0
        }
162
163
0
        return cpu_set;
164
0
    }
165
#endif
166
167
    namespace Polling
168
    {
169
170
        Event::Event(Tag _tag)
171
0
            : flags()
172
0
            , tag(_tag)
173
0
        { }
174
175
        Epoll::Epoll()
176
0
            : epoll_fd([&]()
177
#ifdef _USE_LIBEVENT
178
                       { return TRY_NULL_RET(EventMethFns::create(
179
                             Const::MaxEvents)); }
180
#else
181
0
                       { return TRY_RET(epoll_create(Const::MaxEvents)); }
182
0
#endif
183
0
                       ())
184
0
        { }
185
186
        Epoll::~Epoll()
187
0
        {
188
#ifdef _USE_LIBEVENT
189
            if (epoll_fd != nullptr)
190
                epoll_fd = 0; // EventMethEpollEquiv destructor to be called
191
#else
192
0
            if (epoll_fd >= 0)
193
0
                close(epoll_fd);
194
0
#endif
195
0
        }
196
197
        void Epoll::addFd(Fd fd, Flags<NotifyOn> interest, Tag tag,
198
                          [[maybe_unused]] Mode mode)
199
0
        {
200
0
            PS_TIMEDBG_START_ARGS("fd %" PIST_QUOTE(PS_FD_PRNTFCD), fd);
201
202
#ifdef _USE_LIBEVENT
203
            short events = static_cast<short>(epoll_fd->toEvEvents(interest));
204
            events |= EVM_PERSIST; // since EPOLLONESHOT not to be set
205
206
            if (mode == Mode::Edge)
207
                events |= EVM_ET;
208
            EventMethFns::setEmEventUserData(fd, tag.value_);
209
210
            TRY(epoll_fd->ctl(EvCtlAction::Add,
211
                              fd, events, nullptr /* time */));
212
213
#else
214
0
            struct epoll_event ev;
215
0
            ev.events = toEpollEvents(interest);
216
0
            if (mode == Mode::Edge)
217
0
                ev.events |= EPOLLET;
218
0
            ev.data.u64 = tag.value_;
219
220
0
            TRY(epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd, &ev));
221
0
#endif
222
0
        }
223
224
        void Epoll::addFdOneShot(Fd fd, Flags<NotifyOn> interest,
225
                                 Tag tag, Mode mode)
226
0
        {
227
0
            PS_TIMEDBG_START_ARGS("fd %" PIST_QUOTE(PS_FD_PRNTFCD), fd);
228
229
#ifdef _USE_LIBEVENT
230
            short events = static_cast<short>(epoll_fd->toEvEvents(interest));
231
232
            if (mode == Mode::Edge)
233
                events |= EVM_ET;
234
235
            // EPOLLONESHOT: after an event notified for the FD, the FD is
236
            // disabled in the interest list and no other events will be
237
            // reported.  The user must rearm the FD with a new event mask.
238
239
            // In libevent, there is the EV_PERSIST flag, which is the
240
            // equivalent of the inverse of EPOLLONESHOT. So for libevent any
241
            // event is assumed to be "oneshot" unless EVM_PERSIST is set.
242
243
            EventMethFns::setEmEventUserData(fd, tag.value_);
244
            TRY(epoll_fd->ctl(EvCtlAction::Add,
245
                              fd, events, nullptr /* time */));
246
#else
247
0
            struct epoll_event ev;
248
0
            ev.events = toEpollEvents(interest);
249
0
            ev.events |= EPOLLONESHOT;
250
0
            if (mode == Mode::Edge)
251
0
                ev.events |= EPOLLET;
252
0
            ev.data.u64 = tag.value_;
253
254
0
            TRY(epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd, &ev));
255
0
#endif
256
0
        }
257
258
        void Epoll::removeFd(Fd fd)
259
0
        {
260
0
            PS_TIMEDBG_START_ARGS("fd %" PIST_QUOTE(PS_FD_PRNTFCD), fd);
261
262
#ifdef _USE_LIBEVENT
263
            TRY(epoll_fd->ctl(EvCtlAction::Del,
264
                              fd, 0 /* events */, nullptr /* time */));
265
#else
266
0
            struct epoll_event ev;
267
0
            TRY(epoll_ctl(epoll_fd, EPOLL_CTL_DEL, fd, &ev));
268
0
#endif
269
0
        }
270
271
        void Epoll::rearmFd(Fd fd, Flags<NotifyOn> interest, Tag tag,
272
                            [[maybe_unused]] Mode mode)
273
0
        {
274
0
            PS_TIMEDBG_START_ARGS("fd %" PIST_QUOTE(PS_FD_PRNTFCD), fd);
275
276
#ifdef _USE_LIBEVENT
277
            short events = static_cast<short>(epoll_fd->toEvEvents(interest));
278
279
            // Why do we set EVM_PERSIST here? Since rearmFd is being called,
280
            // presumably fd was previously a one-shot event. You might think
281
            // it should continue to be a one-shot event. However, that does
282
            // not seem to be correct.
283
284
            // Per epoll_ctl man page, for CTL_MOD epoll_ctl will "Change the
285
            // settings associated with fd in the interest list to the new
286
            // settings specified in event [event being a parm to
287
            // epoll_ctl]". So CTL_MOD epoll_ctl will not retain EPOLLONESHOT
288
            // as a flag to the fd even if EPOLLONESHOT was previously set for
289
            // the fd. Accordingly, we must pass EVM_PERSIST here to mimic the
290
            // behaviour correctly, since Pistache does not set EPOLLONESHOT in
291
            // the epoll_ctl call below.
292
            events |= EVM_PERSIST;
293
294
            if (mode == Mode::Edge)
295
                events |= EVM_ET;
296
            EventMethFns::setEmEventUserData(fd, tag.value_);
297
            TRY(epoll_fd->ctl(EvCtlAction::Mod,
298
                              fd, events, nullptr /* time */));
299
300
#else
301
0
            struct epoll_event ev;
302
0
            ev.events = toEpollEvents(interest);
303
0
            if (mode == Mode::Edge)
304
0
                ev.events |= EPOLLET;
305
0
            ev.data.u64 = tag.value_;
306
307
0
            TRY(epoll_ctl(epoll_fd, EPOLL_CTL_MOD, fd, &ev));
308
0
#endif
309
0
        }
310
311
#ifdef DEBUG
312
        static void logFdAndNotifyOn(int i,
313
#ifdef _USE_LIBEVENT
314
                                     const EventMethEpollEquiv*
315
#else
316
                                     Fd
317
#endif
318
                                         epoll_fd,
319
                                     Fd fd,
320
                                     Polling::NotifyOn interest)
321
        {
322
            std::string str("#");
323
324
            std::stringstream ss;
325
            ss << i;
326
            str += ss.str();
327
328
            str += " epoll_fd ";
329
330
            std::stringstream ss2;
331
            ss2 << epoll_fd;
332
            str += ss2.str();
333
334
            str += ", fd ";
335
336
            std::stringstream ss3;
337
            ss3 << fd;
338
            str += ss3.str();
339
340
            if ((static_cast<unsigned int>(interest)) & (static_cast<unsigned int>(Polling::NotifyOn::Read)))
341
                str += " read";
342
            if ((static_cast<unsigned int>(interest)) & (static_cast<unsigned int>(Polling::NotifyOn::Write)))
343
                str += " write";
344
            if ((static_cast<unsigned int>(interest)) & (static_cast<unsigned int>(Polling::NotifyOn::Hangup)))
345
                str += " hangup";
346
            if ((static_cast<unsigned int>(interest)) & (static_cast<unsigned int>(Polling::NotifyOn::Shutdown)))
347
                str += " shutdown";
348
349
            PS_LOG_DEBUG_ARGS("%s", str.c_str());
350
        }
351
352
#ifdef _USE_LIBEVENT
353
#define PS_LOG_DBG_FD_AND_NOTIFY logFdAndNotifyOn(i,                  \
354
                                        epoll_fd.get(),               \
355
                                        reinterpret_cast<Fd>(tag.valueU64()), \
356
                                        event.flags)
357
#else
358
#define PS_LOG_DBG_FD_AND_NOTIFY logFdAndNotifyOn(i,                  \
359
                                     epoll_fd,                      \
360
                                     static_cast<Fd>(tag.valueU64()), \
361
                                     event.flags)
362
#endif
363
#else
364
#define PS_LOG_DBG_FD_AND_NOTIFY
365
#endif
366
367
        int Epoll::poll(std::vector<Event>& events,
368
                        const std::chrono::milliseconds timeout) const
369
0
        {
370
#ifdef _USE_LIBEVENT
371
            // Note; We can't use PIST_QUOTE(PS_FD_PRNTFCD) for this logging
372
            // because the Fd expression is different ("epoll_fd.get()"
373
            // vs. "epoll_fd")
374
            PS_TIMEDBG_START_ARGS("getReadyEmEvents on EMEE (epoll_fd) %p",
375
                                  epoll_fd.get());
376
#else
377
0
            PS_TIMEDBG_START_ARGS("epoll on EMEE (epoll_fd) %d",
378
0
                                  epoll_fd);
379
0
#endif
380
381
#ifdef _USE_LIBEVENT
382
            std::set<Fd> ready_evm_events;
383
            int ready_evs = -1;
384
385
            try { // wrapping a try/catch around this to make sure we don't
386
                  // miss out on calling unlockInterestMutexIfLocked below
387
            do
388
            {
389
                ready_evs = epoll_fd->getReadyEmEvents(
390
                          static_cast<int>(timeout.count()), ready_evm_events);
391
            } while (ready_evs < 0 && errno == EINTR);
392
393
            PS_LOG_DEBUG_ARGS("ready_evs %d", ready_evs);
394
395
            if ((ready_evs > 0) && (!ready_evm_events.empty()))
396
            {
397
#ifdef DEBUG
398
                int i = 0;
399
#endif
400
                for (std::set<Fd>::iterator it = ready_evm_events.begin();
401
                     it != ready_evm_events.end(); it++
402
#ifdef DEBUG
403
                         , i++
404
#endif
405
                    )
406
                {
407
                    Fd fd(*it);
408
#ifdef DEBUG
409
                    if (!fd)
410
                    {
411
                        PS_LOG_ERR("fd is NULL");
412
                        continue;
413
                    }
414
#endif
415
416
                    const Tag tag(EventMethFns::getEmEventUserData(fd));
417
                    Event event(tag);
418
                    event.flags = epoll_fd->toNotifyOn(fd); // uses fd's ready_flags
419
                    PS_LOG_DBG_FD_AND_NOTIFY;
420
                    events.push_back(event);
421
422
                    // fd's ready_flags have been transferred to event.flags
423
                    EventMethFns::resetEmEventReadyFlags(fd);
424
                }
425
            }
426
            } // end of "try {"
427
            catch(...)
428
            {
429
                PS_LOG_ERR("Throw while polling");
430
            }
431
432
            // unlockInterestMutexIfLocked must be called after
433
            // getReadyEmEvents, and after we have finished processing the
434
            // ready_evm_events set. Leaving the mutex locked to this point
435
            // prevents any other thread closing/invalidating an Fd in the
436
            // ready_evm_events set while we're processing the set above.
437
            epoll_fd->unlockInterestMutexIfLocked();
438
439
            if (ready_evs <= 0)
440
                return (ready_evs);
441
442
            if (ready_evm_events.empty())
443
                return (0); // 0 FDs
444
445
            return(static_cast<int>(events.size()));
446
447
#else // not ifdef _USE_LIBEVENT
448
449
0
            struct epoll_event evs[Const::MaxEvents];
450
451
0
            int ready_fds = -1;
452
0
            do
453
0
            {
454
0
                ready_fds = ::epoll_wait(epoll_fd, evs, Const::MaxEvents,
455
0
                                         static_cast<int>(timeout.count()));
456
0
                PS_LOG_DEBUG_ARGS("done epoll_wait on fd %" PIST_QUOTE(PS_FD_PRNTFCD),
457
0
                                  epoll_fd);
458
0
            } while (ready_fds < 0 && errno == EINTR);
459
0
            PS_LOG_DEBUG_ARGS("while loop done for epoll_wait on fd %" PIST_QUOTE(PS_FD_PRNTFCD) ", ready_fds %d",
460
0
                              epoll_fd, ready_fds);
461
462
0
            for (int i = 0; i < ready_fds; ++i)
463
0
            {
464
0
                const struct epoll_event* ev = evs + i;
465
466
0
                const Tag tag(ev->data.u64);
467
468
0
                Event event(tag);
469
0
                event.flags = toNotifyOn(ev->events);
470
0
                PS_LOG_DBG_FD_AND_NOTIFY;
471
0
                events.push_back(event);
472
0
            }
473
474
0
            return ready_fds;
475
0
#endif
476
0
        }
477
478
#ifdef _USE_LIBEVENT
479
        // static method
480
        Fd Epoll::em_event_new(em_socket_t actual_fd, // file desc, signal, or -1
481
                               short flags, // EVM_... flags
482
                               // For setfd and setfl arg:
483
                               //   Zero or pos number - set flags to value of
484
                               //   arg, and clear any other flags
485
                               //   F_SETFDL_NOTHING - change nothing
486
                               //   Other neg num - set flags that are set in
487
                               //   (0 - arg), but don't clear any flags
488
                               int f_setfd_flags, // e.g. FD_CLOEXEC
489
                               int f_setfl_flags // e.g. O_NONBLOCK
490
        )
491
        {
492
            return (EventMethFns::em_event_new(actual_fd, flags,
493
                                                      f_setfd_flags, f_setfl_flags));
494
        }
495
496
        Fd Epoll::em_timer_new(PST_CLOCK_ID_T clock_id,
497
                               // For setfd and setfl arg:
498
                               //   F_SETFDL_NOTHING - change nothing
499
                               //   Zero or pos number that is not
500
                               //   F_SETFDL_NOTHING - set flags to value of
501
                               //   arg, and clear any other flags
502
                               //   Neg number that is not F_SETFDL_NOTHING
503
                               //   - set flags that are set in (0 - arg),
504
                               //   but don't clear any flags
505
                               int f_setfd_flags, // e.g. FD_CLOEXEC
506
                               int f_setfl_flags) // e.g. O_NONBLOCK
507
        {
508
            if (!epoll_fd)
509
                throw std::runtime_error("epoll_fd null");
510
511
            return (EventMethFns::em_timer_new(clock_id,
512
                                               f_setfd_flags, f_setfl_flags,
513
                                               epoll_fd.get()));
514
        }
515
516
        // For "eventfd-style" descriptors
517
        // Note that FdEventFd does not have an "actual fd" that the caller can
518
        // access; the caller must use FdEventFd's member functions instead
519
        FdEventFd Epoll::em_eventfd_new(unsigned int initval,
520
                                        int f_setfd_flags, // e.g. FD_CLOEXEC
521
                                        int f_setfl_flags) // e.g. O_NONBLOCK
522
        {
523
            return (EventMethFns::em_eventfd_new(initval,
524
                                                        f_setfd_flags, f_setfl_flags));
525
        }
526
527
#endif // of ifdef _USE_LIBEVENT
528
529
#ifndef _USE_LIBEVENT
530
531
        int Epoll::toEpollEvents(const Flags<NotifyOn>& interest)
532
0
        {
533
0
            int events = 0;
534
535
0
            if (interest.hasFlag(NotifyOn::Read))
536
0
                events |= EPOLLIN;
537
0
            if (interest.hasFlag(NotifyOn::Write))
538
0
                events |= EPOLLOUT;
539
0
            if (interest.hasFlag(NotifyOn::Hangup))
540
0
                events |= EPOLLHUP;
541
0
            if (interest.hasFlag(NotifyOn::Shutdown))
542
0
                events |= EPOLLRDHUP;
543
544
0
            return events;
545
0
        }
546
547
        Flags<NotifyOn> Epoll::toNotifyOn(int events)
548
0
        {
549
0
            Flags<NotifyOn> flags;
550
551
0
            if (events & EPOLLIN)
552
0
                flags.setFlag(NotifyOn::Read);
553
0
            if (events & EPOLLOUT)
554
0
                flags.setFlag(NotifyOn::Write);
555
0
            if (events & EPOLLHUP)
556
0
                flags.setFlag(NotifyOn::Hangup);
557
0
            if (events & EPOLLRDHUP)
558
0
            {
559
0
                flags.setFlag(NotifyOn::Shutdown);
560
0
            }
561
562
0
            return flags;
563
0
        }
564
565
#endif
566
567
    } // namespace Polling
568
569
    NotifyFd::NotifyFd()
570
0
        : event_fd(PS_FD_EMPTY)
571
0
    { }
572
573
    NotifyFd::~NotifyFd()
574
0
    {
575
0
        if (event_fd != PS_FD_EMPTY)
576
0
        {
577
0
            CLOSE_FD(event_fd);
578
0
            event_fd = PS_FD_EMPTY;
579
0
        }
580
0
    }
581
582
    Polling::Tag NotifyFd::bind(Polling::Epoll& poller)
583
0
    {
584
#ifdef _USE_LIBEVENT
585
        FdEventFd emefd = TRY_NULL_RET(Polling::Epoll::em_eventfd_new(
586
            0, PST_FD_CLOEXEC, PST_O_NONBLOCK));
587
588
        event_fd = EventMethFns::getAsEmEvent(emefd);
589
#else
590
0
        event_fd = TRY_RET(eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC));
591
0
#endif
592
593
0
        Polling::Tag tag(event_fd);
594
595
0
        PS_LOG_DEBUG_ARGS("Add read fd %" PIST_QUOTE(PS_FD_PRNTFCD), event_fd);
596
0
        poller.addFd(event_fd, Flags<Polling::NotifyOn>(Polling::NotifyOn::Read), tag,
597
0
                     Polling::Mode::Edge);
598
0
        return tag;
599
0
    }
600
601
    void NotifyFd::unbind(Polling::Epoll& poller)
602
0
    {
603
0
        if (event_fd != PS_FD_EMPTY)
604
0
        {
605
0
            PS_LOG_DEBUG_ARGS("Remove and close event_fd %" PIST_QUOTE(PS_FD_PRNTFCD), event_fd);
606
607
0
            poller.removeFd(event_fd);
608
0
            CLOSE_FD(event_fd);
609
0
            event_fd = PS_FD_EMPTY;
610
0
        }
611
0
    }
612
613
    bool NotifyFd::isBound() const
614
0
    {
615
0
        return (event_fd != PS_FD_EMPTY);
616
0
    }
617
618
0
    Polling::Tag NotifyFd::tag() const { return Polling::Tag(event_fd); }
619
620
    void NotifyFd::notify() const
621
0
    {
622
0
        PS_TIMEDBG_START_CURLY;
623
624
0
        if (!isBound())
625
0
            throw std::runtime_error("Can not notify an unbound fd");
626
627
0
        uint64_t val = 1;
628
0
        TRY(WRITE_EFD(event_fd, val));
629
0
    }
630
631
    void NotifyFd::read() const
632
0
    {
633
0
        PS_TIMEDBG_START_THIS;
634
635
0
        if (!isBound())
636
0
            throw std::runtime_error("Can not read an unbound fd");
637
638
0
        uint64_t val = 0;
639
0
        TRY(READ_EFD(event_fd, &val));
640
0
    }
641
642
    bool NotifyFd::tryRead() const
643
0
    {
644
0
        PS_TIMEDBG_START_THIS;
645
646
0
        if (!isBound())
647
0
            throw std::runtime_error("Can not try to read if unbound");
648
649
0
        uint64_t val = 0;
650
0
        int res      = TRY_RET(READ_EFD(event_fd, &val));
651
#ifdef DEBUG
652
        if (res != 0) // 0 is success
653
            PS_LOG_DEBUG_ARGS("FdEventFd %p read fail", event_fd);
654
#endif
655
656
0
        if (res != 0) // 0 is success
657
0
        {
658
0
            if (errno == EAGAIN || errno == EWOULDBLOCK)
659
0
                return false;
660
0
            throw std::runtime_error("Failed to read eventfd");
661
0
        }
662
663
0
        return true;
664
0
    }
665
666
} // namespace Pistache