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