add PollSet class (backported from develop)

This commit is contained in:
Guenter Obiltschnig
2017-11-01 11:19:52 +01:00
parent 44c3e8f22b
commit 341aed39fe
8 changed files with 836 additions and 56 deletions

View File

@@ -31,7 +31,8 @@ objects = \
NTPClient NTPEventArgs NTPPacket \ NTPClient NTPEventArgs NTPPacket \
RemoteSyslogChannel RemoteSyslogListener SMTPChannel \ RemoteSyslogChannel RemoteSyslogListener SMTPChannel \
WebSocket WebSocketImpl \ WebSocket WebSocketImpl \
OAuth10Credentials OAuth20Credentials OAuth10Credentials OAuth20Credentials \
PollSet
target = PocoNet target = PocoNet
target_version = $(LIBVERSION) target_version = $(LIBVERSION)

View File

@@ -0,0 +1,86 @@
//
// PollSet.h
//
// Library: Net
// Package: Sockets
// Module: PollSet
//
// Definition of the PollSet class.
//
// Copyright (c) 2016, Applied Informatics Software Engineering GmbH.
// All rights reserved.
//
// SPDX-License-Identifier: BSL-1.0
//
#ifndef Net_PollSet_INCLUDED
#define Net_PollSet_INCLUDED
#include "Poco/Net/Socket.h"
#include <map>
namespace Poco {
namespace Net {
class PollSetImpl;
class Net_API PollSet
/// A set of sockets that can be efficiently polled as a whole.
///
/// If supported, PollSet is implemented using epoll (Linux) or
/// poll (BSD) APIs. A fallback implementation using select()
/// is also provided.
{
public:
enum Mode
{
POLL_READ = 0x01,
POLL_WRITE = 0x02,
POLL_ERROR = 0x04
};
typedef std::map<Poco::Net::Socket, int> SocketModeMap;
PollSet();
/// Creates an empty PollSet.
~PollSet();
/// Destroys the PollSet.
void add(const Poco::Net::Socket& socket, int mode);
/// Adds the given socket to the set, for polling with
/// the given mode, which can be an OR'd combination of
/// POLL_READ, POLL_WRITE and POLL_ERROR.
void remove(const Poco::Net::Socket& socket);
/// Removes the given socket from the set.
void update(const Poco::Net::Socket& socket, int mode);
/// Updates the mode of the given socket.
void clear();
/// Removes all sockets from the PollSet.
SocketModeMap poll(const Poco::Timespan& timeout);
/// Waits until the state of at least one of the PollSet's sockets
/// changes accordingly to its mode, or the timeout expires.
/// Returns a PollMap containing the sockets that have had
/// their state changed.
private:
PollSetImpl* _pImpl;
PollSet(const PollSet&);
PollSet& operator = (const PollSet&);
};
} } // namespace Poco::Net
#endif // Net_PollSet_INCLUDED

View File

@@ -31,7 +31,7 @@ namespace Net {
class Net_API SocketImpl: public Poco::RefCountedObject class Net_API SocketImpl: public Poco::RefCountedObject
/// This class encapsulates the Berkeley sockets API. /// This class encapsulates the Berkeley sockets API.
/// ///
/// Subclasses implement specific socket types like /// Subclasses implement specific socket types like
/// stream or datagram sockets. /// stream or datagram sockets.
/// ///
@@ -56,9 +56,9 @@ public:
/// with the client. /// with the client.
/// ///
/// The client socket's address is returned in clientAddr. /// The client socket's address is returned in clientAddr.
virtual void connect(const SocketAddress& address); virtual void connect(const SocketAddress& address);
/// Initializes the socket and establishes a connection to /// Initializes the socket and establishes a connection to
/// the TCP server at the given address. /// the TCP server at the given address.
/// ///
/// Can also be used for UDP sockets. In this case, no /// Can also be used for UDP sockets. In this case, no
@@ -66,14 +66,14 @@ public:
/// packets are restricted to the specified address. /// packets are restricted to the specified address.
virtual void connect(const SocketAddress& address, const Poco::Timespan& timeout); virtual void connect(const SocketAddress& address, const Poco::Timespan& timeout);
/// Initializes the socket, sets the socket timeout and /// Initializes the socket, sets the socket timeout and
/// establishes a connection to the TCP server at the given address. /// establishes a connection to the TCP server at the given address.
virtual void connectNB(const SocketAddress& address); virtual void connectNB(const SocketAddress& address);
/// Initializes the socket and establishes a connection to /// Initializes the socket and establishes a connection to
/// the TCP server at the given address. Prior to opening the /// the TCP server at the given address. Prior to opening the
/// connection the socket is set to nonblocking mode. /// connection the socket is set to nonblocking mode.
virtual void bind(const SocketAddress& address, bool reuseAddress = false); virtual void bind(const SocketAddress& address, bool reuseAddress = false);
/// Bind a local address to the socket. /// Bind a local address to the socket.
/// ///
@@ -149,14 +149,14 @@ public:
virtual void shutdownReceive(); virtual void shutdownReceive();
/// Shuts down the receiving part of the socket connection. /// Shuts down the receiving part of the socket connection.
virtual void shutdownSend(); virtual void shutdownSend();
/// Shuts down the sending part of the socket connection. /// Shuts down the sending part of the socket connection.
virtual void shutdown(); virtual void shutdown();
/// Shuts down both the receiving and the sending part /// Shuts down both the receiving and the sending part
/// of the socket connection. /// of the socket connection.
virtual int sendBytes(const void* buffer, int length, int flags = 0); virtual int sendBytes(const void* buffer, int length, int flags = 0);
/// Sends the contents of the given buffer through /// Sends the contents of the given buffer through
/// the socket. /// the socket.
@@ -166,7 +166,7 @@ public:
/// ///
/// Certain socket implementations may also return a negative /// Certain socket implementations may also return a negative
/// value denoting a certain condition. /// value denoting a certain condition.
virtual int receiveBytes(void* buffer, int length, int flags = 0); virtual int receiveBytes(void* buffer, int length, int flags = 0);
/// Receives data from the socket and stores it /// Receives data from the socket and stores it
/// in buffer. Up to length bytes are received. /// in buffer. Up to length bytes are received.
@@ -175,21 +175,21 @@ public:
/// ///
/// Certain socket implementations may also return a negative /// Certain socket implementations may also return a negative
/// value denoting a certain condition. /// value denoting a certain condition.
virtual int sendTo(const void* buffer, int length, const SocketAddress& address, int flags = 0); virtual int sendTo(const void* buffer, int length, const SocketAddress& address, int flags = 0);
/// Sends the contents of the given buffer through /// Sends the contents of the given buffer through
/// the socket to the given address. /// the socket to the given address.
/// ///
/// Returns the number of bytes sent, which may be /// Returns the number of bytes sent, which may be
/// less than the number of bytes specified. /// less than the number of bytes specified.
virtual int receiveFrom(void* buffer, int length, SocketAddress& address, int flags = 0); virtual int receiveFrom(void* buffer, int length, SocketAddress& address, int flags = 0);
/// Receives data from the socket and stores it /// Receives data from the socket and stores it
/// in buffer. Up to length bytes are received. /// in buffer. Up to length bytes are received.
/// Stores the address of the sender in address. /// Stores the address of the sender in address.
/// ///
/// Returns the number of bytes received. /// Returns the number of bytes received.
virtual void sendUrgent(unsigned char data); virtual void sendUrgent(unsigned char data);
/// Sends one byte of urgent data through /// Sends one byte of urgent data through
/// the socket. /// the socket.
@@ -198,24 +198,24 @@ public:
/// ///
/// The preferred way for a socket to receive urgent data /// The preferred way for a socket to receive urgent data
/// is by enabling the SO_OOBINLINE option. /// is by enabling the SO_OOBINLINE option.
virtual int available(); virtual int available();
/// Returns the number of bytes available that can be read /// Returns the number of bytes available that can be read
/// without causing the socket to block. /// without causing the socket to block.
virtual bool poll(const Poco::Timespan& timeout, int mode); virtual bool poll(const Poco::Timespan& timeout, int mode);
/// Determines the status of the socket, using a /// Determines the status of the socket, using a
/// call to select(). /// call to select().
/// ///
/// The mode argument is constructed by combining the values /// The mode argument is constructed by combining the values
/// of the SelectMode enumeration. /// of the SelectMode enumeration.
/// ///
/// Returns true if the next operation corresponding to /// Returns true if the next operation corresponding to
/// mode will not block, false otherwise. /// mode will not block, false otherwise.
virtual void setSendBufferSize(int size); virtual void setSendBufferSize(int size);
/// Sets the size of the send buffer. /// Sets the size of the send buffer.
virtual int getSendBufferSize(); virtual int getSendBufferSize();
/// Returns the size of the send buffer. /// Returns the size of the send buffer.
/// ///
@@ -225,7 +225,7 @@ public:
virtual void setReceiveBufferSize(int size); virtual void setReceiveBufferSize(int size);
/// Sets the size of the receive buffer. /// Sets the size of the receive buffer.
virtual int getReceiveBufferSize(); virtual int getReceiveBufferSize();
/// Returns the size of the receive buffer. /// Returns the size of the receive buffer.
/// ///
@@ -235,7 +235,7 @@ public:
virtual void setSendTimeout(const Poco::Timespan& timeout); virtual void setSendTimeout(const Poco::Timespan& timeout);
/// Sets the send timeout for the socket. /// Sets the send timeout for the socket.
virtual Poco::Timespan getSendTimeout(); virtual Poco::Timespan getSendTimeout();
/// Returns the send timeout for the socket. /// Returns the send timeout for the socket.
/// ///
@@ -248,20 +248,20 @@ public:
/// ///
/// On systems that do not support SO_RCVTIMEO, a /// On systems that do not support SO_RCVTIMEO, a
/// workaround using poll() is provided. /// workaround using poll() is provided.
virtual Poco::Timespan getReceiveTimeout(); virtual Poco::Timespan getReceiveTimeout();
/// Returns the receive timeout for the socket. /// Returns the receive timeout for the socket.
/// ///
/// The returned timeout may be different than the /// The returned timeout may be different than the
/// timeout previously set with setReceiveTimeout(), /// timeout previously set with setReceiveTimeout(),
/// as the system is free to adjust the value. /// as the system is free to adjust the value.
virtual SocketAddress address(); virtual SocketAddress address();
/// Returns the IP address and port number of the socket. /// Returns the IP address and port number of the socket.
virtual SocketAddress peerAddress(); virtual SocketAddress peerAddress();
/// Returns the IP address and port number of the peer socket. /// Returns the IP address and port number of the peer socket.
void setOption(int level, int option, int value); void setOption(int level, int option, int value);
/// Sets the socket option specified by level and option /// Sets the socket option specified by level and option
/// to the given integer value. /// to the given integer value.
@@ -273,64 +273,64 @@ public:
void setOption(int level, int option, unsigned char value); void setOption(int level, int option, unsigned char value);
/// Sets the socket option specified by level and option /// Sets the socket option specified by level and option
/// to the given integer value. /// to the given integer value.
void setOption(int level, int option, const Poco::Timespan& value); void setOption(int level, int option, const Poco::Timespan& value);
/// Sets the socket option specified by level and option /// Sets the socket option specified by level and option
/// to the given time value. /// to the given time value.
void setOption(int level, int option, const IPAddress& value); void setOption(int level, int option, const IPAddress& value);
/// Sets the socket option specified by level and option /// Sets the socket option specified by level and option
/// to the given time value. /// to the given time value.
virtual void setRawOption(int level, int option, const void* value, poco_socklen_t length); virtual void setRawOption(int level, int option, const void* value, poco_socklen_t length);
/// Sets the socket option specified by level and option /// Sets the socket option specified by level and option
/// to the given time value. /// to the given time value.
void getOption(int level, int option, int& value); void getOption(int level, int option, int& value);
/// Returns the value of the socket option /// Returns the value of the socket option
/// specified by level and option. /// specified by level and option.
void getOption(int level, int option, unsigned& value); void getOption(int level, int option, unsigned& value);
/// Returns the value of the socket option /// Returns the value of the socket option
/// specified by level and option. /// specified by level and option.
void getOption(int level, int option, unsigned char& value); void getOption(int level, int option, unsigned char& value);
/// Returns the value of the socket option /// Returns the value of the socket option
/// specified by level and option. /// specified by level and option.
void getOption(int level, int option, Poco::Timespan& value); void getOption(int level, int option, Poco::Timespan& value);
/// Returns the value of the socket option /// Returns the value of the socket option
/// specified by level and option. /// specified by level and option.
void getOption(int level, int option, IPAddress& value); void getOption(int level, int option, IPAddress& value);
/// Returns the value of the socket option /// Returns the value of the socket option
/// specified by level and option. /// specified by level and option.
virtual void getRawOption(int level, int option, void* value, poco_socklen_t& length); virtual void getRawOption(int level, int option, void* value, poco_socklen_t& length);
/// Returns the value of the socket option /// Returns the value of the socket option
/// specified by level and option. /// specified by level and option.
void setLinger(bool on, int seconds); void setLinger(bool on, int seconds);
/// Sets the value of the SO_LINGER socket option. /// Sets the value of the SO_LINGER socket option.
void getLinger(bool& on, int& seconds); void getLinger(bool& on, int& seconds);
/// Returns the value of the SO_LINGER socket option. /// Returns the value of the SO_LINGER socket option.
void setNoDelay(bool flag); void setNoDelay(bool flag);
/// Sets the value of the TCP_NODELAY socket option. /// Sets the value of the TCP_NODELAY socket option.
bool getNoDelay(); bool getNoDelay();
/// Returns the value of the TCP_NODELAY socket option. /// Returns the value of the TCP_NODELAY socket option.
void setKeepAlive(bool flag); void setKeepAlive(bool flag);
/// Sets the value of the SO_KEEPALIVE socket option. /// Sets the value of the SO_KEEPALIVE socket option.
bool getKeepAlive(); bool getKeepAlive();
/// Returns the value of the SO_KEEPALIVE socket option. /// Returns the value of the SO_KEEPALIVE socket option.
void setReuseAddress(bool flag); void setReuseAddress(bool flag);
/// Sets the value of the SO_REUSEADDR socket option. /// Sets the value of the SO_REUSEADDR socket option.
bool getReuseAddress(); bool getReuseAddress();
/// Returns the value of the SO_REUSEADDR socket option. /// Returns the value of the SO_REUSEADDR socket option.
@@ -338,22 +338,22 @@ public:
/// Sets the value of the SO_REUSEPORT socket option. /// Sets the value of the SO_REUSEPORT socket option.
/// Does nothing if the socket implementation does not /// Does nothing if the socket implementation does not
/// support SO_REUSEPORT. /// support SO_REUSEPORT.
bool getReusePort(); bool getReusePort();
/// Returns the value of the SO_REUSEPORT socket option. /// Returns the value of the SO_REUSEPORT socket option.
/// ///
/// Returns false if the socket implementation does not /// Returns false if the socket implementation does not
/// support SO_REUSEPORT. /// support SO_REUSEPORT.
void setOOBInline(bool flag); void setOOBInline(bool flag);
/// Sets the value of the SO_OOBINLINE socket option. /// Sets the value of the SO_OOBINLINE socket option.
bool getOOBInline(); bool getOOBInline();
/// Returns the value of the SO_OOBINLINE socket option. /// Returns the value of the SO_OOBINLINE socket option.
void setBroadcast(bool flag); void setBroadcast(bool flag);
/// Sets the value of the SO_BROADCAST socket option. /// Sets the value of the SO_BROADCAST socket option.
bool getBroadcast(); bool getBroadcast();
/// Returns the value of the SO_BROADCAST socket option. /// Returns the value of the SO_BROADCAST socket option.
@@ -397,7 +397,7 @@ public:
protected: protected:
SocketImpl(); SocketImpl();
/// Creates a SocketImpl. /// Creates a SocketImpl.
SocketImpl(poco_socket_t sockfd); SocketImpl(poco_socket_t sockfd);
/// Creates a SocketImpl using the given native socket. /// Creates a SocketImpl using the given native socket.
@@ -443,22 +443,23 @@ protected:
static void error(int code); static void error(int code);
/// Throws an appropriate exception for the given error code. /// Throws an appropriate exception for the given error code.
static void error(int code, const std::string& arg); static void error(int code, const std::string& arg);
/// Throws an appropriate exception for the given error code. /// Throws an appropriate exception for the given error code.
private: private:
SocketImpl(const SocketImpl&); SocketImpl(const SocketImpl&);
SocketImpl& operator = (const SocketImpl&); SocketImpl& operator = (const SocketImpl&);
poco_socket_t _sockfd; poco_socket_t _sockfd;
Poco::Timespan _recvTimeout; Poco::Timespan _recvTimeout;
Poco::Timespan _sndTimeout; Poco::Timespan _sndTimeout;
bool _blocking; bool _blocking;
bool _isBrokenTimeout; bool _isBrokenTimeout;
friend class Socket; friend class Socket;
friend class SecureSocketImpl; friend class SecureSocketImpl;
friend class PollSetImpl;
}; };

518
Net/src/PollSet.cpp Normal file
View File

@@ -0,0 +1,518 @@
//
// PollSet.cpp
//
// Library: Net
// Package: Sockets
// Module: PollSet
//
// Copyright (c) 2016, Applied Informatics Software Engineering GmbH.
// All rights reserved.
//
// SPDX-License-Identifier: BSL-1.0
//
#include "Poco/Net/PollSet.h"
#include "Poco/Net/SocketImpl.h"
#include "Poco/Mutex.h"
#include <set>
#if defined(_WIN32) && _WIN32_WINNT >= 0x0600
#ifndef POCO_HAVE_FD_POLL
#define POCO_HAVE_FD_POLL 1
#endif
#elif defined(POCO_OS_FAMILY_BSD)
#ifndef POCO_HAVE_FD_POLL
#define POCO_HAVE_FD_POLL 1
#endif
#endif
#if defined(POCO_HAVE_FD_EPOLL)
#include <sys/epoll.h>
#elif defined(POCO_HAVE_FD_POLL)
#ifndef _WIN32
#include <poll.h>
#endif
#endif
namespace Poco {
namespace Net {
#if defined(POCO_HAVE_FD_EPOLL)
//
// Linux implementation using epoll
//
class PollSetImpl
{
public:
PollSetImpl():
_epollfd(-1),
_events(1024)
{
_epollfd = epoll_create(1);
if (_epollfd < 0)
{
SocketImpl::error();
}
}
~PollSetImpl()
{
if (_epollfd >= 0)
::close(_epollfd);
}
void add(const Socket& socket, int mode)
{
Poco::FastMutex::ScopedLock lock(_mutex);
poco_socket_t fd = socket.impl()->sockfd();
struct epoll_event ev;
ev.events = 0;
if (mode & PollSet::POLL_READ)
ev.events |= EPOLLIN;
if (mode & PollSet::POLL_WRITE)
ev.events |= EPOLLOUT;
if (mode & PollSet::POLL_ERROR)
ev.events |= EPOLLERR;
ev.data.ptr = socket.impl();
int err = epoll_ctl(_epollfd, EPOLL_CTL_ADD, fd, &ev);
if (err) SocketImpl::error();
_socketMap[socket.impl()] = socket;
}
void remove(const Socket& socket)
{
Poco::FastMutex::ScopedLock lock(_mutex);
poco_socket_t fd = socket.impl()->sockfd();
struct epoll_event ev;
ev.events = 0;
ev.data.ptr = 0;
int err = epoll_ctl(_epollfd, EPOLL_CTL_DEL, fd, &ev);
if (err) SocketImpl::error();
_socketMap.erase(socket.impl());
}
void update(const Socket& socket, int mode)
{
poco_socket_t fd = socket.impl()->sockfd();
struct epoll_event ev;
ev.events = 0;
if (mode & PollSet::POLL_READ)
ev.events |= EPOLLIN;
if (mode & PollSet::POLL_WRITE)
ev.events |= EPOLLOUT;
if (mode & PollSet::POLL_ERROR)
ev.events |= EPOLLERR;
ev.data.ptr = socket.impl();
int err = epoll_ctl(_epollfd, EPOLL_CTL_MOD, fd, &ev);
if (err)
{
SocketImpl::error();
}
}
void clear()
{
Poco::FastMutex::ScopedLock lock(_mutex);
::close(_epollfd);
_socketMap.clear();
_epollfd = epoll_create(1);
if (_epollfd < 0)
{
SocketImpl::error();
}
}
PollSet::SocketModeMap poll(const Poco::Timespan& timeout)
{
PollSet::SocketModeMap result;
if (_socketMap.empty()) return result;
Poco::Timespan remainingTime(timeout);
int rc;
do
{
Poco::Timestamp start;
rc = epoll_wait(_epollfd, &_events[0], _events.size(), remainingTime.totalMilliseconds());
if (rc < 0 && SocketImpl::lastError() == POCO_EINTR)
{
Poco::Timestamp end;
Poco::Timespan waited = end - start;
if (waited < remainingTime)
remainingTime -= waited;
else
remainingTime = 0;
}
}
while (rc < 0 && SocketImpl::lastError() == POCO_EINTR);
if (rc < 0) SocketImpl::error();
Poco::FastMutex::ScopedLock lock(_mutex);
for (int i = 0; i < rc; i++)
{
std::map<void*, Socket>::iterator it = _socketMap.find(_events[i].data.ptr);
if (it != _socketMap.end())
{
if (_events[i].events & EPOLLIN)
result[it->second] |= PollSet::POLL_READ;
if (_events[i].events & EPOLLOUT)
result[it->second] |= PollSet::POLL_WRITE;
if (_events[i].events & EPOLLERR)
result[it->second] |= PollSet::POLL_ERROR;
}
}
return result;
}
private:
Poco::FastMutex _mutex;
int _epollfd;
std::map<void*, Socket> _socketMap;
std::vector<struct epoll_event> _events;
};
#elif defined(POCO_HAVE_FD_POLL)
//
// BSD implementation using poll
//
class PollSetImpl
{
public:
void add(const Socket& socket, int mode)
{
Poco::FastMutex::ScopedLock lock(_mutex);
poco_socket_t fd = socket.impl()->sockfd();
_addMap[fd] = mode;
_removeSet.erase(fd);
_socketMap[fd] = socket;
}
void remove(const Socket& socket)
{
Poco::FastMutex::ScopedLock lock(_mutex);
poco_socket_t fd = socket.impl()->sockfd();
_removeSet.insert(fd);
_addMap.erase(fd);
_socketMap.erase(fd);
}
void update(const Socket& socket, int mode)
{
Poco::FastMutex::ScopedLock lock(_mutex);
poco_socket_t fd = socket.impl()->sockfd();
for (std::vector<pollfd>::iterator it = _pollfds.begin(); it != _pollfds.end(); ++it)
{
if (it->fd == fd)
{
it->events = 0;
if (mode & PollSet::POLL_READ)
it->events |= POLLIN;
if (mode & PollSet::POLL_WRITE)
it->events |= POLLOUT;
}
}
}
void clear()
{
Poco::FastMutex::ScopedLock lock(_mutex);
_socketMap.clear();
_addMap.clear();
_removeSet.clear();
_pollfds.clear();
}
PollSet::SocketModeMap poll(const Poco::Timespan& timeout)
{
PollSet::SocketModeMap result;
{
Poco::FastMutex::ScopedLock lock(_mutex);
if (!_removeSet.empty())
{
for (std::vector<pollfd>::iterator it = _pollfds.begin(); it != _pollfds.end();)
{
if (_removeSet.find(it->fd) != _removeSet.end())
{
it = _pollfds.erase(it);
}
else ++it;
}
_removeSet.clear();
}
_pollfds.reserve(_pollfds.size() + _addMap.size());
for (std::map<poco_socket_t, int>::iterator it = _addMap.begin(); it != _addMap.end(); ++it)
{
pollfd pfd;
pfd.fd = it->first;
pfd.events = 0;
pfd.revents = 0;
if (it->second & PollSet::POLL_READ)
pfd.events |= POLLIN;
if (it->second & PollSet::POLL_WRITE)
pfd.events |= POLLOUT;
_pollfds.push_back(pfd);
}
_addMap.clear();
}
if (_pollfds.empty()) return result;
Poco::Timespan remainingTime(timeout);
int rc;
do
{
Poco::Timestamp start;
#ifdef _WIN32
rc = WSAPoll(&_pollfds[0], _pollfds.size(), static_cast<INT>(timeout.totalMilliseconds()));
#else
rc = ::poll(&_pollfds[0], _pollfds.size(), timeout.totalMilliseconds());
#endif
if (rc < 0 && SocketImpl::lastError() == POCO_EINTR)
{
Poco::Timestamp end;
Poco::Timespan waited = end - start;
if (waited < remainingTime)
remainingTime -= waited;
else
remainingTime = 0;
}
}
while (rc < 0 && SocketImpl::lastError() == POCO_EINTR);
if (rc < 0) SocketImpl::error();
{
Poco::FastMutex::ScopedLock lock(_mutex);
if (!_socketMap.empty())
{
for (std::vector<pollfd>::iterator it = _pollfds.begin(); it != _pollfds.end(); ++it)
{
std::map<poco_socket_t, Socket>::const_iterator its = _socketMap.find(it->fd);
if (its != _socketMap.end())
{
if (it->revents & POLLIN)
result[its->second] |= PollSet::POLL_READ;
if (it->revents & POLLOUT)
result[its->second] |= PollSet::POLL_WRITE;
if (it->revents & POLLERR)
result[its->second] |= PollSet::POLL_ERROR;
}
it->revents = 0;
}
}
}
return result;
}
private:
Poco::FastMutex _mutex;
std::map<poco_socket_t, Socket> _socketMap;
std::map<poco_socket_t, int> _addMap;
std::set<poco_socket_t> _removeSet;
std::vector<pollfd> _pollfds;
};
#else
//
// Fallback implementation using select()
//
class PollSetImpl
{
public:
void add(const Socket& socket, int mode)
{
Poco::FastMutex::ScopedLock lock(_mutex);
_map[socket] = mode;
}
void remove(const Socket& socket)
{
Poco::FastMutex::ScopedLock lock(_mutex);
_map.erase(socket);
}
void update(const Socket& socket, int mode)
{
Poco::FastMutex::ScopedLock lock(_mutex);
_map[socket] = mode;
}
void clear()
{
Poco::FastMutex::ScopedLock lock(_mutex);
_map.clear();
}
PollSet::SocketModeMap poll(const Poco::Timespan& timeout)
{
fd_set fdRead;
fd_set fdWrite;
fd_set fdExcept;
int nfd = 0;
FD_ZERO(&fdRead);
FD_ZERO(&fdWrite);
FD_ZERO(&fdExcept);
{
Poco::FastMutex::ScopedLock lock(_mutex);
for (PollSet::SocketModeMap::const_iterator it = _map.begin(); it != _map.end(); ++it)
{
poco_socket_t fd = it->first.impl()->sockfd();
if (fd != POCO_INVALID_SOCKET && it->second)
{
if (int(fd) > nfd) nfd = int(fd);
if (it->second & PollSet::POLL_READ)
{
FD_SET(fd, &fdRead);
}
if (it->second & PollSet::POLL_WRITE)
{
FD_SET(fd, &fdWrite);
}
if (it->second & PollSet::POLL_ERROR)
{
FD_SET(fd, &fdExcept);
}
}
}
}
PollSet::SocketModeMap result;
if (nfd == 0) return result;
Poco::Timespan remainingTime(timeout);
int rc;
do
{
struct timeval tv;
tv.tv_sec = (long) remainingTime.totalSeconds();
tv.tv_usec = (long) remainingTime.useconds();
Poco::Timestamp start;
rc = ::select(nfd + 1, &fdRead, &fdWrite, &fdExcept, &tv);
if (rc < 0 && SocketImpl::lastError() == POCO_EINTR)
{
Poco::Timestamp end;
Poco::Timespan waited = end - start;
if (waited < remainingTime)
remainingTime -= waited;
else
remainingTime = 0;
}
}
while (rc < 0 && SocketImpl::lastError() == POCO_EINTR);
if (rc < 0) SocketImpl::error();
{
Poco::FastMutex::ScopedLock lock(_mutex);
for (PollSet::SocketModeMap::const_iterator it = _map.begin(); it != _map.end(); ++it)
{
poco_socket_t fd = it->first.impl()->sockfd();
if (fd != POCO_INVALID_SOCKET)
{
if (FD_ISSET(fd, &fdRead))
{
result[it->first] |= PollSet::POLL_READ;
}
if (FD_ISSET(fd, &fdWrite))
{
result[it->first] |= PollSet::POLL_WRITE;
}
if (FD_ISSET(fd, &fdExcept))
{
result[it->first] |= PollSet::POLL_ERROR;
}
}
}
}
return result;
}
private:
Poco::FastMutex _mutex;
PollSet::SocketModeMap _map;
};
#endif
PollSet::PollSet():
_pImpl(new PollSetImpl)
{
}
PollSet::~PollSet()
{
delete _pImpl;
}
void PollSet::add(const Socket& socket, int mode)
{
_pImpl->add(socket, mode);
}
void PollSet::remove(const Socket& socket)
{
_pImpl->remove(socket);
}
void PollSet::update(const Socket& socket, int mode)
{
_pImpl->update(socket, mode);
}
void PollSet::clear()
{
_pImpl->clear();
}
PollSet::SocketModeMap PollSet::poll(const Poco::Timespan& timeout)
{
return _pImpl->poll(timeout);
}
} } // namespace Poco::Net

View File

@@ -26,7 +26,8 @@ objects = \
NTPClientTest NTPClientTestSuite \ NTPClientTest NTPClientTestSuite \
WebSocketTest WebSocketTestSuite \ WebSocketTest WebSocketTestSuite \
SyslogTest \ SyslogTest \
OAuth10CredentialsTest OAuth20CredentialsTest OAuthTestSuite OAuth10CredentialsTest OAuth20CredentialsTest OAuthTestSuite \
PollSetTest
target = testrunner target = testrunner
target_version = 1 target_version = 1

View File

@@ -0,0 +1,135 @@
//
// PollSetTest.cpp
//
// Copyright (c) 2016, Applied Informatics Software Engineering GmbH.
// and Contributors.
//
// SPDX-License-Identifier: BSL-1.0
//
#include "PollSetTest.h"
#include "CppUnit/TestCaller.h"
#include "CppUnit/TestSuite.h"
#include "EchoServer.h"
#include "Poco/Net/StreamSocket.h"
#include "Poco/Net/ServerSocket.h"
#include "Poco/Net/SocketAddress.h"
#include "Poco/Net/NetException.h"
#include "Poco/Net/PollSet.h"
#include "Poco/Stopwatch.h"
using Poco::Net::Socket;
using Poco::Net::StreamSocket;
using Poco::Net::ServerSocket;
using Poco::Net::SocketAddress;
using Poco::Net::ConnectionRefusedException;
using Poco::Net::PollSet;
using Poco::Timespan;
using Poco::Stopwatch;
PollSetTest::PollSetTest(const std::string& name): CppUnit::TestCase(name)
{
}
PollSetTest::~PollSetTest()
{
}
void PollSetTest::testPoll()
{
EchoServer echoServer1;
EchoServer echoServer2;
StreamSocket ss1;
StreamSocket ss2;
ss1.connect(SocketAddress("127.0.0.1", echoServer1.port()));
ss2.connect(SocketAddress("127.0.0.1", echoServer2.port()));
PollSet ps;
ps.add(ss1, PollSet::POLL_READ);
// nothing readable
Stopwatch sw;
sw.start();
Timespan timeout(1000000);
assert (ps.poll(timeout).empty());
assert (sw.elapsed() >= 900000);
sw.restart();
ps.add(ss2, PollSet::POLL_READ);
// ss1 must be writable, if polled for
ps.update(ss1, PollSet::POLL_READ | PollSet::POLL_WRITE);
PollSet::SocketModeMap sm = ps.poll(timeout);
assert (sm.find(ss1) != sm.end());
assert (sm.find(ss2) == sm.end());
assert (sm.find(ss1)->second == PollSet::POLL_WRITE);
assert (sw.elapsed() < 100000);
ps.update(ss1, PollSet::POLL_READ);
ss1.sendBytes("hello", 5);
char buffer[256];
sw.restart();
sm = ps.poll(timeout);
assert (sm.find(ss1) != sm.end());
assert (sm.find(ss2) == sm.end());
assert (sm.find(ss1)->second == PollSet::POLL_READ);
assert (sw.elapsed() < 100000);
int n = ss1.receiveBytes(buffer, sizeof(buffer));
assert (n == 5);
assert (std::string(buffer, n) == "hello");
ss2.sendBytes("HELLO", 5);
sw.restart();
sm = ps.poll(timeout);
assert (sm.find(ss1) == sm.end());
assert (sm.find(ss2) != sm.end());
assert (sm.find(ss2)->second == PollSet::POLL_READ);
assert (sw.elapsed() < 100000);
n = ss2.receiveBytes(buffer, sizeof(buffer));
assert (n == 5);
assert (std::string(buffer, n) == "HELLO");
ps.remove(ss2);
ss2.sendBytes("HELLO", 5);
sw.restart();
sm = ps.poll(timeout);
assert (sm.empty());
n = ss2.receiveBytes(buffer, sizeof(buffer));
assert (n == 5);
assert (std::string(buffer, n) == "HELLO");
ss1.close();
ss2.close();
}
void PollSetTest::setUp()
{
}
void PollSetTest::tearDown()
{
}
CppUnit::Test* PollSetTest::suite()
{
CppUnit::TestSuite* pSuite = new CppUnit::TestSuite("PollSetTest");
CppUnit_addTest(pSuite, PollSetTest, testPoll);
return pSuite;
}

View File

@@ -0,0 +1,36 @@
//
// PollSetTest.h
//
// Definition of the PollSetTest class.
//
// Copyright (c) 2016, Applied Informatics Software Engineering GmbH.
// and Contributors.
//
// SPDX-License-Identifier: BSL-1.0
//
#ifndef PollSetTest_INCLUDED
#define PollSetTest_INCLUDED
#include "Poco/Net/Net.h"
#include "CppUnit/TestCase.h"
class PollSetTest: public CppUnit::TestCase
{
public:
PollSetTest(const std::string& name);
~PollSetTest();
void testPoll();
void setUp();
void tearDown();
static CppUnit::Test* suite();
};
#endif // PollSetTest_INCLUDED

View File

@@ -15,6 +15,7 @@
#include "MulticastSocketTest.h" #include "MulticastSocketTest.h"
#include "DialogSocketTest.h" #include "DialogSocketTest.h"
#include "RawSocketTest.h" #include "RawSocketTest.h"
#include "PollSetTest.h"
CppUnit::Test* SocketsTestSuite::suite() CppUnit::Test* SocketsTestSuite::suite()
@@ -29,5 +30,6 @@ CppUnit::Test* SocketsTestSuite::suite()
#ifdef POCO_NET_HAS_INTERFACE #ifdef POCO_NET_HAS_INTERFACE
pSuite->addTest(MulticastSocketTest::suite()); pSuite->addTest(MulticastSocketTest::suite());
#endif #endif
pSuite->addTest(PollSetTest::suite());
return pSuite; return pSuite;
} }