| @@ -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. | * @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 seperator Seperator to split string at. | ||||
| * @param predicate Predicate to execute for each element. | * @param predicate Predicate to execute for each element. | ||||
| * | * | ||||
| * @return Value returned from the predicate. | * @return Value returned from the predicate. | ||||
| */ | */ | ||||
| template<typename T_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. | * @brief Class to handle string conversions. | ||||
| @@ -44,6 +44,10 @@ namespace cppcore | |||||
| template<typename T_predicate> | template<typename T_predicate> | ||||
| inline bool string_split(const std::string& str, char seperator, T_predicate&& 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* i = str.c_str(); | ||||
| auto* e = str.c_str() + str.size(); | auto* e = str.c_str() + str.size(); | ||||
| @@ -51,8 +55,7 @@ namespace cppcore | |||||
| while (i <= e) | while (i <= e) | ||||
| { | { | ||||
| if ( s != e | if ( s != e | ||||
| && ( *i == seperator | |||||
| || *i == '\0')) | |||||
| && (*i == '\0' || splitter(*i))) | |||||
| { | { | ||||
| std::string tmp(s, convert_cast<size_t>(i - s)); | std::string tmp(s, convert_cast<size_t>(i - s)); | ||||
| if (!predicate(std::move(tmp))) | if (!predicate(std::move(tmp))) | ||||
| @@ -21,7 +21,6 @@ namespace cppcore | |||||
| tv.tv_usec = std::chrono::duration_cast<std::chrono::microseconds>(d - sec).count(); | tv.tv_usec = std::chrono::duration_cast<std::chrono::microseconds>(d - sec).count(); | ||||
| return tv; | return tv; | ||||
| } | } | ||||
| }; | }; | ||||
| template<typename Rep, typename Period> | template<typename Rep, typename Period> | ||||
| @@ -45,7 +44,6 @@ namespace cppcore | |||||
| ts.tv_nsec = std::chrono::duration_cast<std::chrono::nanoseconds>(d - sec).count(); | ts.tv_nsec = std::chrono::duration_cast<std::chrono::nanoseconds>(d - sec).count(); | ||||
| return ts; | return ts; | ||||
| } | } | ||||
| }; | }; | ||||
| template<typename Rep, typename Period> | template<typename Rep, typename Period> | ||||
| @@ -185,7 +185,7 @@ namespace cppcore | |||||
| inline void clear(enum_type e); | inline void clear(enum_type e); | ||||
| /** | /** | ||||
| * @brief REset all flags. | |||||
| * @brief Reset all flags. | |||||
| */ | */ | ||||
| inline void reset(); | inline void reset(); | ||||
| @@ -1,4 +1,3 @@ | |||||
| #pragma once | #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> | #include <sys/select.h> | ||||
| namespace cppcore | |||||
| { | |||||
| namespace cppcore { | |||||
| namespace driver { | |||||
| struct fdset | |||||
| struct fdset final | |||||
| : public fd_set | : public fd_set | ||||
| { | { | ||||
| public: | 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. | * @brief Check if the passed file descriptor is set in this set. | ||||
| * | * | ||||
| @@ -39,6 +54,4 @@ namespace cppcore | |||||
| inline void reset(); | 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()); | |||||
| } | |||||