select.cpp 5.52 KB
Newer Older
Martin Sustrik's avatar
Martin Sustrik committed
1
/*
2
    Copyright (c) 2007-2014 Contributors as noted in the AUTHORS file
Martin Sustrik's avatar
Martin Sustrik committed
3 4 5 6

    This file is part of 0MQ.

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

    0MQ 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
14
    GNU Lesser General Public License for more details.
Martin Sustrik's avatar
Martin Sustrik committed
15

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

20 21 22
#include "select.hpp"
#if defined ZMQ_USE_SELECT

Martin Sustrik's avatar
Martin Sustrik committed
23
#include "platform.hpp"
24 25
#if defined ZMQ_HAVE_WINDOWS
#include "windows.hpp"
Martin Sustrik's avatar
Martin Sustrik committed
26
#elif defined ZMQ_HAVE_HPUX
Martin Sustrik's avatar
Martin Sustrik committed
27 28 29
#include <sys/param.h>
#include <sys/types.h>
#include <sys/time.h>
Martin Sustrik's avatar
Martin Sustrik committed
30
#elif defined ZMQ_HAVE_OPENVMS
Martin Sustrik's avatar
Martin Sustrik committed
31 32 33 34 35 36
#include <sys/types.h>
#include <sys/time.h>
#else
#include <sys/select.h>
#endif

37 38 39
#include <string.h>
#include <algorithm>

Martin Sustrik's avatar
Martin Sustrik committed
40 41 42 43
#include "err.hpp"
#include "config.hpp"
#include "i_poll_events.hpp"

Martin Sustrik's avatar
Martin Sustrik committed
44
zmq::select_t::select_t () :
Martin Sustrik's avatar
Martin Sustrik committed
45 46 47 48 49 50 51 52 53 54
    maxfd (retired_fd),
    retired (false),
    stopping (false)
{
    //  Clear file descriptor sets.
    FD_ZERO (&source_set_in);
    FD_ZERO (&source_set_out);
    FD_ZERO (&source_set_err);
}

55 56
zmq::select_t::~select_t ()
{
57
    worker.stop ();
58 59
}

60
zmq::select_t::handle_t zmq::select_t::add_fd (fd_t fd_, i_poll_events *events_)
Martin Sustrik's avatar
Martin Sustrik committed
61 62 63 64 65
{
    //  Store the file descriptor.
    fd_entry_t entry = {fd_, events_};
    fds.push_back (entry);

66 67 68 69
    //  Ensure we do not attempt to select () on more than FD_SETSIZE
    //  file descriptors.
    zmq_assert (fds.size () <= FD_SETSIZE);

Martin Sustrik's avatar
Martin Sustrik committed
70 71 72 73 74 75 76 77
    //  Start polling on errors.
    FD_SET (fd_, &source_set_err);

    //  Adjust maxfd if necessary.
    if (fd_ > maxfd)
        maxfd = fd_;

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

80
    return fd_;
Martin Sustrik's avatar
Martin Sustrik committed
81 82
}

Martin Sustrik's avatar
Martin Sustrik committed
83
void zmq::select_t::rm_fd (handle_t handle_)
Martin Sustrik's avatar
Martin Sustrik committed
84 85 86
{
    //  Mark the descriptor as retired.
    fd_set_t::iterator it;
87
    for (it = fds.begin (); it != fds.end (); ++it)
88
        if (it->fd == handle_)
Martin Sustrik's avatar
Martin Sustrik committed
89
            break;
Martin Sustrik's avatar
Martin Sustrik committed
90
    zmq_assert (it != fds.end ());
Martin Sustrik's avatar
Martin Sustrik committed
91 92 93 94
    it->fd = retired_fd;
    retired = true;

    //  Stop polling on the descriptor.
95 96 97
    FD_CLR (handle_, &source_set_in);
    FD_CLR (handle_, &source_set_out);
    FD_CLR (handle_, &source_set_err);
Martin Sustrik's avatar
Martin Sustrik committed
98 99

    //  Discard all events generated on this file descriptor.
100 101 102
    FD_CLR (handle_, &readfds);
    FD_CLR (handle_, &writefds);
    FD_CLR (handle_, &exceptfds);
Martin Sustrik's avatar
Martin Sustrik committed
103 104 105

    //  Adjust the maxfd attribute if we have removed the
    //  highest-numbered file descriptor.
106
    if (handle_ == maxfd) {
Martin Sustrik's avatar
Martin Sustrik committed
107
        maxfd = retired_fd;
108
        for (fd_set_t::iterator it = fds.begin (); it != fds.end (); ++it)
Martin Sustrik's avatar
Martin Sustrik committed
109 110 111 112 113
            if (it->fd > maxfd)
                maxfd = it->fd;
    }

    //  Decrease the load metric of the thread.
114
    adjust_load (-1);
Martin Sustrik's avatar
Martin Sustrik committed
115 116
}

Martin Sustrik's avatar
Martin Sustrik committed
117
void zmq::select_t::set_pollin (handle_t handle_)
Martin Sustrik's avatar
Martin Sustrik committed
118
{
119
    FD_SET (handle_, &source_set_in);
Martin Sustrik's avatar
Martin Sustrik committed
120 121
}

Martin Sustrik's avatar
Martin Sustrik committed
122
void zmq::select_t::reset_pollin (handle_t handle_)
Martin Sustrik's avatar
Martin Sustrik committed
123
{
124
    FD_CLR (handle_, &source_set_in);
Martin Sustrik's avatar
Martin Sustrik committed
125 126
}

Martin Sustrik's avatar
Martin Sustrik committed
127
void zmq::select_t::set_pollout (handle_t handle_)
Martin Sustrik's avatar
Martin Sustrik committed
128
{
129
    FD_SET (handle_, &source_set_out);
Martin Sustrik's avatar
Martin Sustrik committed
130 131
}

Martin Sustrik's avatar
Martin Sustrik committed
132
void zmq::select_t::reset_pollout (handle_t handle_)
Martin Sustrik's avatar
Martin Sustrik committed
133
{
134
    FD_CLR (handle_, &source_set_out);
Martin Sustrik's avatar
Martin Sustrik committed
135 136
}

Martin Sustrik's avatar
Martin Sustrik committed
137
void zmq::select_t::start ()
Martin Sustrik's avatar
Martin Sustrik committed
138 139 140 141
{
    worker.start (worker_routine, this);
}

Martin Sustrik's avatar
Martin Sustrik committed
142
void zmq::select_t::stop ()
Martin Sustrik's avatar
Martin Sustrik committed
143 144 145 146
{
    stopping = true;
}

Richard Newton's avatar
Richard Newton committed
147
int zmq::select_t::max_fds ()
148 149 150 151
{
    return FD_SETSIZE;
}

Martin Sustrik's avatar
Martin Sustrik committed
152
void zmq::select_t::loop ()
Martin Sustrik's avatar
Martin Sustrik committed
153 154 155
{
    while (!stopping) {

156
        //  Execute any due timers.
157
        int timeout = (int) execute_timers ();
158

Martin Sustrik's avatar
Martin Sustrik committed
159 160 161 162 163 164
        //  Intialise the pollsets.
        memcpy (&readfds, &source_set_in, sizeof source_set_in);
        memcpy (&writefds, &source_set_out, sizeof source_set_out);
        memcpy (&exceptfds, &source_set_err, sizeof source_set_err);

        //  Wait for events.
Martin Sustrik's avatar
Martin Sustrik committed
165 166
        struct timeval tv = {(long) (timeout / 1000),
            (long) (timeout % 1000 * 1000)};
Martin Sustrik's avatar
Martin Sustrik committed
167
#ifdef ZMQ_HAVE_WINDOWS
168 169
        int rc = select (0, &readfds, &writefds, &exceptfds,
            timeout ? &tv : NULL);
Martin Sustrik's avatar
Martin Sustrik committed
170 171
        wsa_assert (rc != SOCKET_ERROR);
#else
172 173
        int rc = select (maxfd + 1, &readfds, &writefds, &exceptfds,
            timeout ? &tv : NULL);
Martin Hurton's avatar
Martin Hurton committed
174 175
        if (rc == -1) {
            errno_assert (errno == EINTR);
Martin Sustrik's avatar
Martin Sustrik committed
176
            continue;
Martin Hurton's avatar
Martin Hurton committed
177
        }
Martin Sustrik's avatar
Martin Sustrik committed
178 179
#endif

180 181 182
        //  If there are no events (i.e. it's a timeout) there's no point
        //  in checking the pollset.
        if (rc == 0)
Martin Sustrik's avatar
Martin Sustrik committed
183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201
            continue;

        for (fd_set_t::size_type i = 0; i < fds.size (); i ++) {
            if (fds [i].fd == retired_fd)
                continue;
            if (FD_ISSET (fds [i].fd, &exceptfds))
                fds [i].events->in_event ();
            if (fds [i].fd == retired_fd)
                continue;
            if (FD_ISSET (fds [i].fd, &writefds))
                fds [i].events->out_event ();
            if (fds [i].fd == retired_fd)
                continue;
            if (FD_ISSET (fds [i].fd, &readfds))
                fds [i].events->in_event ();
        }

        //  Destroy retired event sources.
        if (retired) {
202 203
            fds.erase (std::remove_if (fds.begin (), fds.end (),
                zmq::select_t::is_retired_fd), fds.end ());
Martin Sustrik's avatar
Martin Sustrik committed
204 205 206 207 208
            retired = false;
        }
    }
}

Martin Sustrik's avatar
Martin Sustrik committed
209
void zmq::select_t::worker_routine (void *arg_)
Martin Sustrik's avatar
Martin Sustrik committed
210 211 212
{
    ((select_t*) arg_)->loop ();
}
213 214 215 216 217 218

bool zmq::select_t::is_retired_fd (const fd_entry_t &entry)
{
    return (entry.fd == retired_fd);
}

219
#endif