浏览代码

* Implemented 'file_event' class to handle the different file event implementation (like select, poll and epoll)

master
bergmann 6 年前
父节点
当前提交
ffd7df63e7
共有 24 个文件被更改,包括 1273 次插入176 次删除
  1. +11
    -0
      include/cppcore/config.h
  2. +17
    -2
      include/cppcore/conversion/string.h
  3. +5
    -2
      include/cppcore/conversion/string.inl
  4. +0
    -2
      include/cppcore/conversion/time.inl
  5. +1
    -1
      include/cppcore/misc/flags.h
  6. +1
    -2
      include/cppcore/platform.h
  7. +0
    -26
      include/cppcore/platform/fdset.inl
  8. +19
    -0
      include/cppcore/platform/file_events.h
  9. +99
    -0
      include/cppcore/platform/file_events/driver/epoll.linux.h
  10. +169
    -0
      include/cppcore/platform/file_events/driver/epoll.linux.inl
  11. +19
    -6
      include/cppcore/platform/file_events/driver/fdset.linux.h
  12. +65
    -0
      include/cppcore/platform/file_events/driver/fdset.linux.inl
  13. +94
    -0
      include/cppcore/platform/file_events/driver/poll.linux.h
  14. +192
    -0
      include/cppcore/platform/file_events/driver/poll.linux.inl
  15. +73
    -0
      include/cppcore/platform/file_events/driver/select.linux.h
  16. +135
    -0
      include/cppcore/platform/file_events/driver/select.linux.inl
  17. +91
    -0
      include/cppcore/platform/file_events/file_events.h
  18. +69
    -0
      include/cppcore/platform/file_events/file_events.inl
  19. +0
    -0
     
  20. +41
    -0
      include/cppcore/platform/file_events/types.h
  21. +32
    -0
      include/cppcore/platform/file_events/types.linux.inl
  22. +0
    -70
      include/cppcore/platform/select.h
  23. +0
    -65
      include/cppcore/platform/select.inl
  24. +140
    -0
      test/cppcore/platform/file_events_tests.cpp

+ 11
- 0
include/cppcore/config.h 查看文件

@@ -0,0 +1,11 @@
#pragma once

#define cppcore_os_linux 1
#define cppcore_os_windows 2
#define cppcore_os_ios 3

#if defined(__linux__)
#define cppcore_os cppcore_os_linux
#else
#error "Unknown or unsupported operation system!"
#endif

+ 17
- 2
include/cppcore/conversion/string.h 查看文件

@@ -115,14 +115,29 @@ namespace cppcore
*
* @tparam T_predicate Type of predicate to execute for each element.
*
* @param s String to split.
* @param str String to split.
* @param seperator Seperator to split string at.
* @param predicate Predicate to execute for each element.
*
* @return Value returned from the predicate.
*/
template<typename T_predicate>
inline bool string_split(const std::string& s, char seperator, const T_predicate& predicate);
inline bool string_split(const std::string& str, char seperator, const T_predicate& predicate);

/**
* @brief Split string at the passed seperator and calls the passed predicate for each element.
*
* @tparam T_splitter Type of predicate to evaluate character to split string at.
* @tparam T_predicate Type of predicate to execute for each element.
*
* @param str String to split.
* @param splitter Predicate to evaluate character to split string at.
* @param predicate Predicate to execute for each element.
*
* @return Value returned from the predicate.
*/
template<typename T_splitter, typename T_predicate>
inline bool string_split(const std::string& str, const T_splitter& splitter, const T_predicate& predicate);

/**
* @brief Class to handle string conversions.


+ 5
- 2
include/cppcore/conversion/string.inl 查看文件

@@ -44,6 +44,10 @@ namespace cppcore

template<typename T_predicate>
inline bool string_split(const std::string& str, char seperator, T_predicate&& predicate)
{ return string_split(str, [&](auto c) { return c == seperator; }, predicate); }

template<typename T_splitter, typename T_predicate>
bool string_split(const std::string& str, const T_splitter& splitter, const T_predicate& predicate)
{
auto* i = str.c_str();
auto* e = str.c_str() + str.size();
@@ -51,8 +55,7 @@ namespace cppcore
while (i <= e)
{
if ( s != e
&& ( *i == seperator
|| *i == '\0'))
&& (*i == '\0' || splitter(*i)))
{
std::string tmp(s, convert_cast<size_t>(i - s));
if (!predicate(std::move(tmp)))


+ 0
- 2
include/cppcore/conversion/time.inl 查看文件

@@ -21,7 +21,6 @@ namespace cppcore
tv.tv_usec = std::chrono::duration_cast<std::chrono::microseconds>(d - sec).count();
return tv;
}

};

template<typename Rep, typename Period>
@@ -45,7 +44,6 @@ namespace cppcore
ts.tv_nsec = std::chrono::duration_cast<std::chrono::nanoseconds>(d - sec).count();
return ts;
}

};

template<typename Rep, typename Period>


+ 1
- 1
include/cppcore/misc/flags.h 查看文件

@@ -185,7 +185,7 @@ namespace cppcore
inline void clear(enum_type e);

/**
* @brief REset all flags.
* @brief Reset all flags.
*/
inline void reset();



+ 1
- 2
include/cppcore/platform.h 查看文件

@@ -1,4 +1,3 @@
#pragma once

#include "platform/fdset.h"
#include "platform/select.h"
#include "platform/file_events.h"

+ 0
- 26
include/cppcore/platform/fdset.inl 查看文件

@@ -1,26 +0,0 @@
#pragma once

#include "fdset.h"

namespace cppcore
{

/* fdset */

static_assert(
sizeof(fd_set) == sizeof(fdset),
"Size of fd set implementation does not match.");

bool fdset::is_set(int fd) const
{ return FD_ISSET(fd, this); }

void fdset::set(int fd)
{ FD_SET(fd, this); }

void fdset::clear(int fd)
{ FD_CLR(fd, this); }

void fdset::reset()
{ FD_ZERO(this); }

}

+ 19
- 0
include/cppcore/platform/file_events.h 查看文件

@@ -0,0 +1,19 @@
#pragma once

#include <cppcore/config.h>

#include "file_events/file_events.h"

#include "file_events/file_events.inl"

#if cppcore_os == cppcore_os_linux
#include "file_events/driver/poll.linux.h"
#include "file_events/driver/epoll.linux.h"
#include "file_events/driver/fdset.linux.h"
#include "file_events/driver/select.linux.h"

#include "file_events/driver/poll.linux.inl"
#include "file_events/driver/epoll.linux.inl"
#include "file_events/driver/fdset.linux.inl"
#include "file_events/driver/select.linux.inl"
#endif

+ 99
- 0
include/cppcore/platform/file_events/driver/epoll.linux.h 查看文件

@@ -0,0 +1,99 @@
#pragma once

#include <vector>
#include <sys/epoll.h>

#include "../types.h"

namespace cppcore {
namespace driver {

struct epoll
{
public:
static constexpr int DEFAULT_SIZE = 30;

struct storage
: public std::vector<epoll_event>
{
public:
inline bool is_set(
const event_types& p_events) const;

template<typename X_handle>
inline bool is_set(
const X_handle& p_handle,
const event_types& p_events) const;
};

using result_type = storage;

private:
size_t _size;
int _handle;
result_type& _result;

public:
/**
* @brief Constructor.
*/
inline epoll(result_type& p_result, int p_size = DEFAULT_SIZE);

/**
* @brief Move constructor.
*/
inline epoll(epoll&&) = default;

/**
* @brief Copy constructor.
*/
inline epoll(const epoll&) = delete;

/**
* @brief Destructor.
*/
inline ~epoll();

public: /* file_events implementation */
/**
* @brief Add or change a event listener for the passed file.
*/
template<typename X_handle>
inline void add(
const X_handle& p_handle,
const event_types& p_events);

/**
* @brief Remove the event listener for the passed handle.
*/
template<typename X_handle>
inline void erase(
const X_handle& p_handle);

/**
* @brief Wait for file events and return the result.
*/
inline void wait(
const event_timeout * p_timeout) const;

/**
* @brief Wait for file events and call the passed callback for each event.
*/
template<typename T_lambda>
inline void wait(
T_lambda&& p_lambda,
const event_timeout * p_timeout) const;

private:
/**
* @brief Convert event flags to epoll event mask.
*/
static inline uint32_t make_event_mask(const event_types& p_events);

/**
* @brief Convert event mask to epoll event flags.
*/
static inline event_types make_event_types(uint32_t p_mask);
};

} }

+ 169
- 0
include/cppcore/platform/file_events/driver/epoll.linux.inl 查看文件

@@ -0,0 +1,169 @@
#pragma once

#include <algorithm>

#include <cppcore/misc/exception.h>
#include <cppcore/conversion/time.h>

#include "epoll.linux.h"

namespace cppcore {
namespace driver {

/* epoll::storage */

bool epoll::storage::is_set(
const event_types& p_events) const
{
auto mask = epoll::make_event_mask(p_events);

for (auto& ev : *this)
{
if (ev.events & mask)
return true;
}

return false;
}

template<typename X_handle>
bool epoll::storage::is_set(
const X_handle& p_handle,
const event_types& p_events) const
{
using handle_helper_type = handle_helper<X_handle>;

auto fd = handle_helper_type::get_raw_handle(p_handle);
auto mask = epoll::make_event_mask(p_events);

for (auto& ev : *this)
{
if ( ev.data.fd == fd
&& (ev.events & mask))
return true;
}

return false;
}

/* epoll */

epoll::epoll(result_type& p_result, int p_size)
: _size (static_cast<size_t>(p_size > 0 ? p_size : DEFAULT_SIZE))
, _handle (-1)
, _result (p_result)
{
_handle = epoll_create(static_cast<int>(_size));
if (_handle < 0)
throw error_exception("Unable to create 'epoll' instance", errno);
}

epoll::~epoll()
{
::close(_handle);
}

template<typename X_handle>
void epoll::add(
const X_handle& p_handle,
const event_types& p_events)
{
using handle_helper_type = handle_helper<X_handle>;

auto fd = handle_helper_type::get_raw_handle(p_handle);

struct epoll_event ev;
ev.events = make_event_mask(p_events);
ev.data.fd = fd;

if (epoll_ctl(_handle, EPOLL_CTL_ADD, fd, &ev) == 0)
return;

auto err = errno;
if (err != EEXIST)
throw error_exception("Error while adding file listener to 'epoll' instance", err);

if (epoll_ctl(_handle, EPOLL_CTL_MOD, fd, &ev) != 0)
throw error_exception("Error while modifying file listener of 'epoll' instance", errno);
}

template<typename X_handle>
void epoll::erase(const X_handle& p_handle)
{
using handle_helper_type = handle_helper<X_handle>;

auto fd = handle_helper_type::get_raw_handle(p_handle);

struct epoll_event ev;
ev.events = 0;
ev.data.ptr = nullptr;

if (epoll_ctl(_handle, EPOLL_CTL_DEL, fd, &ev) != 0)
throw error_exception("Error while removing file listener from 'epoll' instance", errno);
}

void epoll::wait(const event_timeout * p_timeout) const
{
int timeout = 0;
if (p_timeout)
{
auto now = std::chrono::steady_clock::now();
if (now < *p_timeout)
{
auto diff = *p_timeout - now;
timeout = std::chrono::duration_cast<std::chrono::duration<int, std::milli>>(diff).count();
}
}

_result.resize(_size);
int cnt = epoll_pwait(
_handle,
_result.data(),
static_cast<int>(_result.size()),
p_timeout
? timeout
: -1,
nullptr);

if (cnt < 0)
{
auto err = errno;
if (err != EINTR)
throw cppcore::error_exception("Error while fetching file events with 'epoll'", err);
}

_result.resize(static_cast<size_t>(cnt));
}

template<typename T_lambda>
void epoll::wait(T_lambda&& p_lambda, const event_timeout * p_timeout) const
{
wait(p_timeout);

for (auto& ev : _result)
p_lambda(ev.data.fd, make_event_types(ev.events));
}

uint32_t epoll::make_event_mask(const event_types& p_events)
{
uint32_t ret = 0;

if (p_events.is_set(event_type::read)) ret |= EPOLLRDNORM | EPOLLRDBAND | EPOLLIN | EPOLLHUP | EPOLLERR;
if (p_events.is_set(event_type::write)) ret |= EPOLLWRBAND | EPOLLWRNORM | EPOLLOUT | EPOLLERR;
if (p_events.is_set(event_type::except)) ret |= EPOLLPRI;

return ret;
}

event_types epoll::make_event_types(uint32_t p_mask)
{
event_types ret;

if (p_mask & (EPOLLRDNORM | EPOLLRDBAND | EPOLLIN | EPOLLHUP | EPOLLERR)) ret.set(event_type::read);
if (p_mask & (EPOLLWRBAND | EPOLLWRNORM | EPOLLOUT | EPOLLERR)) ret.set(event_type::write);
if (p_mask & (EPOLLPRI)) ret.set(event_type::except);

return ret;
}

} }

include/cppcore/platform/fdset.h → include/cppcore/platform/file_events/driver/fdset.linux.h 查看文件

@@ -2,13 +2,28 @@

#include <sys/select.h>

namespace cppcore
{
namespace cppcore {
namespace driver {

struct fdset
struct fdset final
: public fd_set
{
public:
/**
* @brief Constructor.
*/
inline fdset();

/**
* @brief Check if any file descriptor is set in this set.
*
* @param[in] max_fd Largest file descriptor stored.
*
* @retval true If any file descriptor is set.
* @retval false If no file descriptor is set.
*/
inline bool is_any(int max_fd) const;

/**
* @brief Check if the passed file descriptor is set in this set.
*
@@ -39,6 +54,4 @@ namespace cppcore
inline void reset();
};

}

#include "fdset.inl"
} }

+ 65
- 0
include/cppcore/platform/file_events/driver/fdset.linux.inl 查看文件

@@ -0,0 +1,65 @@
#pragma once

#include <cppcore/misc/exception.h>

#include "fdset.linux.h"

namespace cppcore {
namespace driver {

/* fdset */

static_assert(
sizeof(fd_set) == sizeof(fdset),
"Size of fd set implementation does not match.");

fdset::fdset()
{
FD_ZERO(this);
}

bool fdset::is_any(int max_fd) const
{
static constexpr int field_count = sizeof(fds_bits) / sizeof(fds_bits[0]);

if (max_fd >= FD_SETSIZE)
throw exception("Size of fd set exceeded!");

if (max_fd < 0)
return false;

auto cnt = (max_fd / field_count) + 1;
for (int i = 0; i < cnt; ++i)
{
if (fds_bits[i])
return true;
}

return false;
}

bool fdset::is_set(int fd) const
{
if (fd >= FD_SETSIZE)
throw exception("Size of fd set exceeded!");
return FD_ISSET(fd, this);
}

void fdset::set(int fd)
{
if (fd >= FD_SETSIZE)
throw exception("Size of fd set exceeded!");
FD_SET(fd, this);
}

void fdset::clear(int fd)
{
if (fd >= FD_SETSIZE)
throw exception("Size of fd set exceeded!");
FD_CLR(fd, this);
}

void fdset::reset()
{ FD_ZERO(this); }

} }

+ 94
- 0
include/cppcore/platform/file_events/driver/poll.linux.h 查看文件

@@ -0,0 +1,94 @@
#pragma once

#include <poll.h>
#include <vector>

#include "../types.h"

namespace cppcore {
namespace driver {

struct poll
{
public:
struct storage
: public std::vector<pollfd>
{
public:
friend struct poll;

private:
bool _cleared { true };

public:
inline bool is_set(
const event_types& p_events) const;

template<typename X_handle>
inline bool is_set(
const X_handle& p_handle,
const event_types& p_events) const;

inline void clear();

public:
template<typename X_storage>
static inline auto find(
X_storage& p_storage,
int p_handle);
};

using result_type = storage;

private:
result_type& _result;

public:
/**
* @brief Constructor.
*/
inline poll(result_type& p_result);

public: /* file_events implementation */
/**
* @brief Add or change a event listener for the passed file.
*/
template<typename X_handle>
inline void add(
const X_handle& p_handle,
const event_types& p_events);

/**
* @brief Remove the event listener for the passed handle.
*/
template<typename X_handle>
inline void erase(
const X_handle& p_handle);

/**
* @brief Wait for file events and return the result.
*/
inline int wait(
const event_timeout * p_timeout) const;

/**
* @brief Wait for file events and call the passed callback for each event.
*/
template<typename T_lambda>
inline void wait(
T_lambda&& p_lambda,
const event_timeout * p_timeout) const;

private:
/**
* @brief Convert event flags to poll event mask.
*/
static inline short int make_event_mask(const event_types& p_events);

/**
* @brief Convert event mask to poll event flags.
*/
static inline event_types make_event_types(short int p_mask);
};

} }

+ 192
- 0
include/cppcore/platform/file_events/driver/poll.linux.inl 查看文件

@@ -0,0 +1,192 @@
#pragma once

#include <algorithm>

#include <cppcore/misc/exception.h>
#include <cppcore/conversion/time.h>

#include "poll.linux.h"

namespace cppcore {
namespace driver {

/* poll::storage */

bool poll::storage::is_set(
const event_types& p_events) const
{
auto mask = poll::make_event_mask(p_events);

for (auto& v : *this)
{
if (v.revents & mask)
return true;
}

return false;
}

template<typename X_handle>
bool poll::storage::is_set(
const X_handle& p_handle,
const event_types& p_events) const
{
using handle_helper_type = handle_helper<X_handle>;

auto fd = handle_helper_type::get_raw_handle(p_handle);
auto mask = poll::make_event_mask(p_events);

auto it = find(*this, fd);
return (it != end())
&& (it->fd == fd)
&& (it->revents & mask);
}

void poll::storage::clear()
{
if (!_cleared)
{
_cleared = true;
for (auto& v : *this)
v.revents = 0;
}
}

template<typename X_storage>
auto poll::storage::find(
X_storage& p_storage,
int p_handle)
{
struct less_comparer
{
constexpr bool operator()(const pollfd& lhv, const pollfd& rhv) const
{ return lhv.fd < rhv.fd; }
};

pollfd value;
value.fd = p_handle;

return std::lower_bound(p_storage.begin(), p_storage.end(), value, less_comparer { });
}

/* poll */

poll::poll(result_type& p_result)
: _result(p_result)
{ }

template<typename X_handle>
void poll::add(
const X_handle& p_handle,
const event_types& p_events)
{
using handle_helper_type = handle_helper<X_handle>;

pollfd value;
value.fd = handle_helper_type::get_raw_handle(p_handle);
value.events = make_event_mask(p_events);
value.revents = 0;

auto it = storage::find(_result, value.fd);
auto found = (it != _result.end()) && (it->fd == value.fd);

if (found) *it = value;
else _result.insert(it, value);
}

template<typename X_handle>
void poll::erase(const X_handle& p_handle)
{
using handle_helper_type = handle_helper<X_handle>;

pollfd value;
value.fd = handle_helper_type::get_raw_handle(p_handle);
value.events = 0;
value.revents = 0;

auto it = storage::find(_result, value.fd);
auto found = (it != _result.end()) && (it->fd == value.fd);

if (found)
_result.erase(it);
}

int poll::wait(const event_timeout * p_timeout) const
{
struct timespec timeout { 0, 0 };
if (p_timeout)
{
auto now = std::chrono::steady_clock::now();
if (now < *p_timeout)
{
auto diff = *p_timeout - now;
timeout = cppcore::duration_cast<timespec>(diff);
}
}

_result.clear();

int cnt = ::ppoll(
_result.data(),
_result.size(),
p_timeout
? &timeout
: nullptr,
nullptr);

if (cnt < 0)
{
auto err = errno;
if (err != EINTR)
throw cppcore::error_exception("Error while fetching file events with 'poll'", err);
}

return cnt;
}

template<typename T_lambda>
void poll::wait(T_lambda&& p_lambda, const event_timeout * p_timeout) const
{
int cnt = wait(p_timeout);

for (auto& v : _result)
{
if (v.revents)
{
auto ev = make_event_types(v.revents);
v.revents = 0;

if (ev)
p_lambda(v.fd, ev);

if (--cnt <= 0)
return;
}
}

_result._cleared = true;
}

short int poll::make_event_mask(const event_types& p_events)
{
short int ret = 0;

if (p_events.is_set(event_type::read)) ret |= POLLRDNORM | POLLRDBAND | POLLIN | POLLHUP | POLLERR;
if (p_events.is_set(event_type::write)) ret |= POLLWRBAND | POLLWRNORM | POLLOUT | POLLERR;
if (p_events.is_set(event_type::except)) ret |= POLLPRI;

return ret;
}

event_types poll::make_event_types(short int p_mask)
{
event_types ret;

if (p_mask & (POLLRDNORM | POLLRDBAND | POLLIN | POLLHUP | POLLERR)) ret.set(event_type::read);
if (p_mask & (POLLWRBAND | POLLWRNORM | POLLOUT | POLLERR)) ret.set(event_type::write);
if (p_mask & (POLLPRI)) ret.set(event_type::except);

return ret;
}

} }

+ 73
- 0
include/cppcore/platform/file_events/driver/select.linux.h 查看文件

@@ -0,0 +1,73 @@
#pragma once

#include <sys/select.h>

#include "../types.h"
#include "fdset.linux.h"

namespace cppcore {
namespace driver {

struct select
{
public:
struct storage
{
int max_fd { -1 };
fdset read;
fdset write;
fdset except;

inline bool is_set(
const event_types& p_events) const;

template<typename X_handle>
inline bool is_set(
const X_handle& p_handle,
const event_types& p_events) const;
};

using result_type = storage;

private:
storage _storage;
result_type& _result;

public:
/**
* @brief Constructor.
*/
inline select(result_type& p_result);

public: /* file_events implementation */
/**
* @brief Add or change a event listener for the passed file.
*/
template<typename X_handle>
inline void add(
const X_handle& p_handle,
const event_types& p_events);

/**
* @brief Remove the event listener for the passed handle.
*/
template<typename X_handle>
inline void erase(
const X_handle& p_handle);

/**
* @brief Wait for file events and return the result.
*/
inline void wait(
const event_timeout * p_timeout) const;

/**
* @brief Wait for file events and call the passed callback for each event.
*/
template<typename T_lambda>
inline void wait(
T_lambda&& p_lambda,
const event_timeout * p_timeout) const;
};

} }

+ 135
- 0
include/cppcore/platform/file_events/driver/select.linux.inl 查看文件

@@ -0,0 +1,135 @@
#pragma once

#include <cppcore/misc/exception.h>
#include <cppcore/conversion/time.h>

#include "select.linux.h"

namespace cppcore {
namespace driver {

/* select::storage */

bool select::storage::is_set(
const event_types& p_events) const
{
return (p_events.is_set(event_type::read) && read.is_any(max_fd))
|| (p_events.is_set(event_type::write) && write.is_any(max_fd))
|| (p_events.is_set(event_type::except) && except.is_any(max_fd));
}

template<typename X_handle>
bool select::storage::is_set(
const X_handle& p_handle,
const event_types& p_events) const
{
using handle_helper_type = handle_helper<X_handle>;

auto fd = handle_helper_type::get_raw_handle(p_handle);

return (p_events.is_set(event_type::read) && read.is_set(fd))
|| (p_events.is_set(event_type::write) && write.is_set(fd))
|| (p_events.is_set(event_type::except) && except.is_set(fd));
}

/* select */

select::select(result_type& p_result)
: _storage ()
, _result (p_result)
{ }

template<typename X_handle>
void select::add(
const X_handle& p_handle,
const event_types& p_events)
{
int fd = p_handle.handle();
if (fd < 0)
throw exception("The passed file is not opened!");

if (p_events.is_set(event_type::read)) _storage.read.set(fd);
if (p_events.is_set(event_type::write)) _storage.write.set(fd);
if (p_events.is_set(event_type::except)) _storage.except.set(fd);

if ( !_storage.read.is_set(fd)
&& !_storage.write.is_set(fd)
&& !_storage.except.is_set(fd))
throw exception("No or unkown events passed!");

_storage.max_fd = std::max(fd, _storage.max_fd);
}

template<typename X_handle>
void select::erase(const X_handle& p_handle)
{
int fd = p_handle.handle();
if (fd < 0)
throw exception("The passed file is not opened!");

_storage.read.clear(fd);
_storage.write.clear(fd);
_storage.except.clear(fd);

if (fd == _storage.max_fd)
{
while (_storage.max_fd >= 0
&& !_storage.read.is_set(_storage.max_fd)
&& !_storage.write.is_set(_storage.max_fd)
&& !_storage.except.is_set(_storage.max_fd))
--_storage.max_fd;
}
}

void select::wait(const event_timeout * p_timeout) const
{
struct timespec timeout { 0, 0 };
if (p_timeout)
{
auto now = std::chrono::steady_clock::now();
if (now < *p_timeout)
{
auto diff = *p_timeout - now;
timeout = cppcore::duration_cast<timespec>(diff);
}
}

_result = _storage;

int cnt = ::pselect(
_result.max_fd + 1,
&_result.read,
&_result.write,
&_result.except,
p_timeout
? &timeout
: nullptr,
nullptr);

if (cnt < 0)
{
auto err = errno;
if (err != EINTR)
throw cppcore::error_exception("Error while fetching file events with 'select'", err);
}
}

template<typename T_lambda>
void select::wait(T_lambda&& p_lambda, const event_timeout * p_timeout) const
{
wait(p_timeout);

for (auto fd = 0; fd <= _result.max_fd; ++fd)
{
event_types ev;

if (_result.read.is_set(fd)) ev.set(event_type::read);
if (_result.write.is_set(fd)) ev.set(event_type::write);
if (_result.except.is_set(fd)) ev.set(event_type::except);

if (ev)
p_lambda(fd, ev);
}
}

} }

+ 91
- 0
include/cppcore/platform/file_events/file_events.h 查看文件

@@ -0,0 +1,91 @@
#pragma once

#include "types.h"

namespace cppcore
{

template<typename T_driver>
struct file_events
{
public:
using driver_type = T_driver;
using result_driver_type = typename driver_type::result_type;

struct result
{
public:
template<typename>
friend struct file_events;

private:
result_driver_type _driver;

public:
/**
* @brief Constructor.
*/
inline result() = default;

/**
* @brief Check if any event has occured in any handle.
*/
bool is_set(
const event_types& p_events = event_types::all()) const;

/**
* @brief Check if any event has occured in the passed handle.
*/
template<typename X_handle>
bool is_set(
const X_handle& p_handle,
const event_types& p_events) const;
};

private:
result _result;
driver_type _driver;

public:
/**
* @brief Constructor.
*/
template<typename... X_args>
inline file_events(X_args&&... p_args);

/**
* @brief Add or change a event listener for the passed file.
*/
template<typename X_handle>
inline void add(
const X_handle& p_handle,
const event_types& p_events);

/**
* @brief Remove the event listener for the passed file.
*/
template<typename X_handle>
inline void erase(
const X_handle& p_handle);

/**
* @brief Reset all event listeners.
*/
inline void reset();

/**
* @brief Wait for file events and return the result.
*/
inline const result& wait(
const event_timeout * p_timeout) const;

/**
* @brief Wait for file events and call the passed callback for each event.
*/
template<typename T_lambda>
inline void wait(
T_lambda&& p_lambda,
const event_timeout * p_timeout) const;
};

}

+ 69
- 0
include/cppcore/platform/file_events/file_events.inl 查看文件

@@ -0,0 +1,69 @@
#pragma once

#include "file_events.h"

namespace cppcore
{

/* file_events::result */

template<typename T_driver>
bool file_events<T_driver>::result::is_set(
const event_types& p_events) const
{
return _driver.is_set(p_events);
}

template<typename T_driver>
template<typename X_handle>
bool file_events<T_driver>::result::is_set(
const X_handle& p_handle,
const event_types& p_events) const
{
return _driver.is_set(p_handle, p_events);
}

/* file_events */

template<typename T_driver>
template<typename... X_args>
file_events<T_driver>::file_events(X_args&&... p_args)
: _driver(_result._driver, std::forward<X_args>(p_args)...)
{ }

template<typename T_driver>
template<typename X_handle>
void file_events<T_driver>::add(
const X_handle& p_handle,
const event_types& p_events)
{ _driver.add(p_handle, p_events); }

template<typename T_driver>
template<typename X_handle>
void file_events<T_driver>::erase(
const X_handle& p_handle)
{ _driver.erase(p_handle); }

template<typename T_driver>
void file_events<T_driver>::reset()
{ _driver = driver_type(); }

template<typename T_driver>
typename file_events<T_driver>::result const &
file_events<T_driver>::wait(
const event_timeout * p_timeout) const
{
_driver.wait(p_timeout);
return _result;
}

template<typename T_driver>
template<typename T_lambda>
void file_events<T_driver>::wait(
T_lambda&& p_lambda,
const event_timeout * p_timeout) const
{
_driver.wait(std::forward<T_lambda>(p_lambda), p_timeout);
}

}

+ 0
- 0
查看文件


+ 41
- 0
include/cppcore/platform/file_events/types.h 查看文件

@@ -0,0 +1,41 @@
#pragma once

#include <chrono>

#include <cppcore/config.h>
#include <cppcore/misc/flags.h>

namespace cppcore
{

enum class event_type
{
unknown = 0,
read,
write,
except,
};

using event_types = cppcore::shifted_flags<event_type>;

using event_clock = std::chrono::steady_clock;
using event_timeout = event_clock::time_point;

/**
* @brief Helper class to implement different methods on generic handle types.
*/
template<typename X_handle, typename = void>
struct handle_helper
{
public:
/**
* @brief Extract the raw handle from the passed handle.
*/
static inline auto get_raw_handle(const X_handle& p_handle) = delete;
};

}

#if cppcore_os == cppcore_os_linux
#include "types.linux.inl"
#endif

+ 32
- 0
include/cppcore/platform/file_events/types.linux.inl 查看文件

@@ -0,0 +1,32 @@
#pragma once

#include "types.h"

namespace cppcore
{

template<>
struct handle_helper<int, void>
{
public:
static inline int get_raw_handle(const int& p_handle)
{ return p_handle; }
};

template<typename X_handle>
struct handle_helper<
X_handle,
std::enable_if_t<
std::is_same_v<
int,
decltype(std::declval<X_handle>().handle())
>
>
>
{
public:
static inline int get_raw_handle(const X_handle& p_handle)
{ return p_handle.handle(); }
};

}

+ 0
- 70
include/cppcore/platform/select.h 查看文件

@@ -1,70 +0,0 @@
#pragma once

#include <chrono>

#include "fdset.h"

namespace cppcore
{

/**
* @brief Wait for the file descriptors passed in the file descriptor sets until
* any file descriptor is read.
*
* @param[in] fd_count Number of file descriptor stored in the file descriptor sets.
* @param[in] read File descriptor set to wait for read operations.
* @param[in] write File descriptor set to wait for write operations.
* @param[in] except File descriptor set to wait for a exceptional state.
*
* @return Number of active file descriptors.
*/
inline uint select(
int fd_count,
fdset& read,
fdset& write,
fdset& except);

/**
* @brief Wait for the file descriptors passed in the file descriptor sets until
* any file descriptor is read, or the timeout has occured.
*
* @param[in] fd_count Number of file descriptor stored in the file descriptor sets.
* @param[in] read File descriptor set to wait for read operations.
* @param[in] write File descriptor set to wait for write operations.
* @param[in] except File descriptor set to wait for a exceptional state.
* @param[in] timeout Relative timeout to wait for te file descriptors.
* May be updated to the time left after select returned.
*
* @return Number of active file descriptors.
*/
template<typename T_rep, typename T_period>
inline uint select(
int fd_count,
fdset& read,
fdset& write,
fdset& except,
std::chrono::duration<T_rep, T_period>& timeout);

/**
* @brief Wait for the file descriptors passed in the file descriptor sets until
* any file descriptor is read, or the timeout has occured.
*
* @param[in] fd_count Number of file descriptor stored in the file descriptor sets.
* @param[in] read File descriptor set to wait for read operations.
* @param[in] write File descriptor set to wait for write operations.
* @param[in] except File descriptor set to wait for a exceptional state.
* @param[in] timeout Relative timeout to wait for te file descriptors.
*
* @return Number of active file descriptors.
*/
template<typename T_rep, typename T_period>
inline uint select(
int fd_count,
fdset& read,
fdset& write,
fdset& except,
const std::chrono::duration<T_rep, T_period>& timeout);

}

#include "select.inl"

+ 0
- 65
include/cppcore/platform/select.inl 查看文件

@@ -1,65 +0,0 @@
#pragma once

#include "select.h"
#include "../misc/exception.h"
#include "../conversion/time.h"

namespace cppcore
{

namespace __impl
{
inline uint throw_select_error(int err)
{
if (err == EINTR)
return 0;
throw error_exception("Error while waiting for select operation", err);
}
}

uint select(
int fd_count,
fdset& read,
fdset& write,
fdset& except)
{
auto ret = ::select(fd_count, &read, &write, &except, nullptr);
if (ret < 0)
return __impl::throw_select_error(errno);
return static_cast<uint>(ret);
}

template<typename T_rep, typename T_period>
uint select(
int fd_count,
fdset& read,
fdset& write,
fdset& except,
std::chrono::duration<T_rep, T_period>& timeout)
{
using duration_type = std::chrono::duration<T_rep, T_period>;

auto tmp = duration_cast<timeval>(timeout);
auto ret = ::select(fd_count, &read, &write, &except, &tmp);
if (ret < 0)
return __impl::throw_select_error(errno);
timeout = duration_cast<duration_type>(tmp);
return static_cast<uint>(ret);
}

template<typename T_rep, typename T_period>
uint select(
int fd_count,
fdset& read,
fdset& write,
fdset& except,
const std::chrono::duration<T_rep, T_period>& timeout)
{
auto tmp = duration_cast<timeval>(timeout);
auto ret = ::select(fd_count, &read, &write, &except, &tmp);
if (ret < 0)
return __impl::throw_select_error(errno);
return static_cast<uint>(ret);
}

}

+ 140
- 0
test/cppcore/platform/file_events_tests.cpp 查看文件

@@ -0,0 +1,140 @@
#include <gtest/gtest.h>

#include <fstream>

#include <cppcore/platform/file_events.h>

using namespace ::testing;
using namespace ::cppcore;

struct pipe_handle
{
private:
int _handle;

public:
pipe_handle(int p_handle)
: _handle(p_handle)
{ }

inline ~pipe_handle()
{ close(_handle); }

int handle() const
{ return _handle; }
};

template<typename T_driver, typename... X_args>
void test_with_callback(X_args&&... p_args)
{
int fds[2];
ASSERT_EQ(0, pipe(fds));

pipe_handle read_handle(fds[0]);
pipe_handle write_handle(fds[1]);

file_events<T_driver> fe(std::forward<X_args>(p_args)...);

fe.add(read_handle, event_types::all());
fe.add(write_handle, event_types::all());

auto timeout = event_clock::now();
fe.wait(
[&](auto fd, auto events) {
EXPECT_EQ(fd, write_handle.handle());
},
&timeout);

write(write_handle.handle(), "fuuu", 4);

uint i = 3;
fe.wait(
[&](auto fd, auto events) {
if ( (i & 1)
&& fd == read_handle.handle())
{
i &= ~static_cast<uint>(1);
EXPECT_EQ(events, event_types(event_type::read));
}
else if ( (i & 2)
&& fd == write_handle.handle())
{
i &= ~static_cast<uint>(2);
EXPECT_EQ(events, event_types(event_type::write));
}
},
&timeout);

EXPECT_EQ(0, i);
}

template<typename T_driver, typename... X_args>
void test_with_result(X_args&&... p_args)
{
int fds[2];
ASSERT_EQ(0, pipe(fds));

pipe_handle read_handle(fds[0]);
pipe_handle write_handle(fds[1]);

file_events<T_driver> fe(std::forward<X_args>(p_args)...);

fe.add(read_handle, event_types::all());
fe.add(write_handle, event_types::all());

auto timeout = event_clock::now();

auto ret = fe.wait(&timeout);

EXPECT_TRUE (ret.is_set());
EXPECT_FALSE(ret.is_set(event_type::read));
EXPECT_TRUE (ret.is_set(event_type::write));
EXPECT_FALSE(ret.is_set(event_type::except));
EXPECT_FALSE(ret.is_set(read_handle, event_type::read));
EXPECT_FALSE(ret.is_set(read_handle, event_type::write));
EXPECT_FALSE(ret.is_set(read_handle, event_type::except));
EXPECT_FALSE(ret.is_set(write_handle, event_type::read));
EXPECT_TRUE (ret.is_set(write_handle, event_type::write));
EXPECT_FALSE(ret.is_set(write_handle, event_type::except));

write(write_handle.handle(), "fuuu", 4);

ret = fe.wait(&timeout);

EXPECT_TRUE (ret.is_set());
EXPECT_TRUE (ret.is_set(event_type::read));
EXPECT_TRUE (ret.is_set(event_type::write));
EXPECT_FALSE(ret.is_set(event_type::except));
EXPECT_TRUE (ret.is_set(read_handle, event_type::read));
EXPECT_FALSE(ret.is_set(read_handle, event_type::write));
EXPECT_FALSE(ret.is_set(read_handle, event_type::except));
EXPECT_FALSE(ret.is_set(write_handle, event_type::read));
EXPECT_TRUE (ret.is_set(write_handle, event_type::write));
EXPECT_FALSE(ret.is_set(write_handle, event_type::except));
}

TEST(file_events_tests, select_with_callback)
{ test_with_callback<driver::select>(); }

TEST(file_events_tests, select_with_result)
{ test_with_result<driver::select>(); }

TEST(file_events_tests, poll_with_callback)
{ test_with_callback<driver::poll>(); }

TEST(file_events_tests, poll_with_result)
{ test_with_result<driver::poll>(); }

TEST(file_events_tests, epoll_with_callback)
{ test_with_callback<driver::epoll>(15); }

TEST(file_events_tests, epoll_with_result)
{ test_with_result<driver::epoll>(20 ); }

TEST(file_events_tests, vector_resize_does_not_change_capacity)
{
std::vector<int> v;
v.resize(1000);
v.resize(1);
EXPECT_EQ(1000, v.capacity());
}

正在加载...
取消
保存