| @@ -3,5 +3,4 @@ | |||
| #include <cppfs/config.h> | |||
| #include <cppfs/directory.h> | |||
| #include <cppfs/file.h> | |||
| #include <cppfs/file_events.h> | |||
| #include <cppfs/path.h> | |||
| @@ -1,15 +0,0 @@ | |||
| #pragma once | |||
| #include <cppfs/config.h> | |||
| #include "file_events/file_events.h" | |||
| #if cppfs_os == cppfs_os_linux | |||
| #include "file_events/select.h" | |||
| #endif | |||
| #include "file_events/file_events.inl" | |||
| #if cppfs_os == cppfs_os_linux | |||
| #include "file_events/select.inl" | |||
| #endif | |||
| @@ -1,59 +0,0 @@ | |||
| #pragma once | |||
| #include <sys/select.h> | |||
| namespace cppfs | |||
| { | |||
| 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. | |||
| * | |||
| * @param[in] fd File descriptor to check for. | |||
| * | |||
| * @retval true If the passed file descriptor is set. | |||
| * @retval false If the passed file descriptor is not set. | |||
| */ | |||
| inline bool is_set(int fd) const; | |||
| /** | |||
| * @brief Set the passed file descriptor in the set. | |||
| * | |||
| * @param[in] fd File descriptor to set. | |||
| */ | |||
| inline void set(int fd); | |||
| /** | |||
| * @brief Clear the passed file descriptor in the set. | |||
| * | |||
| * @param[in] fd File descriptor to clear. | |||
| */ | |||
| inline void clear(int fd); | |||
| /** | |||
| * @brief Clear the complete file descriptor set. | |||
| */ | |||
| inline void reset(); | |||
| }; | |||
| } | |||
| #include "fdset.inl" | |||
| @@ -1,63 +0,0 @@ | |||
| #pragma once | |||
| #include "fdset.h" | |||
| namespace cppfs | |||
| { | |||
| /* 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 cppcore::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 cppcore::exception("Size of fd set exceeded!"); | |||
| return FD_ISSET(fd, this); | |||
| } | |||
| void fdset::set(int fd) | |||
| { | |||
| if (fd >= FD_SETSIZE) | |||
| throw cppcore::exception("Size of fd set exceeded!"); | |||
| FD_SET(fd, this); | |||
| } | |||
| void fdset::clear(int fd) | |||
| { | |||
| if (fd >= FD_SETSIZE) | |||
| throw cppcore::exception("Size of fd set exceeded!"); | |||
| FD_CLR(fd, this); | |||
| } | |||
| void fdset::reset() | |||
| { FD_ZERO(this); } | |||
| } | |||
| @@ -1,96 +0,0 @@ | |||
| #pragma once | |||
| #include <cppfs/misc.h> | |||
| #include "types.h" | |||
| namespace cppfs | |||
| { | |||
| template<typename T_impl> | |||
| struct file_events | |||
| { | |||
| public: | |||
| using impl_type = T_impl; | |||
| using result_impl_type = typename impl_type::result_type; | |||
| struct result | |||
| { | |||
| private: | |||
| result_impl_type _impl; | |||
| public: | |||
| /** | |||
| * @brief Constructor. | |||
| */ | |||
| template<typename X_impl> | |||
| inline result(X_impl&& p_impl); | |||
| /** | |||
| * @brief Check if any event has occured in any handle. | |||
| */ | |||
| bool is_set() const; | |||
| /** | |||
| * @brief Check if any event has occured in the passed handle. | |||
| */ | |||
| bool is_set( | |||
| const raw_handle& p_handle) const; | |||
| /** | |||
| * @brief Check if any of the passed event has occured in any handle. | |||
| */ | |||
| bool is_set( | |||
| const event_types& p_events) const; | |||
| /** | |||
| * @brief Check if any of the passed event has occured in the passed handle. | |||
| */ | |||
| bool is_set( | |||
| const event_types& p_events, | |||
| const raw_handle& p_handle) const; | |||
| }; | |||
| private: | |||
| impl_type _impl; | |||
| public: | |||
| /** | |||
| * @brief Constructor. | |||
| */ | |||
| inline file_events(); | |||
| /** | |||
| * @brief Add or change a event listener for the passed file. | |||
| */ | |||
| inline void add( | |||
| const raw_handle& p_handle, | |||
| const event_types& p_events); | |||
| /** | |||
| * @brief Remove the event listener for the passed file. | |||
| */ | |||
| inline void erase( | |||
| const raw_handle& p_handle); | |||
| /** | |||
| * @brief Reset all event listeners. | |||
| */ | |||
| inline void reset(); | |||
| /** | |||
| * @brief Wait for file events and return the result. | |||
| */ | |||
| inline 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; | |||
| }; | |||
| } | |||
| @@ -1,89 +0,0 @@ | |||
| #pragma once | |||
| #include "file_events.h" | |||
| namespace cppfs | |||
| { | |||
| /* file_events::result */ | |||
| template<typename T_impl> | |||
| template<typename X_impl> | |||
| file_events<T_impl>::result::result(X_impl&& p_impl) | |||
| : _impl(std::forward<X_impl>(p_impl)) | |||
| { } | |||
| template<typename T_impl> | |||
| bool file_events<T_impl>::result::is_set() const | |||
| { | |||
| static const event_types all_types({ | |||
| event_type::read, | |||
| event_type::write, | |||
| event_type::except, | |||
| }); | |||
| return _impl.is_set(all_types, nullptr); | |||
| } | |||
| template<typename T_impl> | |||
| bool file_events<T_impl>::result::is_set( | |||
| const raw_handle& p_handle) const | |||
| { | |||
| static const event_types all_types({ | |||
| event_type::read, | |||
| event_type::write, | |||
| event_type::except, | |||
| }); | |||
| return _impl.is_set(all_types, &p_handle); | |||
| } | |||
| template<typename T_impl> | |||
| bool file_events<T_impl>::result::is_set( | |||
| const event_types& p_events) const | |||
| { | |||
| return _impl.is_set(p_events, nullptr); | |||
| } | |||
| template<typename T_impl> | |||
| bool file_events<T_impl>::result::is_set( | |||
| const event_types& p_events, | |||
| const raw_handle& p_handle) const | |||
| { | |||
| return _impl.is_set(p_events, &p_handle); | |||
| } | |||
| /* file_events */ | |||
| template<typename T_impl> | |||
| file_events<T_impl>::file_events() | |||
| : _impl() | |||
| { } | |||
| template<typename T_impl> | |||
| void file_events<T_impl>::add( | |||
| const raw_handle& p_handle, | |||
| const event_types& p_events) | |||
| { _impl.add(p_handle, p_events); } | |||
| template<typename T_impl> | |||
| void file_events<T_impl>::erase( | |||
| const raw_handle& p_handle) | |||
| { _impl.erase(p_handle); } | |||
| template<typename T_impl> | |||
| void file_events<T_impl>::reset() | |||
| { _impl = impl_type(); } | |||
| template<typename T_impl> | |||
| typename file_events<T_impl>::result | |||
| file_events<T_impl>::wait( | |||
| const event_timeout * p_timeout) const | |||
| { return result(_impl.wait(p_timeout)); } | |||
| template<typename T_impl> | |||
| template<typename T_lambda> | |||
| void file_events<T_impl>::wait( | |||
| T_lambda&& p_lambda, | |||
| const event_timeout * p_timeout) const | |||
| { _impl.wait(std::forward<T_lambda>(p_lambda), p_timeout); } | |||
| } | |||
| @@ -1,68 +0,0 @@ | |||
| #pragma once | |||
| #include <sys/select.h> | |||
| #include <cppfs/file/file.h> | |||
| #include "types.h" | |||
| #include "fdset.h" | |||
| namespace cppfs | |||
| { | |||
| struct fe_select | |||
| { | |||
| public: | |||
| struct storage | |||
| { | |||
| int max_fd { -1 }; | |||
| fdset read; | |||
| fdset write; | |||
| fdset except; | |||
| inline bool is_set( | |||
| const event_types& p_events, | |||
| const raw_handle* p_handle) const; | |||
| }; | |||
| using result_type = storage; | |||
| private: | |||
| storage _storage; | |||
| public: | |||
| /** | |||
| * @brief Constructor. | |||
| */ | |||
| inline fe_select() = default; | |||
| public: /* file_events implementation */ | |||
| /** | |||
| * @brief Add or change a event listener for the passed file. | |||
| */ | |||
| inline void add( | |||
| const raw_handle& p_handle, | |||
| const event_types& p_events); | |||
| /** | |||
| * @brief Remove the event listener for the passed handle. | |||
| */ | |||
| inline void erase( | |||
| const raw_handle& p_handle); | |||
| /** | |||
| * @brief Wait for file events and return the result. | |||
| */ | |||
| inline result_type 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; | |||
| }; | |||
| } | |||
| @@ -1,114 +0,0 @@ | |||
| #pragma once | |||
| #include <cppcore/conversion/time.h> | |||
| #include "select.h" | |||
| namespace cppfs | |||
| { | |||
| /* fe_select::storage */ | |||
| bool fe_select::storage::is_set( | |||
| const event_types& p_events, | |||
| const raw_handle* p_handle) const | |||
| { | |||
| if (p_handle) | |||
| { | |||
| auto fd = p_handle->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)); | |||
| } | |||
| else | |||
| { | |||
| 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)); | |||
| } | |||
| } | |||
| /* fe_select */ | |||
| void fe_select::add( | |||
| const raw_handle& p_handle, | |||
| const event_types& p_events) | |||
| { | |||
| int fd = p_handle.handle(); | |||
| if (fd < 0) | |||
| throw cppcore::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 cppcore::exception("No or unkown events passed!"); | |||
| _storage.max_fd = std::max(fd, _storage.max_fd); | |||
| } | |||
| void fe_select::erase(const raw_handle& p_handle) | |||
| { | |||
| int fd = p_handle.handle(); | |||
| if (fd < 0) | |||
| throw cppcore::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; | |||
| } | |||
| } | |||
| fe_select::result_type fe_select::wait(const event_timeout * p_timeout) const | |||
| { | |||
| auto ret = _storage; | |||
| 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); | |||
| } | |||
| } | |||
| if (::pselect(ret.max_fd + 1, &ret.read, &ret.write, &ret.except, p_timeout ? &timeout : nullptr, nullptr) < 0) | |||
| { | |||
| auto err = errno; | |||
| if (err != EINTR) | |||
| throw cppcore::error_exception("Error while fetching file events with 'select'", err); | |||
| } | |||
| return ret; | |||
| } | |||
| template<typename T_lambda> | |||
| void fe_select::wait(T_lambda&& p_lambda, const event_timeout * p_timeout) const | |||
| { | |||
| auto ret = wait(p_timeout); | |||
| for (auto fd = 0; fd <= ret.max_fd; ++fd) | |||
| { | |||
| event_types ev; | |||
| if (ret.read.is_set(fd)) ev.set(event_type::read); | |||
| if (ret.write.is_set(fd)) ev.set(event_type::write); | |||
| if (ret.except.is_set(fd)) ev.set(event_type::except); | |||
| if (ev) | |||
| p_lambda(fd, ev); | |||
| } | |||
| } | |||
| } | |||
| @@ -1,23 +0,0 @@ | |||
| #pragma once | |||
| #include <chrono> | |||
| #include <cppcore/misc/flags.h> | |||
| namespace cppfs | |||
| { | |||
| 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; | |||
| } | |||
| @@ -31,7 +31,6 @@ TEST(directory_tests, iterator) | |||
| files, | |||
| std::vector<std::string>({ | |||
| "directory_tests.cpp", | |||
| "file_events_tests.cpp", | |||
| "file_tests.cpp", | |||
| "path_tests.cpp", | |||
| })); | |||
| @@ -1,108 +0,0 @@ | |||
| #include <gtest/gtest.h> | |||
| #include <fstream> | |||
| #include <cppfs.h> | |||
| using namespace ::testing; | |||
| using namespace ::cppfs; | |||
| struct pipe_handle | |||
| : public raw_handle | |||
| { | |||
| pipe_handle(int p_handle) | |||
| { | |||
| _handle = p_handle; | |||
| } | |||
| ~pipe_handle() | |||
| { | |||
| close(_handle); | |||
| } | |||
| }; | |||
| TEST(file_events_tests, select_with_callback) | |||
| { | |||
| int fds[2]; | |||
| ASSERT_EQ(0, pipe(fds)); | |||
| pipe_handle read_handle(fds[0]); | |||
| pipe_handle write_handle(fds[1]); | |||
| file_events<fe_select> fe; | |||
| 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); | |||
| auto i = 0; | |||
| fe.wait( | |||
| [&](auto fd, auto events) { | |||
| switch (i) | |||
| { | |||
| case 0: | |||
| EXPECT_EQ(fd, read_handle.handle()); | |||
| EXPECT_EQ(events, event_types(event_type::read)); | |||
| break; | |||
| case 1: | |||
| EXPECT_EQ(fd, write_handle.handle()); | |||
| EXPECT_EQ(events, event_types(event_type::write)); | |||
| break; | |||
| } | |||
| ++i; | |||
| }, | |||
| &timeout); | |||
| } | |||
| TEST(file_events_tests, select_with_result) | |||
| { | |||
| int fds[2]; | |||
| ASSERT_EQ(0, pipe(fds)); | |||
| pipe_handle read_handle(fds[0]); | |||
| pipe_handle write_handle(fds[1]); | |||
| file_events<fe_select> fe; | |||
| 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(event_type::read, read_handle)); | |||
| EXPECT_FALSE(ret.is_set(event_type::write, read_handle)); | |||
| EXPECT_FALSE(ret.is_set(event_type::except, read_handle)); | |||
| EXPECT_FALSE(ret.is_set(event_type::read, write_handle)); | |||
| EXPECT_TRUE (ret.is_set(event_type::write, write_handle)); | |||
| EXPECT_FALSE(ret.is_set(event_type::except, write_handle)); | |||
| 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(event_type::read, read_handle)); | |||
| EXPECT_FALSE(ret.is_set(event_type::write, read_handle)); | |||
| EXPECT_FALSE(ret.is_set(event_type::except, read_handle)); | |||
| EXPECT_FALSE(ret.is_set(event_type::read, write_handle)); | |||
| EXPECT_TRUE (ret.is_set(event_type::write, write_handle)); | |||
| EXPECT_FALSE(ret.is_set(event_type::except, write_handle)); | |||
| } | |||