@@ -3,5 +3,4 @@ | |||||
#include <cppfs/config.h> | #include <cppfs/config.h> | ||||
#include <cppfs/directory.h> | #include <cppfs/directory.h> | ||||
#include <cppfs/file.h> | #include <cppfs/file.h> | ||||
#include <cppfs/file_events.h> | |||||
#include <cppfs/path.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, | files, | ||||
std::vector<std::string>({ | std::vector<std::string>({ | ||||
"directory_tests.cpp", | "directory_tests.cpp", | ||||
"file_events_tests.cpp", | |||||
"file_tests.cpp", | "file_tests.cpp", | ||||
"path_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)); | |||||
} |