epoll.cpp 6.23 KB
Newer Older
Martin Sustrik's avatar
Martin Sustrik committed
1
/*
2
    Copyright (c) 2007-2016 Contributors as noted in the AUTHORS file
Martin Sustrik's avatar
Martin Sustrik committed
3

4
    This file is part of libzmq, the ZeroMQ core engine in C++.
Martin Sustrik's avatar
Martin Sustrik committed
5

6 7 8
    libzmq is free software; you can redistribute it and/or modify it under
    the terms of the GNU Lesser General Public License (LGPL) as published
    by the Free Software Foundation; either version 3 of the License, or
Martin Sustrik's avatar
Martin Sustrik committed
9 10
    (at your option) any later version.

11 12 13 14 15 16 17 18 19 20 21 22 23 24
    As a special exception, the Contributors give you permission to link
    this library with independent modules to produce an executable,
    regardless of the license terms of these independent modules, and to
    copy and distribute the resulting executable under terms of your choice,
    provided that you also meet, for each linked independent module, the
    terms and conditions of the license of that module. An independent
    module is a module which is not derived from or based on this library.
    If you modify this library, you must extend this exception to your
    version of the library.

    libzmq is distributed in the hope that it will be useful, but WITHOUT
    ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
    FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
    License for more details.
Martin Sustrik's avatar
Martin Sustrik committed
25

26
    You should have received a copy of the GNU Lesser General Public License
Martin Sustrik's avatar
Martin Sustrik committed
27 28 29
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

30
#include "precompiled.hpp"
31
#if defined ZMQ_IOTHREAD_POLLER_USE_EPOLL
32 33 34 35 36
#include "epoll.hpp"

#if !defined ZMQ_HAVE_WINDOWS
#include <unistd.h>
#endif
Martin Sustrik's avatar
Martin Sustrik committed
37 38 39

#include <stdlib.h>
#include <string.h>
40
#include <signal.h>
Martin Sustrik's avatar
Martin Sustrik committed
41
#include <algorithm>
42
#include <new>
Martin Sustrik's avatar
Martin Sustrik committed
43

44
#include "macros.hpp"
Martin Sustrik's avatar
Martin Sustrik committed
45 46 47 48
#include "err.hpp"
#include "config.hpp"
#include "i_poll_events.hpp"

49 50 51 52 53
#ifdef ZMQ_HAVE_WINDOWS
const zmq::epoll_t::epoll_fd_t zmq::epoll_t::epoll_retired_fd =
  INVALID_HANDLE_VALUE;
#endif

54
zmq::epoll_t::epoll_t (const zmq::thread_ctx_t &ctx_) :
55
    worker_poller_base_t (ctx_)
Martin Sustrik's avatar
Martin Sustrik committed
56
{
57 58 59 60
#ifdef ZMQ_USE_EPOLL_CLOEXEC
    //  Setting this option result in sane behaviour when exec() functions
    //  are used. Old sockets are closed and don't block TCP ports, avoid
    //  leaks, etc.
61
    _epoll_fd = epoll_create1 (EPOLL_CLOEXEC);
62
#else
63
    _epoll_fd = epoll_create (1);
64
#endif
65
    errno_assert (_epoll_fd != epoll_retired_fd);
Martin Sustrik's avatar
Martin Sustrik committed
66 67
}

Martin Sustrik's avatar
Martin Sustrik committed
68
zmq::epoll_t::~epoll_t ()
Martin Sustrik's avatar
Martin Sustrik committed
69
{
70
    //  Wait till the worker thread exits.
71
    stop_worker ();
72

73
#ifdef ZMQ_HAVE_WINDOWS
74
    epoll_close (_epoll_fd);
75
#else
76
    close (_epoll_fd);
77
#endif
78
    for (retired_t::iterator it = _retired.begin (); it != _retired.end ();
79 80
         ++it) {
        LIBZMQ_DELETE (*it);
81
    }
Martin Sustrik's avatar
Martin Sustrik committed
82 83
}

84
zmq::epoll_t::handle_t zmq::epoll_t::add_fd (fd_t fd_, i_poll_events *events_)
Martin Sustrik's avatar
Martin Sustrik committed
85
{
86
    check_thread ();
87
    poll_entry_t *pe = new (std::nothrow) poll_entry_t;
88
    alloc_assert (pe);
Martin Sustrik's avatar
Martin Sustrik committed
89 90 91 92 93 94 95 96 97 98

    //  The memset is not actually needed. It's here to prevent debugging
    //  tools to complain about using uninitialised memory.
    memset (pe, 0, sizeof (poll_entry_t));

    pe->fd = fd_;
    pe->ev.events = 0;
    pe->ev.data.ptr = pe;
    pe->events = events_;

99
    int rc = epoll_ctl (_epoll_fd, EPOLL_CTL_ADD, fd_, &pe->ev);
Martin Sustrik's avatar
Martin Sustrik committed
100 101 102
    errno_assert (rc != -1);

    //  Increase the load metric of the thread.
103
    adjust_load (1);
Martin Sustrik's avatar
Martin Sustrik committed
104

105
    return pe;
Martin Sustrik's avatar
Martin Sustrik committed
106 107
}

Martin Sustrik's avatar
Martin Sustrik committed
108
void zmq::epoll_t::rm_fd (handle_t handle_)
Martin Sustrik's avatar
Martin Sustrik committed
109
{
110
    check_thread ();
111
    poll_entry_t *pe = static_cast<poll_entry_t *> (handle_);
112
    int rc = epoll_ctl (_epoll_fd, EPOLL_CTL_DEL, pe->fd, &pe->ev);
Martin Sustrik's avatar
Martin Sustrik committed
113 114
    errno_assert (rc != -1);
    pe->fd = retired_fd;
115 116 117
    _retired_sync.lock ();
    _retired.push_back (pe);
    _retired_sync.unlock ();
Martin Sustrik's avatar
Martin Sustrik committed
118 119

    //  Decrease the load metric of the thread.
120
    adjust_load (-1);
Martin Sustrik's avatar
Martin Sustrik committed
121 122
}

Martin Sustrik's avatar
Martin Sustrik committed
123
void zmq::epoll_t::set_pollin (handle_t handle_)
Martin Sustrik's avatar
Martin Sustrik committed
124
{
125
    check_thread ();
126
    poll_entry_t *pe = static_cast<poll_entry_t *> (handle_);
Martin Sustrik's avatar
Martin Sustrik committed
127
    pe->ev.events |= EPOLLIN;
128
    int rc = epoll_ctl (_epoll_fd, EPOLL_CTL_MOD, pe->fd, &pe->ev);
Martin Sustrik's avatar
Martin Sustrik committed
129 130 131
    errno_assert (rc != -1);
}

Martin Sustrik's avatar
Martin Sustrik committed
132
void zmq::epoll_t::reset_pollin (handle_t handle_)
Martin Sustrik's avatar
Martin Sustrik committed
133
{
134
    check_thread ();
135 136
    poll_entry_t *pe = static_cast<poll_entry_t *> (handle_);
    pe->ev.events &= ~(static_cast<short> (EPOLLIN));
137
    int rc = epoll_ctl (_epoll_fd, EPOLL_CTL_MOD, pe->fd, &pe->ev);
Martin Sustrik's avatar
Martin Sustrik committed
138 139 140
    errno_assert (rc != -1);
}

Martin Sustrik's avatar
Martin Sustrik committed
141
void zmq::epoll_t::set_pollout (handle_t handle_)
Martin Sustrik's avatar
Martin Sustrik committed
142
{
143
    check_thread ();
144
    poll_entry_t *pe = static_cast<poll_entry_t *> (handle_);
Martin Sustrik's avatar
Martin Sustrik committed
145
    pe->ev.events |= EPOLLOUT;
146
    int rc = epoll_ctl (_epoll_fd, EPOLL_CTL_MOD, pe->fd, &pe->ev);
Martin Sustrik's avatar
Martin Sustrik committed
147 148 149
    errno_assert (rc != -1);
}

Martin Sustrik's avatar
Martin Sustrik committed
150
void zmq::epoll_t::reset_pollout (handle_t handle_)
Martin Sustrik's avatar
Martin Sustrik committed
151
{
152
    check_thread ();
153 154
    poll_entry_t *pe = static_cast<poll_entry_t *> (handle_);
    pe->ev.events &= ~(static_cast<short> (EPOLLOUT));
155
    int rc = epoll_ctl (_epoll_fd, EPOLL_CTL_MOD, pe->fd, &pe->ev);
Martin Sustrik's avatar
Martin Sustrik committed
156 157 158
    errno_assert (rc != -1);
}

Martin Sustrik's avatar
Martin Sustrik committed
159
void zmq::epoll_t::stop ()
Martin Sustrik's avatar
Martin Sustrik committed
160
{
161
    check_thread ();
Martin Sustrik's avatar
Martin Sustrik committed
162 163
}

Richard Newton's avatar
Richard Newton committed
164
int zmq::epoll_t::max_fds ()
165 166 167 168
{
    return -1;
}

Martin Sustrik's avatar
Martin Sustrik committed
169
void zmq::epoll_t::loop ()
Martin Sustrik's avatar
Martin Sustrik committed
170
{
171
    epoll_event ev_buf[max_io_events];
Martin Sustrik's avatar
Martin Sustrik committed
172

173
    while (true) {
174
        //  Execute any due timers.
175
        int timeout = static_cast<int> (execute_timers ());
Martin Sustrik's avatar
Martin Sustrik committed
176

177 178 179 180 181 182 183 184
        if (get_load () == 0) {
            if (timeout == 0)
                break;

            // TODO sleep for timeout
            continue;
        }

185
        //  Wait for events.
186
        int n = epoll_wait (_epoll_fd, &ev_buf[0], max_io_events,
187
                            timeout ? timeout : -1);
Martin Hurton's avatar
Martin Hurton committed
188 189
        if (n == -1) {
            errno_assert (errno == EINTR);
Martin Sustrik's avatar
Martin Sustrik committed
190
            continue;
Martin Hurton's avatar
Martin Hurton committed
191
        }
Martin Sustrik's avatar
Martin Sustrik committed
192

193
        for (int i = 0; i < n; i++) {
194 195
            poll_entry_t *pe =
              (static_cast<poll_entry_t *> (ev_buf[i].data.ptr));
Martin Sustrik's avatar
Martin Sustrik committed
196 197 198

            if (pe->fd == retired_fd)
                continue;
199
            if (ev_buf[i].events & (EPOLLERR | EPOLLHUP))
Martin Sustrik's avatar
Martin Sustrik committed
200 201
                pe->events->in_event ();
            if (pe->fd == retired_fd)
202 203
                continue;
            if (ev_buf[i].events & EPOLLOUT)
Martin Sustrik's avatar
Martin Sustrik committed
204 205 206
                pe->events->out_event ();
            if (pe->fd == retired_fd)
                continue;
207
            if (ev_buf[i].events & EPOLLIN)
Martin Sustrik's avatar
Martin Sustrik committed
208 209 210 211
                pe->events->in_event ();
        }

        //  Destroy retired event sources.
212 213
        _retired_sync.lock ();
        for (retired_t::iterator it = _retired.begin (); it != _retired.end ();
214 215
             ++it) {
            LIBZMQ_DELETE (*it);
216
        }
217 218
        _retired.clear ();
        _retired_sync.unlock ();
Martin Sustrik's avatar
Martin Sustrik committed
219 220 221 222
    }
}

#endif