1515#include < stdexcept>
1616#include < string>
1717
18+ #ifdef USE_EPOLL
19+ #include < sys/epoll.h>
20+ #endif
21+
22+ #ifdef USE_KQUEUE
23+ #include < sys/event.h>
24+ #endif
25+
1826#ifdef USE_POLL
1927#include < poll.h>
2028#endif
@@ -145,11 +153,16 @@ bool Sock::IsSelectable(bool is_select) const
145153 return IsSelectableSocket (m_socket, is_select);
146154}
147155
148- bool Sock::Wait (std::chrono::milliseconds timeout, Event requested, SocketEventsMode event_mode , Event* occurred) const
156+ bool Sock::Wait (std::chrono::milliseconds timeout, Event requested, SocketEventsParams event_params , Event* occurred) const
149157{
150158 EventsPerSock events_per_sock{std::make_pair (m_socket, Events{requested})};
151159
152- if (!WaitMany (timeout, events_per_sock)) {
160+ if (auto [sem, _] = event_params; sem != SocketEventsMode::Poll && sem != SocketEventsMode::Select) {
161+ // We need to ensure we are only using a level-triggered mode because we are expecting
162+ // a direct correlation between the events reported and the one socket we are querying
163+ event_params = SocketEventsParams ();
164+ }
165+ if (!WaitMany (timeout, events_per_sock, event_params)) {
153166 return false ;
154167 }
155168
@@ -160,16 +173,16 @@ bool Sock::Wait(std::chrono::milliseconds timeout, Event requested, SocketEvents
160173 return true ;
161174}
162175
163- bool Sock::WaitMany (std::chrono::milliseconds timeout, EventsPerSock& events_per_sock, SocketEventsMode event_mode ) const
176+ bool Sock::WaitMany (std::chrono::milliseconds timeout, EventsPerSock& events_per_sock, SocketEventsParams event_params ) const
164177{
165- return WaitManyInternal (timeout, events_per_sock, event_mode );
178+ return WaitManyInternal (timeout, events_per_sock, event_params );
166179}
167180
168- bool Sock::WaitManyInternal (std::chrono::milliseconds timeout, EventsPerSock& events_per_sock, SocketEventsMode event_mode )
181+ bool Sock::WaitManyInternal (std::chrono::milliseconds timeout, EventsPerSock& events_per_sock, SocketEventsParams event_params )
169182{
170183 std::string debug_str;
171184
172- switch (event_mode )
185+ switch (event_params. m_event_mode )
173186 {
174187 case SocketEventsMode::Poll:
175188#ifdef USE_POLL
@@ -181,11 +194,21 @@ bool Sock::WaitManyInternal(std::chrono::milliseconds timeout, EventsPerSock& ev
181194 case SocketEventsMode::Select:
182195 return WaitManySelect (timeout, events_per_sock);
183196 case SocketEventsMode::EPoll:
184- debug_str += " Sock::Wait -- Unimplemented for epoll, falling back on " ;
197+ #ifdef USE_EPOLL
198+ assert (event_params.m_event_fd != INVALID_SOCKET);
199+ return WaitManyEPoll (timeout, events_per_sock, event_params.m_event_fd );
200+ #else
201+ debug_str += " Sock::Wait -- Support for epoll not compiled in, falling back on " ;
185202 break ;
203+ #endif /* USE_EPOLL */
186204 case SocketEventsMode::KQueue:
187- debug_str += " Sock::Wait -- Unimplemented for kqueue, falling back on " ;
205+ #ifdef USE_KQUEUE
206+ assert (event_params.m_event_fd != INVALID_SOCKET);
207+ return WaitManyKQueue (timeout, events_per_sock, event_params.m_event_fd );
208+ #else
209+ debug_str += " Sock::Wait -- Support for kqueue not compiled in, falling back on " ;
188210 break ;
211+ #endif /* USE_KQUEUE */
189212 default :
190213 assert (false );
191214 }
@@ -202,6 +225,75 @@ bool Sock::WaitManyInternal(std::chrono::milliseconds timeout, EventsPerSock& ev
202225#endif /* USE_POLL */
203226}
204227
228+ #ifdef USE_EPOLL
229+ bool Sock::WaitManyEPoll (std::chrono::milliseconds timeout, EventsPerSock& events_per_sock, SOCKET epoll_fd)
230+ {
231+ std::array<epoll_event, MAX_EVENTS> events{};
232+
233+ int ret = epoll_wait (epoll_fd, events.data (), events.size (), count_milliseconds (timeout));
234+ if (ret == SOCKET_ERROR) {
235+ return false ;
236+ }
237+
238+ // Events reported do not correspond to sockets requested in edge-triggered modes, we will clear the
239+ // entire map before populating it with our events data.
240+ events_per_sock.clear ();
241+
242+ for (int idx = 0 ; idx < ret; idx++) {
243+ auto & ev = events[idx];
244+ Event occurred = 0 ;
245+ if (ev.events & (EPOLLERR | EPOLLHUP)) {
246+ occurred |= ERR;
247+ } else {
248+ if (ev.events & EPOLLIN) {
249+ occurred |= RECV;
250+ }
251+ if (ev.events & EPOLLOUT) {
252+ occurred |= SEND;
253+ }
254+ }
255+ events_per_sock.emplace (static_cast <SOCKET>(ev.data .fd ), Sock::Events{/* req=*/ RECV | SEND, occurred});
256+ }
257+
258+ return true ;
259+ }
260+ #endif /* USE_EPOLL */
261+
262+ #ifdef USE_KQUEUE
263+ bool Sock::WaitManyKQueue (std::chrono::milliseconds timeout, EventsPerSock& events_per_sock, SOCKET kqueue_fd)
264+ {
265+ std::array<struct kevent , MAX_EVENTS> events{};
266+ struct timespec ts = MillisToTimespec (timeout);
267+
268+ int ret = kevent (kqueue_fd, nullptr , 0 , events.data (), events.size (), &ts);
269+ if (ret == SOCKET_ERROR) {
270+ return false ;
271+ }
272+
273+ // Events reported do not correspond to sockets requested in edge-triggered modes, we will clear the
274+ // entire map before populating it with our events data.
275+ events_per_sock.clear ();
276+
277+ for (int idx = 0 ; idx < ret; idx++) {
278+ auto & ev = events[idx];
279+ Event occurred = 0 ;
280+ if (ev.flags & (EV_ERROR | EV_EOF)) {
281+ occurred |= ERR;
282+ } else {
283+ if (ev.filter == EVFILT_READ) {
284+ occurred |= RECV;
285+ }
286+ if (ev.filter == EVFILT_WRITE) {
287+ occurred |= SEND;
288+ }
289+ }
290+ events_per_sock.emplace (static_cast <SOCKET>(ev.ident ), Sock::Events{/* req=*/ RECV | SEND, occurred});
291+ }
292+
293+ return true ;
294+ }
295+ #endif /* USE_KQUEUE */
296+
205297#ifdef USE_POLL
206298bool Sock::WaitManyPoll (std::chrono::milliseconds timeout, EventsPerSock& events_per_sock)
207299{
0 commit comments