Coverage Report

Created: 2026-02-09 06:53

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libzmq/src/epoll.cpp
Line
Count
Source
1
/* SPDX-License-Identifier: MPL-2.0 */
2
3
#include "precompiled.hpp"
4
#if defined ZMQ_IOTHREAD_POLLER_USE_EPOLL
5
#include "epoll.hpp"
6
7
#if !defined ZMQ_HAVE_WINDOWS
8
#include <unistd.h>
9
#endif
10
11
#include <stdlib.h>
12
#include <string.h>
13
#include <signal.h>
14
#include <algorithm>
15
#include <new>
16
17
#include "macros.hpp"
18
#include "err.hpp"
19
#include "config.hpp"
20
#include "i_poll_events.hpp"
21
22
#ifdef ZMQ_HAVE_WINDOWS
23
const zmq::epoll_t::epoll_fd_t zmq::epoll_t::epoll_retired_fd =
24
  INVALID_HANDLE_VALUE;
25
#endif
26
27
zmq::epoll_t::epoll_t (const zmq::thread_ctx_t &ctx_) :
28
1.78k
    worker_poller_base_t (ctx_)
29
1.78k
{
30
1.78k
#ifdef ZMQ_IOTHREAD_POLLER_USE_EPOLL_CLOEXEC
31
    //  Setting this option result in sane behaviour when exec() functions
32
    //  are used. Old sockets are closed and don't block TCP ports, avoid
33
    //  leaks, etc.
34
1.78k
    _epoll_fd = epoll_create1 (EPOLL_CLOEXEC);
35
#else
36
    _epoll_fd = epoll_create (1);
37
#endif
38
1.78k
    errno_assert (_epoll_fd != epoll_retired_fd);
39
1.78k
}
40
41
zmq::epoll_t::~epoll_t ()
42
1.78k
{
43
    //  Wait till the worker thread exits.
44
1.78k
    stop_worker ();
45
46
#ifdef ZMQ_HAVE_WINDOWS
47
    epoll_close (_epoll_fd);
48
#else
49
1.78k
    close (_epoll_fd);
50
1.78k
#endif
51
1.78k
    for (retired_t::iterator it = _retired.begin (), end = _retired.end ();
52
1.78k
         it != end; ++it) {
53
0
        LIBZMQ_DELETE (*it);
54
0
    }
55
1.78k
}
56
57
zmq::epoll_t::handle_t zmq::epoll_t::add_fd (fd_t fd_, i_poll_events *events_)
58
2.67k
{
59
2.67k
    check_thread ();
60
2.67k
    poll_entry_t *pe = new (std::nothrow) poll_entry_t;
61
2.67k
    alloc_assert (pe);
62
63
    //  The memset is not actually needed. It's here to prevent debugging
64
    //  tools to complain about using uninitialised memory.
65
2.67k
    memset (pe, 0, sizeof (poll_entry_t));
66
67
2.67k
    pe->fd = fd_;
68
2.67k
    pe->ev.events = 0;
69
2.67k
    pe->ev.data.ptr = pe;
70
2.67k
    pe->events = events_;
71
72
2.67k
    const int rc = epoll_ctl (_epoll_fd, EPOLL_CTL_ADD, fd_, &pe->ev);
73
2.67k
    errno_assert (rc != -1);
74
75
    //  Increase the load metric of the thread.
76
2.67k
    adjust_load (1);
77
78
2.67k
    return pe;
79
2.67k
}
80
81
void zmq::epoll_t::rm_fd (handle_t handle_)
82
2.67k
{
83
2.67k
    check_thread ();
84
2.67k
    poll_entry_t *pe = static_cast<poll_entry_t *> (handle_);
85
2.67k
    const int rc = epoll_ctl (_epoll_fd, EPOLL_CTL_DEL, pe->fd, &pe->ev);
86
2.67k
    errno_assert (rc != -1);
87
2.67k
    pe->fd = retired_fd;
88
2.67k
    _retired.push_back (pe);
89
90
    //  Decrease the load metric of the thread.
91
2.67k
    adjust_load (-1);
92
2.67k
}
93
94
void zmq::epoll_t::set_pollin (handle_t handle_)
95
2.67k
{
96
2.67k
    check_thread ();
97
2.67k
    poll_entry_t *pe = static_cast<poll_entry_t *> (handle_);
98
2.67k
    pe->ev.events |= EPOLLIN;
99
2.67k
    const int rc = epoll_ctl (_epoll_fd, EPOLL_CTL_MOD, pe->fd, &pe->ev);
100
2.67k
    errno_assert (rc != -1);
101
2.67k
}
102
103
void zmq::epoll_t::reset_pollin (handle_t handle_)
104
0
{
105
0
    check_thread ();
106
0
    poll_entry_t *pe = static_cast<poll_entry_t *> (handle_);
107
0
    pe->ev.events &= ~(static_cast<uint32_t> (EPOLLIN));
108
0
    const int rc = epoll_ctl (_epoll_fd, EPOLL_CTL_MOD, pe->fd, &pe->ev);
109
0
    errno_assert (rc != -1);
110
0
}
111
112
void zmq::epoll_t::set_pollout (handle_t handle_)
113
0
{
114
0
    check_thread ();
115
0
    poll_entry_t *pe = static_cast<poll_entry_t *> (handle_);
116
0
    pe->ev.events |= EPOLLOUT;
117
0
    const int rc = epoll_ctl (_epoll_fd, EPOLL_CTL_MOD, pe->fd, &pe->ev);
118
0
    errno_assert (rc != -1);
119
0
}
120
121
void zmq::epoll_t::reset_pollout (handle_t handle_)
122
0
{
123
0
    check_thread ();
124
0
    poll_entry_t *pe = static_cast<poll_entry_t *> (handle_);
125
0
    pe->ev.events &= ~(static_cast<uint32_t> (EPOLLOUT));
126
0
    const int rc = epoll_ctl (_epoll_fd, EPOLL_CTL_MOD, pe->fd, &pe->ev);
127
0
    errno_assert (rc != -1);
128
0
}
129
130
void zmq::epoll_t::stop ()
131
1.78k
{
132
1.78k
    check_thread ();
133
1.78k
}
134
135
int zmq::epoll_t::max_fds ()
136
1.78k
{
137
1.78k
    return -1;
138
1.78k
}
139
140
void zmq::epoll_t::loop ()
141
1.78k
{
142
1.78k
    epoll_event ev_buf[max_io_events];
143
144
3.56k
    while (true) {
145
        //  Execute any due timers.
146
3.56k
        const int timeout = static_cast<int> (execute_timers ());
147
148
3.56k
        if (get_load () == 0) {
149
1.78k
            if (timeout == 0)
150
1.78k
                break;
151
152
            // TODO sleep for timeout
153
0
            continue;
154
1.78k
        }
155
156
        //  Wait for events.
157
1.78k
        const int n = epoll_wait (_epoll_fd, &ev_buf[0], max_io_events,
158
1.78k
                                  timeout ? timeout : -1);
159
1.78k
        if (n == -1) {
160
0
            errno_assert (errno == EINTR);
161
0
            continue;
162
0
        }
163
164
3.56k
        for (int i = 0; i < n; i++) {
165
1.78k
            const poll_entry_t *const pe =
166
1.78k
              static_cast<const poll_entry_t *> (ev_buf[i].data.ptr);
167
168
1.78k
            if (NULL == pe)
169
0
                continue;
170
1.78k
            if (NULL == pe->events)
171
0
                continue;
172
1.78k
            if (pe->fd == retired_fd)
173
0
                continue;
174
1.78k
            if (ev_buf[i].events & (EPOLLERR | EPOLLHUP))
175
0
                pe->events->in_event ();
176
1.78k
            if (pe->fd == retired_fd)
177
0
                continue;
178
1.78k
            if (ev_buf[i].events & EPOLLOUT)
179
0
                pe->events->out_event ();
180
1.78k
            if (pe->fd == retired_fd)
181
0
                continue;
182
1.78k
            if (ev_buf[i].events & EPOLLIN)
183
1.78k
                pe->events->in_event ();
184
1.78k
        }
185
186
        //  Destroy retired event sources.
187
1.78k
        for (retired_t::iterator it = _retired.begin (), end = _retired.end ();
188
4.45k
             it != end; ++it) {
189
2.67k
            LIBZMQ_DELETE (*it);
190
2.67k
        }
191
1.78k
        _retired.clear ();
192
1.78k
    }
193
1.78k
}
194
195
#endif