| @@ -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 | |||
| @@ -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. | |||
| @@ -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))) | |||
| @@ -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> | |||
| @@ -185,7 +185,7 @@ namespace cppcore | |||
| inline void clear(enum_type e); | |||
| /** | |||
| * @brief REset all flags. | |||
| * @brief Reset all flags. | |||
| */ | |||
| inline void reset(); | |||
| @@ -1,4 +1,3 @@ | |||
| #pragma once | |||
| #include "platform/fdset.h" | |||
| #include "platform/select.h" | |||
| #include "platform/file_events.h" | |||
| @@ -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); } | |||
| } | |||
| @@ -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 | |||
| @@ -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); | |||
| }; | |||
| } } | |||
| @@ -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; | |||
| } | |||
| } } | |||
| @@ -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" | |||
| } } | |||
| @@ -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); } | |||
| } } | |||
| @@ -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); | |||
| }; | |||
| } } | |||
| @@ -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; | |||
| } | |||
| } } | |||
| @@ -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; | |||
| }; | |||
| } } | |||
| @@ -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); | |||
| } | |||
| } | |||
| } } | |||
| @@ -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; | |||
| }; | |||
| } | |||
| @@ -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 +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 | |||
| @@ -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(); } | |||
| }; | |||
| } | |||
| @@ -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" | |||
| @@ -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); | |||
| } | |||
| } | |||
| @@ -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()); | |||
| } | |||