mirror of
https://github.com/zeromq/libzmq.git
synced 2025-03-09 07:16:04 +00:00
Make VMCI to work on Windows.
select was improved to support multiple service providers on Windows. it should be slightly faster because of optimized iteration over selected sockets.
This commit is contained in:
parent
d7e59d6188
commit
538e5d4742
404
src/select.cpp
404
src/select.cpp
@ -31,6 +31,7 @@
|
|||||||
#if defined ZMQ_USE_SELECT
|
#if defined ZMQ_USE_SELECT
|
||||||
|
|
||||||
#include "platform.hpp"
|
#include "platform.hpp"
|
||||||
|
|
||||||
#if defined ZMQ_HAVE_WINDOWS
|
#if defined ZMQ_HAVE_WINDOWS
|
||||||
#include "windows.hpp"
|
#include "windows.hpp"
|
||||||
#elif defined ZMQ_HAVE_HPUX
|
#elif defined ZMQ_HAVE_HPUX
|
||||||
@ -44,23 +45,21 @@
|
|||||||
#include <sys/select.h>
|
#include <sys/select.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <string.h>
|
|
||||||
#include <algorithm>
|
|
||||||
|
|
||||||
#include "err.hpp"
|
#include "err.hpp"
|
||||||
#include "config.hpp"
|
#include "config.hpp"
|
||||||
#include "i_poll_events.hpp"
|
#include "i_poll_events.hpp"
|
||||||
|
|
||||||
zmq::select_t::select_t (const zmq::ctx_t &ctx_) :
|
zmq::select_t::select_t (const zmq::ctx_t &ctx_) :
|
||||||
ctx(ctx_),
|
ctx (ctx_),
|
||||||
maxfd (retired_fd),
|
#if defined ZMQ_HAVE_WINDOWS
|
||||||
|
// Fine as long as map is not cleared.
|
||||||
|
current_family_entry_it (family_entries.end ()),
|
||||||
|
#else
|
||||||
retired (false),
|
retired (false),
|
||||||
|
maxfd (retired_fd),
|
||||||
|
#endif
|
||||||
stopping (false)
|
stopping (false)
|
||||||
{
|
{
|
||||||
// Clear file descriptor sets.
|
|
||||||
FD_ZERO (&source_set_in);
|
|
||||||
FD_ZERO (&source_set_out);
|
|
||||||
FD_ZERO (&source_set_err);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
zmq::select_t::~select_t ()
|
zmq::select_t::~select_t ()
|
||||||
@ -70,22 +69,24 @@ zmq::select_t::~select_t ()
|
|||||||
|
|
||||||
zmq::select_t::handle_t zmq::select_t::add_fd (fd_t fd_, i_poll_events *events_)
|
zmq::select_t::handle_t zmq::select_t::add_fd (fd_t fd_, i_poll_events *events_)
|
||||||
{
|
{
|
||||||
// Store the file descriptor.
|
fd_entry_t fd_entry;
|
||||||
fd_entry_t entry = {fd_, events_};
|
fd_entry.fd = fd_;
|
||||||
fds.push_back (entry);
|
fd_entry.events = events_;
|
||||||
|
|
||||||
// Ensure we do not attempt to select () on more than FD_SETSIZE
|
#if defined ZMQ_HAVE_WINDOWS
|
||||||
// file descriptors.
|
u_short family = get_fd_family (fd_);
|
||||||
zmq_assert (fds.size () <= FD_SETSIZE);
|
wsa_assert (family != AF_UNSPEC);
|
||||||
|
family_entry_t& family_entry = family_entries [family];
|
||||||
|
family_entry.fd_entries.push_back (fd_entry);
|
||||||
|
FD_SET (fd_, &family_entry.fds_set.error);
|
||||||
|
#else
|
||||||
|
fd_entries.push_back (fd_entry);
|
||||||
|
FD_SET (fd_, &fds_set.error);
|
||||||
|
|
||||||
// Start polling on errors.
|
|
||||||
FD_SET (fd_, &source_set_err);
|
|
||||||
|
|
||||||
// Adjust maxfd if necessary.
|
|
||||||
if (fd_ > maxfd)
|
if (fd_ > maxfd)
|
||||||
maxfd = fd_;
|
maxfd = fd_;
|
||||||
|
#endif
|
||||||
|
|
||||||
// Increase the load metric of the thread.
|
|
||||||
adjust_load (1);
|
adjust_load (1);
|
||||||
|
|
||||||
return fd_;
|
return fd_;
|
||||||
@ -93,56 +94,106 @@ zmq::select_t::handle_t zmq::select_t::add_fd (fd_t fd_, i_poll_events *events_)
|
|||||||
|
|
||||||
void zmq::select_t::rm_fd (handle_t handle_)
|
void zmq::select_t::rm_fd (handle_t handle_)
|
||||||
{
|
{
|
||||||
// Mark the descriptor as retired.
|
#if defined ZMQ_HAVE_WINDOWS
|
||||||
fd_set_t::iterator it;
|
u_short family = get_fd_family (handle_);
|
||||||
for (it = fds.begin (); it != fds.end (); ++it)
|
wsa_assert (family != AF_UNSPEC);
|
||||||
if (it->fd == handle_)
|
|
||||||
|
family_entries_t::iterator family_entry_it = family_entries.find (family);
|
||||||
|
family_entry_t& family_entry = family_entry_it->second;
|
||||||
|
|
||||||
|
if (family_entry_it != current_family_entry_it) {
|
||||||
|
// Family is not currently being iterated and can be safely
|
||||||
|
// modified in palce. So later it can be skipped withour re-verifying
|
||||||
|
// its content.
|
||||||
|
fd_entries_t::iterator fd_entry_it;
|
||||||
|
for (fd_entry_it = family_entry.fd_entries.begin ();
|
||||||
|
fd_entry_it != family_entry.fd_entries.end (); ++fd_entry_it)
|
||||||
|
if (fd_entry_it->fd == handle_)
|
||||||
|
break;
|
||||||
|
zmq_assert (fd_entry_it != family_entry.fd_entries.end ());
|
||||||
|
|
||||||
|
family_entry.fd_entries.erase (fd_entry_it);
|
||||||
|
family_entry.fds_set.remove_fd (handle_);
|
||||||
|
} else {
|
||||||
|
// Otherwise mark removed entries as retired. It will be cleaned up
|
||||||
|
// at the end of the iteration. See zmq::select_t::loop
|
||||||
|
fd_entries_t::iterator fd_entry_it;
|
||||||
|
for (fd_entry_it = family_entry.fd_entries.begin ();
|
||||||
|
fd_entry_it != family_entry.fd_entries.end (); ++fd_entry_it)
|
||||||
|
if (fd_entry_it->fd == handle_)
|
||||||
|
break;
|
||||||
|
zmq_assert (fd_entry_it != family_entry.fd_entries.end ());
|
||||||
|
|
||||||
|
fd_entry_it->fd = retired_fd;
|
||||||
|
family_entry.fds_set.remove_fd (handle_);
|
||||||
|
family_entry.retired = true;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
fd_entries_t::iterator fd_entry_it;
|
||||||
|
for (fd_entry_it = fd_entries.begin ();
|
||||||
|
fd_entry_it != fd_entries.end (); ++fd_entry_it)
|
||||||
|
if (fd_entry_it->fd == handle_)
|
||||||
break;
|
break;
|
||||||
zmq_assert (it != fds.end ());
|
zmq_assert (fd_entry_it != fd_entries.end ());
|
||||||
it->fd = retired_fd;
|
|
||||||
retired = true;
|
|
||||||
|
|
||||||
// Stop polling on the descriptor.
|
fd_entry_it->fd = retired_fd;
|
||||||
FD_CLR (handle_, &source_set_in);
|
fds_set.remove_fd (handle_);
|
||||||
FD_CLR (handle_, &source_set_out);
|
|
||||||
FD_CLR (handle_, &source_set_err);
|
|
||||||
|
|
||||||
// Discard all events generated on this file descriptor.
|
|
||||||
FD_CLR (handle_, &readfds);
|
|
||||||
FD_CLR (handle_, &writefds);
|
|
||||||
FD_CLR (handle_, &exceptfds);
|
|
||||||
|
|
||||||
// Adjust the maxfd attribute if we have removed the
|
|
||||||
// highest-numbered file descriptor.
|
|
||||||
if (handle_ == maxfd) {
|
if (handle_ == maxfd) {
|
||||||
maxfd = retired_fd;
|
maxfd = retired_fd;
|
||||||
for (fd_set_t::iterator it = fds.begin (); it != fds.end (); ++it)
|
for (fd_entry_it = fd_entries.begin (); fd_entry_it != fd_entries.end ();
|
||||||
if (it->fd > maxfd)
|
++fd_entry_it)
|
||||||
maxfd = it->fd;
|
if (fd_entry_it->fd > maxfd)
|
||||||
|
maxfd = fd_entry_it->fd;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Decrease the load metric of the thread.
|
retired = true;
|
||||||
|
#endif
|
||||||
adjust_load (-1);
|
adjust_load (-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
void zmq::select_t::set_pollin (handle_t handle_)
|
void zmq::select_t::set_pollin (handle_t handle_)
|
||||||
{
|
{
|
||||||
FD_SET (handle_, &source_set_in);
|
#if defined ZMQ_HAVE_WINDOWS
|
||||||
|
u_short family = get_fd_family (handle_);
|
||||||
|
wsa_assert (family != AF_UNSPEC);
|
||||||
|
FD_SET (handle_, &family_entries [family].fds_set.read);
|
||||||
|
#else
|
||||||
|
FD_SET (handle_, &fds_set.read);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void zmq::select_t::reset_pollin (handle_t handle_)
|
void zmq::select_t::reset_pollin (handle_t handle_)
|
||||||
{
|
{
|
||||||
FD_CLR (handle_, &source_set_in);
|
#if defined ZMQ_HAVE_WINDOWS
|
||||||
|
u_short family = get_fd_family (handle_);
|
||||||
|
wsa_assert (family != AF_UNSPEC);
|
||||||
|
FD_CLR (handle_, &family_entries [family].fds_set.read);
|
||||||
|
#else
|
||||||
|
FD_CLR (handle_, &fds_set.read);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void zmq::select_t::set_pollout (handle_t handle_)
|
void zmq::select_t::set_pollout (handle_t handle_)
|
||||||
{
|
{
|
||||||
FD_SET (handle_, &source_set_out);
|
#if defined ZMQ_HAVE_WINDOWS
|
||||||
|
u_short family = get_fd_family (handle_);
|
||||||
|
wsa_assert (family != AF_UNSPEC);
|
||||||
|
FD_SET (handle_, &family_entries [family].fds_set.write);
|
||||||
|
#else
|
||||||
|
FD_SET (handle_, &fds_set.write);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void zmq::select_t::reset_pollout (handle_t handle_)
|
void zmq::select_t::reset_pollout (handle_t handle_)
|
||||||
{
|
{
|
||||||
FD_CLR (handle_, &source_set_out);
|
#if defined ZMQ_HAVE_WINDOWS
|
||||||
|
u_short family = get_fd_family (handle_);
|
||||||
|
wsa_assert (family != AF_UNSPEC);
|
||||||
|
FD_CLR (handle_, &family_entries [family].fds_set.write);
|
||||||
|
#else
|
||||||
|
FD_CLR (handle_, &fds_set.write);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void zmq::select_t::start ()
|
void zmq::select_t::start ()
|
||||||
@ -163,61 +214,181 @@ int zmq::select_t::max_fds ()
|
|||||||
void zmq::select_t::loop ()
|
void zmq::select_t::loop ()
|
||||||
{
|
{
|
||||||
while (!stopping) {
|
while (!stopping) {
|
||||||
|
|
||||||
// Execute any due timers.
|
// Execute any due timers.
|
||||||
int timeout = (int) execute_timers ();
|
int timeout = (int) execute_timers ();
|
||||||
|
|
||||||
// Intialise the pollsets.
|
#if defined ZMQ_HAVE_OSX
|
||||||
memcpy (&readfds, &source_set_in, sizeof source_set_in);
|
struct timeval tv = { (long) (timeout / 1000), timeout % 1000 * 1000 };
|
||||||
memcpy (&writefds, &source_set_out, sizeof source_set_out);
|
|
||||||
memcpy (&exceptfds, &source_set_err, sizeof source_set_err);
|
|
||||||
|
|
||||||
// Wait for events.
|
|
||||||
#ifdef ZMQ_HAVE_OSX
|
|
||||||
struct timeval tv = {(long) (timeout / 1000), timeout % 1000 * 1000};
|
|
||||||
#else
|
#else
|
||||||
struct timeval tv = {(long) (timeout / 1000),
|
struct timeval tv = { (long) (timeout / 1000), (long) (timeout % 1000 * 1000) };
|
||||||
(long) (timeout % 1000 * 1000)};
|
|
||||||
#endif
|
#endif
|
||||||
#ifdef ZMQ_HAVE_WINDOWS
|
|
||||||
int rc = select (0, &readfds, &writefds, &exceptfds,
|
int rc = 0;
|
||||||
timeout ? &tv : NULL);
|
|
||||||
wsa_assert (rc != SOCKET_ERROR);
|
#if defined ZMQ_HAVE_WINDOWS
|
||||||
|
/*
|
||||||
|
On Windows select does not allow to mix descriptors from different
|
||||||
|
service providers. It seems to work for AF_INET and AF_INET6,
|
||||||
|
but fails for AF_INET and VMCI. The workaround is to use
|
||||||
|
WSAEventSelect and WSAWaitForMultipleEvents to wait, then use
|
||||||
|
select to find out what actually changed. WSAWaitForMultipleEvents
|
||||||
|
cannot be used alone, because it does not support more than 64 events
|
||||||
|
which is not enough.
|
||||||
|
|
||||||
|
To reduce unncessary overhead, WSA is only used when there are more
|
||||||
|
than one family. Moreover, AF_INET and AF_INET6 are considered the same
|
||||||
|
family because Windows seems to handle them properly.
|
||||||
|
See get_fd_family for details.
|
||||||
|
*/
|
||||||
|
wsa_events_t wsa_events;
|
||||||
|
|
||||||
|
// If there is just one family, there is no reason to use WSA events.
|
||||||
|
if (family_entries.size () > 1) {
|
||||||
|
for (family_entries_t::iterator family_entry_it = family_entries.begin ();
|
||||||
|
family_entry_it != family_entries.end (); ++family_entry_it) {
|
||||||
|
family_entry_t& family_entry = family_entry_it->second;
|
||||||
|
|
||||||
|
for (fd_entries_t::iterator fd_entry_it = family_entry.fd_entries.begin ();
|
||||||
|
fd_entry_it != family_entry.fd_entries.end (); ++fd_entry_it) {
|
||||||
|
fd_t fd = fd_entry_it->fd;
|
||||||
|
|
||||||
|
// http://stackoverflow.com/q/35043420/188530
|
||||||
|
if (FD_ISSET (fd, &family_entry.fds_set.read) &&
|
||||||
|
FD_ISSET (fd, &family_entry.fds_set.write))
|
||||||
|
rc = WSAEventSelect (fd, wsa_events.events [3],
|
||||||
|
FD_READ | FD_ACCEPT | FD_CLOSE | FD_WRITE | FD_CONNECT | FD_OOB);
|
||||||
|
else if (FD_ISSET (fd, &family_entry.fds_set.read))
|
||||||
|
rc = WSAEventSelect (fd, wsa_events.events [0],
|
||||||
|
FD_READ | FD_ACCEPT | FD_CLOSE | FD_OOB);
|
||||||
|
else if (FD_ISSET (fd, &family_entry.fds_set.write))
|
||||||
|
rc = WSAEventSelect (fd, wsa_events.events [1],
|
||||||
|
FD_WRITE | FD_CONNECT | FD_OOB);
|
||||||
|
else if (FD_ISSET (fd, &family_entry.fds_set.error))
|
||||||
|
rc = WSAEventSelect (fd, wsa_events.events [2],
|
||||||
|
FD_OOB);
|
||||||
|
else
|
||||||
|
rc = 0;
|
||||||
|
|
||||||
|
wsa_assert (rc != SOCKET_ERROR);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined ZMQ_HAVE_WINDOWS
|
||||||
|
if (family_entries.size () > 1) {
|
||||||
|
rc = WSAWaitForMultipleEvents (4, wsa_events.events, FALSE,
|
||||||
|
timeout ? timeout : INFINITE, FALSE);
|
||||||
|
wsa_assert (rc != WSA_WAIT_FAILED);
|
||||||
|
zmq_assert (rc != WSA_WAIT_IO_COMPLETION);
|
||||||
|
|
||||||
|
if (rc == WSA_WAIT_TIMEOUT)
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (current_family_entry_it = family_entries.begin ();
|
||||||
|
current_family_entry_it != family_entries.end (); ++current_family_entry_it) {
|
||||||
|
family_entry_t& family_entry = current_family_entry_it->second;
|
||||||
|
|
||||||
|
// select will fail when run with empty sets.
|
||||||
|
if (family_entry.fd_entries.empty ())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
fds_set_t local_fds_set = family_entry.fds_set;
|
||||||
|
|
||||||
|
if (family_entries.size () > 1) {
|
||||||
|
// There is no reason to wait again after WSAWaitForMultipleEvents.
|
||||||
|
// Simply collect what is ready.
|
||||||
|
struct timeval tv_nodelay = { 0, 0 };
|
||||||
|
rc = select (0, &local_fds_set.read, &local_fds_set.write, &local_fds_set.error,
|
||||||
|
&tv_nodelay);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
rc = select (0, &local_fds_set.read, &local_fds_set.write,
|
||||||
|
&local_fds_set.error, timeout > 0 ? &tv : NULL);
|
||||||
|
|
||||||
|
wsa_assert (rc != SOCKET_ERROR);
|
||||||
|
|
||||||
|
// Size is cached to avoid iteration through recently added descriptors.
|
||||||
|
for (fd_entries_t::size_type i = 0, size = family_entry.fd_entries.size (); i < size && rc > 0; ++i) {
|
||||||
|
fd_entry_t& fd_entry = family_entry.fd_entries [i];
|
||||||
|
|
||||||
|
if (fd_entry.fd == retired_fd)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (FD_ISSET (fd_entry.fd, &local_fds_set.read)) {
|
||||||
|
fd_entry.events->in_event ();
|
||||||
|
--rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fd_entry.fd == retired_fd || rc == 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (FD_ISSET (fd_entry.fd, &local_fds_set.write)) {
|
||||||
|
fd_entry.events->out_event ();
|
||||||
|
--rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fd_entry.fd == retired_fd || rc == 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (FD_ISSET (fd_entry.fd, &local_fds_set.error)) {
|
||||||
|
fd_entry.events->in_event ();
|
||||||
|
--rc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (family_entry.retired) {
|
||||||
|
family_entry.retired = false;
|
||||||
|
family_entry.fd_entries.erase (std::remove_if (family_entry.fd_entries.begin (),
|
||||||
|
family_entry.fd_entries.end (), is_retired_fd), family_entry.fd_entries.end ());
|
||||||
|
}
|
||||||
|
}
|
||||||
#else
|
#else
|
||||||
int rc = select (maxfd + 1, &readfds, &writefds, &exceptfds,
|
fds_set_t local_fds_set = fds_set;
|
||||||
timeout ? &tv : NULL);
|
rc = select (maxfd + 1, &local_fds_set.read, &local_fds_set.write,
|
||||||
|
&local_fds_set.error, timeout ? &tv : NULL);
|
||||||
|
|
||||||
if (rc == -1) {
|
if (rc == -1) {
|
||||||
errno_assert (errno == EINTR);
|
errno_assert (errno == EINTR);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
// If there are no events (i.e. it's a timeout) there's no point
|
// Size is cached to avoid iteration through just added descriptors.
|
||||||
// in checking the pollset.
|
for (fd_entries_t::size_type i = 0, size = fd_entries.size (); i < size && rc > 0; ++i) {
|
||||||
if (rc == 0)
|
fd_entry_t& fd_entry = fd_entries [i];
|
||||||
continue;
|
|
||||||
|
|
||||||
for (fd_set_t::size_type i = 0; i < fds.size (); i ++) {
|
if (fd_entry.fd == retired_fd)
|
||||||
if (fds [i].fd == retired_fd)
|
|
||||||
continue;
|
continue;
|
||||||
if (FD_ISSET (fds [i].fd, &exceptfds))
|
|
||||||
fds [i].events->in_event ();
|
if (FD_ISSET (fd_entry.fd, &local_fds_set.read)) {
|
||||||
if (fds [i].fd == retired_fd)
|
fd_entry.events->in_event ();
|
||||||
|
--rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fd_entry.fd == retired_fd || rc == 0)
|
||||||
continue;
|
continue;
|
||||||
if (FD_ISSET (fds [i].fd, &writefds))
|
|
||||||
fds [i].events->out_event ();
|
if (FD_ISSET (fd_entry.fd, &local_fds_set.write)) {
|
||||||
if (fds [i].fd == retired_fd)
|
fd_entry.events->out_event ();
|
||||||
|
--rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fd_entry.fd == retired_fd || rc == 0)
|
||||||
continue;
|
continue;
|
||||||
if (FD_ISSET (fds [i].fd, &readfds))
|
|
||||||
fds [i].events->in_event ();
|
if (FD_ISSET (fd_entry.fd, &local_fds_set.error)) {
|
||||||
|
fd_entry.events->in_event ();
|
||||||
|
--rc;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Destroy retired event sources.
|
|
||||||
if (retired) {
|
if (retired) {
|
||||||
fds.erase (std::remove_if (fds.begin (), fds.end (),
|
|
||||||
zmq::select_t::is_retired_fd), fds.end ());
|
|
||||||
retired = false;
|
retired = false;
|
||||||
|
fd_entries.erase (std::remove_if (fd_entries.begin (), fd_entries.end (),
|
||||||
|
is_retired_fd), fd_entries.end ());
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -226,9 +397,80 @@ void zmq::select_t::worker_routine (void *arg_)
|
|||||||
((select_t*) arg_)->loop ();
|
((select_t*) arg_)->loop ();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
zmq::select_t::fds_set_t::fds_set_t ()
|
||||||
|
{
|
||||||
|
FD_ZERO (&read);
|
||||||
|
FD_ZERO (&write);
|
||||||
|
FD_ZERO (&error);
|
||||||
|
}
|
||||||
|
|
||||||
|
zmq::select_t::fds_set_t::fds_set_t (const fds_set_t& other_)
|
||||||
|
{
|
||||||
|
memcpy (&read, &other_.read, sizeof other_.read);
|
||||||
|
memcpy (&write, &other_.write, sizeof other_.write);
|
||||||
|
memcpy (&error, &other_.error, sizeof other_.error);
|
||||||
|
}
|
||||||
|
|
||||||
|
zmq::select_t::fds_set_t& zmq::select_t::fds_set_t::operator= (const fds_set_t& other_)
|
||||||
|
{
|
||||||
|
memcpy (&read, &other_.read, sizeof other_.read);
|
||||||
|
memcpy (&write, &other_.write, sizeof other_.write);
|
||||||
|
memcpy (&error, &other_.error, sizeof other_.error);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
void zmq::select_t::fds_set_t::remove_fd (const fd_t& fd_)
|
||||||
|
{
|
||||||
|
FD_CLR (fd_, &read);
|
||||||
|
FD_CLR (fd_, &write);
|
||||||
|
FD_CLR (fd_, &error);
|
||||||
|
}
|
||||||
|
|
||||||
bool zmq::select_t::is_retired_fd (const fd_entry_t &entry)
|
bool zmq::select_t::is_retired_fd (const fd_entry_t &entry)
|
||||||
{
|
{
|
||||||
return (entry.fd == retired_fd);
|
return (entry.fd == retired_fd);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if defined ZMQ_HAVE_WINDOWS
|
||||||
|
u_short zmq::select_t::get_fd_family (fd_t fd_)
|
||||||
|
{
|
||||||
|
sockaddr addr{ 0 };
|
||||||
|
int addr_size = sizeof addr;
|
||||||
|
int rc = getsockname (fd_, &addr, &addr_size);
|
||||||
|
|
||||||
|
// AF_INET and AF_INET6 can be mixed in select
|
||||||
|
// TODO: If proven otherwise, should simply return addr.sa_family
|
||||||
|
if (rc != SOCKET_ERROR)
|
||||||
|
return addr.sa_family == AF_INET6 ? AF_INET : addr.sa_family;
|
||||||
|
else
|
||||||
|
return AF_UNSPEC;
|
||||||
|
}
|
||||||
|
|
||||||
|
zmq::select_t::family_entry_t::family_entry_t () :
|
||||||
|
retired (false)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
zmq::select_t::wsa_events_t::wsa_events_t ()
|
||||||
|
{
|
||||||
|
events [0] = WSACreateEvent ();
|
||||||
|
wsa_assert (events [0] != WSA_INVALID_EVENT);
|
||||||
|
events [1] = WSACreateEvent ();
|
||||||
|
wsa_assert (events [1] != WSA_INVALID_EVENT);
|
||||||
|
events [2] = WSACreateEvent ();
|
||||||
|
wsa_assert (events [2] != WSA_INVALID_EVENT);
|
||||||
|
events [3] = WSACreateEvent ();
|
||||||
|
wsa_assert (events [3] != WSA_INVALID_EVENT);
|
||||||
|
}
|
||||||
|
|
||||||
|
zmq::select_t::wsa_events_t::~wsa_events_t ()
|
||||||
|
{
|
||||||
|
wsa_assert (WSACloseEvent (events [0]));
|
||||||
|
wsa_assert (WSACloseEvent (events [1]));
|
||||||
|
wsa_assert (WSACloseEvent (events [2]));
|
||||||
|
wsa_assert (WSACloseEvent (events [3]));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -38,9 +38,10 @@
|
|||||||
|
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include <map>
|
||||||
|
|
||||||
#ifdef ZMQ_HAVE_WINDOWS
|
#if defined ZMQ_HAVE_WINDOWS
|
||||||
#include <winsock2.h>
|
#include "windows.hpp"
|
||||||
#elif defined ZMQ_HAVE_OPENVMS
|
#elif defined ZMQ_HAVE_OPENVMS
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <sys/time.h>
|
#include <sys/time.h>
|
||||||
@ -90,37 +91,69 @@ namespace zmq
|
|||||||
// Main event loop.
|
// Main event loop.
|
||||||
void loop ();
|
void loop ();
|
||||||
|
|
||||||
// Reference to ZMQ context.
|
// Reference to ZMQ context.
|
||||||
const ctx_t &ctx;
|
const ctx_t &ctx;
|
||||||
|
|
||||||
|
// Internal state.
|
||||||
|
struct fds_set_t
|
||||||
|
{
|
||||||
|
fds_set_t ();
|
||||||
|
fds_set_t (const fds_set_t& other_);
|
||||||
|
fds_set_t& operator=(const fds_set_t& other_);
|
||||||
|
// Convinient method to descriptor from all sets.
|
||||||
|
void remove_fd (const fd_t& fd_);
|
||||||
|
|
||||||
|
fd_set read;
|
||||||
|
fd_set write;
|
||||||
|
fd_set error;
|
||||||
|
};
|
||||||
|
|
||||||
struct fd_entry_t
|
struct fd_entry_t
|
||||||
{
|
{
|
||||||
fd_t fd;
|
fd_t fd;
|
||||||
zmq::i_poll_events *events;
|
zmq::i_poll_events* events;
|
||||||
};
|
};
|
||||||
|
typedef std::vector<fd_entry_t> fd_entries_t;
|
||||||
|
|
||||||
|
#if defined ZMQ_HAVE_WINDOWS
|
||||||
|
struct family_entry_t
|
||||||
|
{
|
||||||
|
family_entry_t ();
|
||||||
|
|
||||||
|
fd_entries_t fd_entries;
|
||||||
|
fds_set_t fds_set;
|
||||||
|
bool retired;
|
||||||
|
};
|
||||||
|
typedef std::map<u_short, family_entry_t> family_entries_t;
|
||||||
|
|
||||||
|
struct wsa_events_t
|
||||||
|
{
|
||||||
|
wsa_events_t ();
|
||||||
|
~wsa_events_t ();
|
||||||
|
|
||||||
|
// read, write, error and readwrite
|
||||||
|
WSAEVENT events [4];
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined ZMQ_HAVE_WINDOWS
|
||||||
|
family_entries_t family_entries;
|
||||||
|
// See loop for details.
|
||||||
|
family_entries_t::iterator current_family_entry_it;
|
||||||
|
#else
|
||||||
|
fd_entries_t fd_entries;
|
||||||
|
fds_set_t fds_set;
|
||||||
|
fd_t maxfd;
|
||||||
|
bool retired;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined ZMQ_HAVE_WINDOWS
|
||||||
|
// Socket's family or AF_UNSPEC on error.
|
||||||
|
static u_short get_fd_family (fd_t fd_);
|
||||||
|
#endif
|
||||||
// Checks if an fd_entry_t is retired.
|
// Checks if an fd_entry_t is retired.
|
||||||
static bool is_retired_fd (const fd_entry_t &entry);
|
static bool is_retired_fd (const fd_entry_t &entry);
|
||||||
|
|
||||||
// Set of file descriptors that are used to retrieve
|
|
||||||
// information for fd_set.
|
|
||||||
typedef std::vector <fd_entry_t> fd_set_t;
|
|
||||||
fd_set_t fds;
|
|
||||||
|
|
||||||
fd_set source_set_in;
|
|
||||||
fd_set source_set_out;
|
|
||||||
fd_set source_set_err;
|
|
||||||
|
|
||||||
fd_set readfds;
|
|
||||||
fd_set writefds;
|
|
||||||
fd_set exceptfds;
|
|
||||||
|
|
||||||
// Maximum file descriptor.
|
|
||||||
fd_t maxfd;
|
|
||||||
|
|
||||||
// If true, at least one file descriptor has retired.
|
|
||||||
bool retired;
|
|
||||||
|
|
||||||
// If true, thread is shutting down.
|
// If true, thread is shutting down.
|
||||||
bool stopping;
|
bool stopping;
|
||||||
|
|
||||||
|
@ -40,7 +40,7 @@ void zmq::tune_vmci_buffer_size (ctx_t *context_, fd_t sockfd_, uint64_t default
|
|||||||
assert (family != -1);
|
assert (family != -1);
|
||||||
|
|
||||||
if (default_size_ != 0) {
|
if (default_size_ != 0) {
|
||||||
int rc = setsockopt (sockfd_, family, SO_VMCI_BUFFER_SIZE, &default_size_, sizeof default_size_);
|
int rc = setsockopt (sockfd_, family, SO_VMCI_BUFFER_SIZE, (char*) &default_size_, sizeof default_size_);
|
||||||
#if defined ZMQ_HAVE_WINDOWS
|
#if defined ZMQ_HAVE_WINDOWS
|
||||||
wsa_assert (rc != SOCKET_ERROR);
|
wsa_assert (rc != SOCKET_ERROR);
|
||||||
#else
|
#else
|
||||||
@ -49,7 +49,7 @@ void zmq::tune_vmci_buffer_size (ctx_t *context_, fd_t sockfd_, uint64_t default
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (min_size_ != 0) {
|
if (min_size_ != 0) {
|
||||||
int rc = setsockopt (sockfd_, family, SO_VMCI_BUFFER_SIZE, &min_size_, sizeof min_size_);
|
int rc = setsockopt (sockfd_, family, SO_VMCI_BUFFER_SIZE, (char*) &min_size_, sizeof min_size_);
|
||||||
#if defined ZMQ_HAVE_WINDOWS
|
#if defined ZMQ_HAVE_WINDOWS
|
||||||
wsa_assert (rc != SOCKET_ERROR);
|
wsa_assert (rc != SOCKET_ERROR);
|
||||||
#else
|
#else
|
||||||
@ -58,7 +58,7 @@ void zmq::tune_vmci_buffer_size (ctx_t *context_, fd_t sockfd_, uint64_t default
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (max_size_ != 0) {
|
if (max_size_ != 0) {
|
||||||
int rc = setsockopt (sockfd_, family, SO_VMCI_BUFFER_SIZE, &max_size_, sizeof max_size_);
|
int rc = setsockopt (sockfd_, family, SO_VMCI_BUFFER_SIZE, (char*) &max_size_, sizeof max_size_);
|
||||||
#if defined ZMQ_HAVE_WINDOWS
|
#if defined ZMQ_HAVE_WINDOWS
|
||||||
wsa_assert (rc != SOCKET_ERROR);
|
wsa_assert (rc != SOCKET_ERROR);
|
||||||
#else
|
#else
|
||||||
@ -76,7 +76,7 @@ void zmq::tune_vmci_connect_timeout (ctx_t *context_, fd_t sockfd_, struct timev
|
|||||||
int family = context_->get_vmci_socket_family ();
|
int family = context_->get_vmci_socket_family ();
|
||||||
assert (family != -1);
|
assert (family != -1);
|
||||||
|
|
||||||
int rc = setsockopt (sockfd_, family, SO_VMCI_CONNECT_TIMEOUT, &timeout_, sizeof timeout_);
|
int rc = setsockopt (sockfd_, family, SO_VMCI_CONNECT_TIMEOUT, (char*) &timeout_, sizeof timeout_);
|
||||||
#if defined ZMQ_HAVE_WINDOWS
|
#if defined ZMQ_HAVE_WINDOWS
|
||||||
wsa_assert (rc != SOCKET_ERROR);
|
wsa_assert (rc != SOCKET_ERROR);
|
||||||
#else
|
#else
|
||||||
|
@ -32,7 +32,6 @@
|
|||||||
#if defined ZMQ_HAVE_VMCI
|
#if defined ZMQ_HAVE_VMCI
|
||||||
|
|
||||||
#include <new>
|
#include <new>
|
||||||
#include <string>
|
|
||||||
|
|
||||||
#include "stream_engine.hpp"
|
#include "stream_engine.hpp"
|
||||||
#include "io_thread.hpp"
|
#include "io_thread.hpp"
|
||||||
@ -118,7 +117,8 @@ void zmq::vmci_connecter_t::out_event ()
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
tune_vmci_buffer_size (this->get_ctx (), fd, options.vmci_buffer_size, options.vmci_buffer_min_size, options.vmci_buffer_max_size);
|
tune_vmci_buffer_size (this->get_ctx (), fd, options.vmci_buffer_size,
|
||||||
|
options.vmci_buffer_min_size, options.vmci_buffer_max_size);
|
||||||
|
|
||||||
if (options.vmci_connect_timeout > 0)
|
if (options.vmci_connect_timeout > 0)
|
||||||
{
|
{
|
||||||
@ -218,8 +218,15 @@ int zmq::vmci_connecter_t::open ()
|
|||||||
|
|
||||||
// Create the socket.
|
// Create the socket.
|
||||||
s = open_socket (family, SOCK_STREAM, 0);
|
s = open_socket (family, SOCK_STREAM, 0);
|
||||||
|
#ifdef ZMQ_HAVE_WINDOWS
|
||||||
|
if (s == INVALID_SOCKET) {
|
||||||
|
errno = wsa_error_to_errno(WSAGetLastError());
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
#else
|
||||||
if (s == -1)
|
if (s == -1)
|
||||||
return -1;
|
return -1;
|
||||||
|
#endif
|
||||||
|
|
||||||
// Set the non-blocking flag.
|
// Set the non-blocking flag.
|
||||||
unblock_socket (s);
|
unblock_socket (s);
|
||||||
@ -233,12 +240,18 @@ int zmq::vmci_connecter_t::open ()
|
|||||||
if (rc == 0)
|
if (rc == 0)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
// Translate other error codes indicating asynchronous connect has been
|
// Translate error codes indicating asynchronous connect has been
|
||||||
// launched to a uniform EINPROGRESS.
|
// launched to a uniform EINPROGRESS.
|
||||||
if (rc == -1 && errno == EINTR) {
|
#ifdef ZMQ_HAVE_WINDOWS
|
||||||
|
const int error_code = WSAGetLastError();
|
||||||
|
if (error_code == WSAEINPROGRESS || error_code == WSAEWOULDBLOCK)
|
||||||
errno = EINPROGRESS;
|
errno = EINPROGRESS;
|
||||||
return -1;
|
else
|
||||||
}
|
errno = wsa_error_to_errno(error_code);
|
||||||
|
#else
|
||||||
|
if (errno == EINTR)
|
||||||
|
errno = EINPROGRESS;
|
||||||
|
#endif
|
||||||
|
|
||||||
// Forward the error.
|
// Forward the error.
|
||||||
return -1;
|
return -1;
|
||||||
@ -269,19 +282,45 @@ zmq::fd_t zmq::vmci_connecter_t::connect ()
|
|||||||
socklen_t len = sizeof (err);
|
socklen_t len = sizeof (err);
|
||||||
#endif
|
#endif
|
||||||
int rc = getsockopt (s, SOL_SOCKET, SO_ERROR, (char*) &err, &len);
|
int rc = getsockopt (s, SOL_SOCKET, SO_ERROR, (char*) &err, &len);
|
||||||
|
|
||||||
|
// Assert if the error was caused by 0MQ bug.
|
||||||
|
// Networking problems are OK. No need to assert.
|
||||||
|
#ifdef ZMQ_HAVE_WINDOWS
|
||||||
|
zmq_assert(rc == 0);
|
||||||
|
if (err != 0) {
|
||||||
|
if (err != WSAECONNREFUSED
|
||||||
|
&& err != WSAETIMEDOUT
|
||||||
|
&& err != WSAECONNABORTED
|
||||||
|
&& err != WSAEHOSTUNREACH
|
||||||
|
&& err != WSAENETUNREACH
|
||||||
|
&& err != WSAENETDOWN
|
||||||
|
&& err != WSAEACCES
|
||||||
|
&& err != WSAEINVAL
|
||||||
|
&& err != WSAEADDRINUSE
|
||||||
|
&& err != WSAECONNRESET)
|
||||||
|
{
|
||||||
|
wsa_assert_no(err);
|
||||||
|
}
|
||||||
|
return retired_fd;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
// Following code should handle both Berkeley-derived socket
|
||||||
|
// implementations and Solaris.
|
||||||
if (rc == -1)
|
if (rc == -1)
|
||||||
err = errno;
|
err = errno;
|
||||||
if (err != 0) {
|
if (err != 0) {
|
||||||
|
|
||||||
// Assert if the error was caused by 0MQ bug.
|
|
||||||
// Networking problems are OK. No need to assert.
|
|
||||||
errno = err;
|
errno = err;
|
||||||
errno_assert (errno == ECONNREFUSED || errno == ECONNRESET ||
|
errno_assert(
|
||||||
errno == ETIMEDOUT || errno == EHOSTUNREACH ||
|
errno == ECONNREFUSED ||
|
||||||
errno == ENETUNREACH || errno == ENETDOWN);
|
errno == ECONNRESET ||
|
||||||
|
errno == ETIMEDOUT ||
|
||||||
|
errno == EHOSTUNREACH ||
|
||||||
|
errno == ENETUNREACH ||
|
||||||
|
errno == ENETDOWN ||
|
||||||
|
errno == EINVAL);
|
||||||
return retired_fd;
|
return retired_fd;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
fd_t result = s;
|
fd_t result = s;
|
||||||
s = retired_fd;
|
s = retired_fd;
|
||||||
|
@ -27,8 +27,8 @@
|
|||||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef __VMCI_CONNECTER_HPP_INCLUDED__
|
#ifndef __ZMQ_VMCI_CONNECTER_HPP_INCLUDED__
|
||||||
#define __VMCI_CONNECTER_HPP_INCLUDED__
|
#define __ZMQ_VMCI_CONNECTER_HPP_INCLUDED__
|
||||||
|
|
||||||
#include "platform.hpp"
|
#include "platform.hpp"
|
||||||
|
|
||||||
|
@ -33,8 +33,6 @@
|
|||||||
|
|
||||||
#include <new>
|
#include <new>
|
||||||
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
#include "stream_engine.hpp"
|
#include "stream_engine.hpp"
|
||||||
#include "vmci_address.hpp"
|
#include "vmci_address.hpp"
|
||||||
#include "io_thread.hpp"
|
#include "io_thread.hpp"
|
||||||
@ -153,20 +151,46 @@ int zmq::vmci_listener_t::set_address (const char *addr_)
|
|||||||
|
|
||||||
// Create a listening socket.
|
// Create a listening socket.
|
||||||
s = open_socket (this->get_ctx ()->get_vmci_socket_family (), SOCK_STREAM, 0);
|
s = open_socket (this->get_ctx ()->get_vmci_socket_family (), SOCK_STREAM, 0);
|
||||||
|
#ifdef ZMQ_HAVE_WINDOWS
|
||||||
|
if (s == INVALID_SOCKET) {
|
||||||
|
errno = wsa_error_to_errno(WSAGetLastError());
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
#if !defined _WIN32_WCE
|
||||||
|
// On Windows, preventing sockets to be inherited by child processes.
|
||||||
|
BOOL brc = SetHandleInformation((HANDLE)s, HANDLE_FLAG_INHERIT, 0);
|
||||||
|
win_assert(brc);
|
||||||
|
#endif
|
||||||
|
#else
|
||||||
if (s == -1)
|
if (s == -1)
|
||||||
return -1;
|
return -1;
|
||||||
|
#endif
|
||||||
|
|
||||||
address.to_string (endpoint);
|
address.to_string (endpoint);
|
||||||
|
|
||||||
// Bind the socket.
|
// Bind the socket.
|
||||||
rc = bind (s, address.addr (), address.addrlen ());
|
rc = bind (s, address.addr (), address.addrlen ());
|
||||||
|
#ifdef ZMQ_HAVE_WINDOWS
|
||||||
|
if (rc == SOCKET_ERROR) {
|
||||||
|
errno = wsa_error_to_errno(WSAGetLastError());
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
#else
|
||||||
if (rc != 0)
|
if (rc != 0)
|
||||||
goto error;
|
goto error;
|
||||||
|
#endif
|
||||||
|
|
||||||
// Listen for incoming connections.
|
// Listen for incoming connections.
|
||||||
rc = listen (s, options.backlog);
|
rc = listen (s, options.backlog);
|
||||||
|
#ifdef ZMQ_HAVE_WINDOWS
|
||||||
|
if (rc == SOCKET_ERROR) {
|
||||||
|
errno = wsa_error_to_errno(WSAGetLastError());
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
#else
|
||||||
if (rc != 0)
|
if (rc != 0)
|
||||||
goto error;
|
goto error;
|
||||||
|
#endif
|
||||||
|
|
||||||
socket->event_listening (endpoint, s);
|
socket->event_listening (endpoint, s);
|
||||||
return 0;
|
return 0;
|
||||||
@ -199,12 +223,29 @@ zmq::fd_t zmq::vmci_listener_t::accept ()
|
|||||||
// resources is considered valid and treated by ignoring the connection.
|
// resources is considered valid and treated by ignoring the connection.
|
||||||
zmq_assert (s != retired_fd);
|
zmq_assert (s != retired_fd);
|
||||||
fd_t sock = ::accept (s, NULL, NULL);
|
fd_t sock = ::accept (s, NULL, NULL);
|
||||||
|
|
||||||
|
#ifdef ZMQ_HAVE_WINDOWS
|
||||||
|
if (sock == INVALID_SOCKET) {
|
||||||
|
wsa_assert(WSAGetLastError() == WSAEWOULDBLOCK ||
|
||||||
|
WSAGetLastError() == WSAECONNRESET ||
|
||||||
|
WSAGetLastError() == WSAEMFILE ||
|
||||||
|
WSAGetLastError() == WSAENOBUFS);
|
||||||
|
return retired_fd;
|
||||||
|
}
|
||||||
|
#if !defined _WIN32_WCE
|
||||||
|
// On Windows, preventing sockets to be inherited by child processes.
|
||||||
|
BOOL brc = SetHandleInformation((HANDLE)sock, HANDLE_FLAG_INHERIT, 0);
|
||||||
|
win_assert(brc);
|
||||||
|
#endif
|
||||||
|
#else
|
||||||
if (sock == -1) {
|
if (sock == -1) {
|
||||||
errno_assert (errno == EAGAIN || errno == EWOULDBLOCK ||
|
errno_assert(errno == EAGAIN || errno == EWOULDBLOCK ||
|
||||||
errno == EINTR || errno == ECONNABORTED || errno == EPROTO ||
|
errno == EINTR || errno == ECONNABORTED || errno == EPROTO ||
|
||||||
|
errno == ENOBUFS || errno == ENOMEM || errno == EMFILE ||
|
||||||
errno == ENFILE);
|
errno == ENFILE);
|
||||||
return retired_fd;
|
return retired_fd;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
// Race condition can cause socket not to be closed (if fork happens
|
// Race condition can cause socket not to be closed (if fork happens
|
||||||
// between accept and this point).
|
// between accept and this point).
|
||||||
|
Loading…
x
Reference in New Issue
Block a user