/* async_file_io Provides a threadpool and asynchronous file i/o infrastructure based on Boost.ASIO, Boost.Iostreams and filesystem (C) 2013-2015 Niall Douglas http://www.nedprod.com/ File Created: Mar 2013 Boost Software License - Version 1.0 - August 17th, 2003 Permission is hereby granted, free of charge, to any person or organization obtaining a copy of the software and accompanying documentation covered by this license (the "Software") to use, reproduce, display, distribute, execute, and transmit the Software, and to prepare derivative works of the Software, and to permit third-parties to whom the Software is furnished to do so, all subject to the following: The copyright notices in the Software and this entire statement, including the above license grant, this restriction and the following disclaimer, must be included in all copies of the Software, in whole or in part, and all derivative works of the Software, unless such copies or derivative works are solely in the form of machine-executable object code generated by a source language processor. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef BOOST_AFIO_HEADER_INCLUDED #ifdef DOXYGEN_SHOULD_SKIP_THIS #define BOOST_AFIO_HEADERS_ONLY 0 #define BOOST_AFIO_USE_BOOST_THREAD 0 #define BOOST_AFIO_USE_BOOST_FILESYSTEM 1 #define ASIO_STANDALONE 0 #endif #include "config.hpp" // clang-format off #ifdef DOXYGEN_SHOULD_SKIP_THIS #undef BOOST_AFIO_V2_NAMESPACE #undef BOOST_AFIO_V2_NAMESPACE_BEGIN #undef BOOST_AFIO_V2_NAMESPACE_END #undef BOOST_AFIO_HEADERS_ONLY_MEMFUNC_SPEC #undef BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC #define BOOST_AFIO_V2_NAMESPACE boost::afio #define BOOST_AFIO_V2_NAMESPACE_BEGIN namespace boost { namespace afio { #define BOOST_AFIO_V2_NAMESPACE_END } } #define BOOST_AFIO_HEADERS_ONLY_MEMFUNC_SPEC #define BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC virtual #endif // clang-format on #ifndef BOOST_AFIO_AFIO_H #define BOOST_AFIO_AFIO_H #include "detail/Undoer.hpp" #include "detail/ErrorHandling.hpp" #include "detail/Utility.hpp" #include // Boost.ASIO needs std::min and std::max #include #include #include /*! \brief Validate inputs at the point of instantiation. Turns on the checking of inputs for validity and throwing of exception conditions at the point of instantiation rather than waiting until those inputs are sent for dispatch. This, being very useful for debugging, defaults to 1 except when `NDEBUG` is defined i.e. final release builds. \ingroup macros */ #ifndef BOOST_AFIO_VALIDATE_INPUTS #ifndef NDEBUG #define BOOST_AFIO_VALIDATE_INPUTS 1 #else #define BOOST_AFIO_VALIDATE_INPUTS 0 #endif #endif #ifdef BOOST_MSVC #pragma warning(push) #pragma warning(disable: 4251) // type needs to have dll-interface to be used by clients of class #endif /*! \file afio.hpp \brief Provides a batch asynchronous file i/o implementation based on Boost.ASIO */ /*! \def BOOST_AFIO_HEADERS_ONLY \brief Determines if AFIO is compiled as headers only. Defaults to 1. */ /*! \def BOOST_AFIO_USE_BOOST_THREAD \brief Determines if AFIO is bound against Boost.Thread or the C++ 11 STL thread. Defaults to 0. */ /*! \def BOOST_AFIO_USE_BOOST_FILESYSTEM \brief Determines if AFIO is bound against Boost.Filesystem or the C++ 1z Filesystem TS. Defaults to 1 unless on VS2015 which provides a full Filesystem TS implementation. */ /*! \def ASIO_STANDALONE \brief Determines if AFIO is bound against standalone ASIO or Boost.ASIO. Defaults to undefined, and therefore Boost.ASIO. */ BOOST_AFIO_V2_NAMESPACE_BEGIN // This isn't consistent on MSVC so hard code it typedef unsigned long long off_t; //! \brief The namespace containing Boost.ASIO internal details namespace detail { template class enqueued_task_impl { protected: struct Private { std::function task; promise r; shared_future f; bool autoset; atomic done; Private(std::function _task) : task(std::move(_task)), f(r.get_future().share()), autoset(true), done(0) { } }; std::shared_ptr p; void validate() const { assert(p); /*if(!p) abort();*/ } public: //! Default constructor enqueued_task_impl(std::function _task=std::function()) : p(std::make_shared(std::move(_task))) { } //! Returns true if valid bool valid() const noexcept{ return p.get()!=nullptr; } //! Swaps contents with another instance void swap(enqueued_task_impl &o) noexcept{ p.swap(o.p); } //! Resets the contents void reset() { p.reset(); } //! Sets the task void set_task(std::function _task) { p->task=std::move(_task); } //! Returns the shared stl_future corresponding to the stl_future return value of the task const shared_future &get_future() const { validate(); return p->f; } //! Sets the shared stl_future corresponding to the stl_future return value of the task. template void set_future_value(T v) { int _=0; validate(); if(!p->done.compare_exchange_strong(_, 1)) return; p->r.set_value(std::move(v)); } void set_future_value() { int _=0; validate(); if(!p->done.compare_exchange_strong(_, 1)) return; p->r.set_value(); } //! Sets the shared stl_future corresponding to the stl_future return value of the task. void set_future_exception(exception_ptr e) { int _=0; validate(); if(!p->done.compare_exchange_strong(_, 1)) return; p->r.set_exception(e); } //! Disables the task setting the shared stl_future return value. void disable_auto_set_future(bool v=true) { validate(); p->autoset=!v; } }; } template class enqueued_task; /*! \class enqueued_task \tparam "class R" The return type of the callable which must be without parameters. \brief Effectively our own custom std::packaged_task<>, with copy semantics and letting us early set value to significantly improve performance Unlike `std::packaged_task<>`, this custom variant is copyable though each copy always refers to the same internal state. Early stl_future value setting is possible, with any subsequent value setting including that by the function being executed being ignored. Note that this behaviour opens the potential to lose exception state - if you set the stl_future value early and then an exception is later thrown, the exception is swallowed. */ // Can't have args in callable type as that segfaults VS2010 template class enqueued_task : public detail::enqueued_task_impl { typedef detail::enqueued_task_impl Base; public: //! Default constructor enqueued_task(std::function _task=std::function()) : Base(std::move(_task)) { } //! Invokes the callable, setting the shared stl_future to the value it returns void operator()() { auto _p(Base::p); Base::validate(); if(!_p->task) abort(); try { auto v(_p->task()); if(_p->autoset && !_p->done) Base::set_future_value(v); } catch(...) { if(_p->done) { BOOST_AFIO_LOG_FATAL_EXIT(detail::output_exception_info << " thrown up to enqueued_task<> after stl_future set." << std::endl); BOOST_AFIO_THROW_FATAL(std::runtime_error("Exception thrown up to enqueued_task<> after stl_future set.")); } if(_p->autoset && !_p->done) { auto e(current_exception()); Base::set_future_exception(e); } } // Free any bound parameters in task to save memory _p->task=std::function(); } }; template<> class enqueued_task : public detail::enqueued_task_impl { typedef detail::enqueued_task_impl Base; public: //! Default constructor enqueued_task(std::function _task=std::function()) : Base(std::move(_task)) { } //! Invokes the callable, setting the stl_future to the value it returns void operator()() { auto _p(Base::p); Base::validate(); if(!_p->task) abort(); try { _p->task(); if(_p->autoset && !_p->done) Base::set_future_value(); } catch(...) { if(_p->done) { BOOST_AFIO_LOG_FATAL_EXIT(detail::output_exception_info << " thrown up to enqueued_task<> after stl_future set." << std::endl); BOOST_AFIO_THROW_FATAL(std::runtime_error("Exception thrown up to enqueued_task<> after stl_future set.")); } if(_p->autoset && !_p->done) { auto e(current_exception()); Base::set_future_exception(e); } } // Free any bound parameters in task to save memory _p->task=std::function(); } }; /*! \class thread_source \brief Abstract base class for a source of thread workers Note that in Boost 1.54, and possibly later versions, `asio::io_service` on Windows appears to dislike being destructed during static data deinit, hence why this inherits from `std::enable_shared_from_this<>` in order that it may be reference count deleted before static data deinit occurs. */ class thread_source : public std::enable_shared_from_this { protected: asio::io_service &service; thread_source(asio::io_service &_service) : service(_service) { } BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC ~thread_source() { } thread_source &operator=(const thread_source &) = delete; public: //! Returns the underlying io_service asio::io_service &io_service() { return service; } //! Sends a task to the thread pool for execution \tparam "class R" The return type of the enqueued task template void enqueue(enqueued_task task) { service.post(task); } //! Sends some callable entity to the thread pool for execution \return An enqueued task for the enqueued callable \tparam "class F" Any callable type with signature R(void) \param f Any instance of a callable type template shared_future::type> enqueue(F f) { typedef typename std::result_of::type R; enqueued_task out(std::move(f)); auto ret(out.get_future()); service.post(out); return ret; } }; /*! \class std_thread_pool \brief A very simple thread pool based on std::thread or boost::thread This instantiates a `asio::io_service` and a latchable `asio::io_service::work` to keep any threads working until the instance is destructed. */ class std_thread_pool : public thread_source { class worker { std_thread_pool *pool; public: explicit worker(std_thread_pool *p) : pool(p) { } void operator()() { detail::set_threadname("boost::afio::std_thread_pool worker"); try { pool->service.run(); } catch(...) { BOOST_AFIO_LOG_FATAL_EXIT("WARNING: ASIO exits via " << detail::output_exception_info << " which shouldn't happen." << std::endl); } } }; friend class worker; asio::io_service service; std::unique_ptr working; std::vector< std::unique_ptr > workers; public: /*! \brief Constructs a thread pool of \em no workers \param no The number of worker threads to create */ explicit std_thread_pool(size_t no) : thread_source(service), working(detail::make_unique(service)) { add_workers(no); } //! Adds more workers to the thread pool \param no The number of worker threads to add void add_workers(size_t no) { workers.reserve(workers.size()+no); for(size_t n=0; n(worker(this))); } //! Destroys the thread pool, waiting for worker threads to exit beforehand. void destroy() { if(!service.stopped()) { // Tell the threads there is no more work to do working.reset(); for(auto &i: workers) { i->join(); } workers.clear(); // For some reason ASIO occasionally thinks there is still more work to do if(!service.stopped()) service.run(); service.stop(); service.reset(); } } ~std_thread_pool() final { destroy(); } }; /*! \brief Returns the process threadpool On first use, this instantiates a default std_thread_pool running `BOOST_AFIO_MAX_NON_ASYNC_QUEUE_DEPTH` threads which will remain until its shared count reaches zero. \ingroup process_threadpool */ BOOST_AFIO_HEADERS_ONLY_FUNC_SPEC std::shared_ptr process_threadpool(); class dispatcher; using dispatcher_ptr = std::shared_ptr; template class future; struct path_req; template struct io_req; struct enumerate_req; struct lock_req; namespace detail { struct async_io_handle_posix; struct async_io_handle_windows; struct dispatcher_p; class async_file_io_dispatcher_compat; class async_file_io_dispatcher_windows; class async_file_io_dispatcher_linux; class async_file_io_dispatcher_qnx; struct immediate_async_ops; template class io_req_impl; } //! \brief The types of path normalisation available enum class path_normalise { dos, //!< Return the shortest normalised path possible (usually a drive letter prefix). This is a traditional DOS style path. guid_volume, //!< Return the volume as a GUID. This eliminates problems with drive letters vanishing or being ambiguous. Anything accepting a Win32 path can accept one of these. guid_all //!< Return the whole path as a GUID. This eliminates problems with long paths or if the file is renamed. Note this may cause the creation of a GUID for the file. Anything accepting a Win32 path can accept one of these. }; /*! \class path \brief An AFIO filesystem path, a thin wrapper of filesystem::path used to mark when a filesystem path has been prepared for AFIO usage. Note that on Windows this exclusively refers to a case sensitive NT kernel path, not a Win32 path (Win32 paths are converted in the constructor). \qbk{ [include generated/struct_path_1_1make_absolute.qbk] [include generated/group_normalise_path.qbk] [include generated/struct_path_hash.qbk] } */ class path : protected filesystem::path { void int_regularise() { #ifdef _MSC_VER #pragma warning(push) #pragma warning(disable: 6326) // comparison of constants #endif if(preferred_separator!='/') make_preferred(); #ifdef _MSC_VER #pragma warning(pop) #endif #ifdef WIN32 // Need to strip off any win32 prefixing, and instead prefix any drive letters bool isExtendedPath=false, isDevicePath=false; if(native().size()>=4) { #ifndef NDEBUG if(native()[0]=='\\' && native()[1]=='?' && native()[2]=='?' && native()[3]=='\\') { assert(!(native()[0]=='\\' && native()[1]=='?' && native()[2]=='?' && native()[3]=='\\')); } #endif isExtendedPath=(native()[0]=='\\' && native()[1]=='\\' && native()[2]=='?' && native()[3]=='\\'); isDevicePath=(native()[0]=='\\' && native()[1]=='\\' && native()[2]=='.' && native()[3]=='\\'); } bool hasDriveLetter=(isalpha(native()[((int) isExtendedPath+(int) isDevicePath)*4+0]) && native()[((int) isExtendedPath+(int) isDevicePath)*4+1]==':'); if(hasDriveLetter && (isExtendedPath || isDevicePath)) { filesystem::path::string_type &me=const_cast(native()); me[1]=me[2]='?'; } else if(hasDriveLetter) { filesystem::path::string_type &me=const_cast(native()); me=L"\\??\\"+me; } else if(isExtendedPath || isDevicePath) { filesystem::path::string_type &me=const_cast(native()); me=me.substr(isDevicePath ? 3 : 4); } #endif } friend struct detail::async_io_handle_windows; struct direct { }; path(filesystem::path &&p, direct) : filesystem::path(std::move(p)) { } public: typedef filesystem::path::value_type value_type; typedef filesystem::path::string_type string_type; using filesystem::path::preferred_separator; //! Makes a path absolute struct make_absolute; //! \constr path() {} //! \cconstr path(const path &p) : filesystem::path(p) { } //! Converts a filesystem::path to AFIO format path(const filesystem::path &p) : filesystem::path(p) { int_regularise(); } //! Converts a filesystem::path to AFIO format path(const char *p) : filesystem::path(p) { int_regularise(); } #ifdef WIN32 //! Converts a filesystem::path to AFIO format path(const wchar_t *p) : filesystem::path(p) { int_regularise(); } //! Converts a filesystem::path to AFIO format path(const std::string &p) : filesystem::path(p) { int_regularise(); } #endif //! Converts a filesystem::path to AFIO format path(const string_type &p) : filesystem::path(p) { int_regularise(); } //! \mconstr path(path &&p) noexcept : filesystem::path(std::move(p)) { } //! Converts a filesystem::path to AFIO format path(filesystem::path &&p) : filesystem::path(std::move(p)) { int_regularise(); } #ifdef WIN32 //! Converts a filesystem::path to AFIO format path(std::string &&p) : filesystem::path(std::move(p)) { int_regularise(); } #endif //! Converts a filesystem::path to AFIO format path(string_type &&p) : filesystem::path(std::move(p)) { int_regularise(); } //! Converts source to AFIO path format //template path(const Source &source) : filesystem::path(source) { int_regularise(); } //! Converts source to AFIO path format template path(InputIterator begin, InputIterator end) : filesystem::path(begin, end) { int_regularise(); } //! \cassign path& operator=(const path& p) { filesystem::path::operator=(filesystem::path(p)); return *this; } //! \massign path& operator=(path&& p) noexcept { filesystem::path::operator=(static_cast(p)); return *this; } //! Converts source to AFIO path format //template path& operator=(Source const& source) { filesystem::path::operator=(source); int_regularise(); return *this; } template path& assign(Source const& source) { filesystem::path::assign(source); return *this; } template path& assign(InputIterator begin, InputIterator end) { filesystem::path::assign(begin, end); return *this; } path& operator/=(const path& p) { filesystem::path::operator/=(filesystem::path(p)); return *this; } template path& operator/=(Source const& source) { filesystem::path::operator/=(source); return *this; } template path& append(Source const& source) { filesystem::path::append(source); return *this; } template path& append(InputIterator begin, InputIterator end) { filesystem::path::append(begin, end); return *this; } path& operator+=(const path& x) { filesystem::path::operator+=(filesystem::path(x)); return *this; } path& operator+=(const string_type& x) { filesystem::path::operator+=(x); return *this; } path& operator+=(const value_type* x) { filesystem::path::operator+=(x); return *this; } path& operator+=(value_type x) { filesystem::path::operator+=(x); return *this; } template path& operator+=(Source const& x) { filesystem::path::operator+=(x); return *this; } template path& concat(Source const& x) { filesystem::path::concat(x); return *this; } template path& concat(InputIterator begin, InputIterator end) { filesystem::path::concat(begin, end); return *this; } using filesystem::path::clear; path& make_preferred() { filesystem::path::make_preferred(); return *this; } path& remove_filename() { filesystem::path::remove_filename(); return *this; } path& replace_extension(const path& new_extension = path()) { filesystem::path::replace_extension(filesystem::path(new_extension)); return *this; } using filesystem::path::swap; using filesystem::path::native; using filesystem::path::c_str; using filesystem::path::string; using filesystem::path::wstring; using filesystem::path::generic_string; using filesystem::path::compare; path root_name() const { return path(filesystem::path::root_name(), direct()); } path root_directory() const { return path(filesystem::path::root_directory(), direct()); } path root_path() const { return path(filesystem::path::root_path(), direct()); } path relative_path() const { return path(filesystem::path::relative_path(), direct()); } path parent_path() const { return path(filesystem::path::parent_path(), direct()); } #ifdef BOOST_AFIO_USE_LEGACY_FILESYSTEM_SEMANTICS path filename() const { return path(filesystem::path::leaf(), direct()); } #else path filename() const { return path(filesystem::path::filename(), direct()); } #endif path stem() const { return path(filesystem::path::stem(), direct()); } path extension() const { return path(filesystem::path::extension(), direct()); } using filesystem::path::empty; using filesystem::path::has_root_name; using filesystem::path::has_root_directory; using filesystem::path::has_root_path; using filesystem::path::has_relative_path; using filesystem::path::has_parent_path; using filesystem::path::has_filename; using filesystem::path::has_stem; using filesystem::path::has_extension; using filesystem::path::is_absolute; using filesystem::path::is_relative; // TODO FIXME: Need our own iterator here typedef filesystem::path::iterator iterator; typedef filesystem::path::const_iterator const_iterator; iterator begin() const { return filesystem::path::begin(); } iterator end() const { return filesystem::path::end(); } /*! \brief Return a normalised filesystem::path from an AFIO path. On POSIX this passes through its input unchanged. On Windows AFIO exclusively uses NT kernel paths which are not necessarily trivially convertible to Win32 paths. As an example, the Win32 path `C:\Foo` might be `\??\C:\Foo` or even `\Device\HarddiskVolume1\Foo`. This function will convert any NT kernel path into something which can be fed to normal Win32 APIs quickly, though note that the output path will be rejected by most other APIs as invalid. If you need a Win32 path which is completely valid, use normalise_path(). */ filesystem::path filesystem_path() const { #ifdef WIN32 bool isSymlinkedDosPath=(native()[0]=='\\' && native()[1]=='?' && native()[2]=='?' && native()[3]=='\\'); if(isSymlinkedDosPath) { filesystem::path::string_type p(native()); p[1]='\\'; return p; } else return filesystem::path(L"\\\\.")/filesystem::path(*this); #else return *this; #endif } friend inline bool operator<(const path& lhs, const path& rhs); friend inline bool operator<=(const path& lhs, const path& rhs); friend inline bool operator>(const path& lhs, const path& rhs); friend inline bool operator>=(const path& lhs, const path& rhs); friend inline bool operator==(const path& lhs, const path& rhs); friend inline bool operator!=(const path& lhs, const path& rhs); friend inline path operator/(const path& lhs, const path& rhs); friend inline std::ostream &operator<<(std::ostream &s, const path &p); friend struct path_hash; #ifdef WIN32 #ifdef _MSC_VER friend BOOST_AFIO_HEADERS_ONLY_FUNC_SPEC filesystem::path normalise_path(path p, path_normalise type); #else friend filesystem::path normalise_path(path p, path_normalise type); #endif #else friend inline filesystem::path normalise_path(path p, path_normalise type); #endif }; inline bool operator<(const path& lhs, const path& rhs) { return filesystem::path(lhs)(const path& lhs, const path& rhs) { return filesystem::path(lhs)>filesystem::path(rhs); } inline bool operator>=(const path& lhs, const path& rhs) { return filesystem::path(lhs)>=filesystem::path(rhs); } inline bool operator==(const path& lhs, const path& rhs) { return filesystem::path(lhs)==filesystem::path(rhs); } inline bool operator!=(const path& lhs, const path& rhs) { return filesystem::path(lhs)!=filesystem::path(rhs); } inline path operator/(const path& lhs, const path& rhs) { return path(filesystem::path(lhs)/filesystem::path(rhs), path::direct()); } inline std::ostream &operator<<(std::ostream &s, const path &p) { return s << filesystem::path(p); } //! Makes a path absolute according to the current working directory struct path::make_absolute : public path { make_absolute(const path &p) : path(p) { if(native()[0]!=preferred_separator) *this=filesystem::absolute(std::move(*this)); } make_absolute(path &&p) : path(std::move(p)) { if(native()[0]!=preferred_separator) *this=filesystem::absolute(std::move(*this)); } template::value>::type> make_absolute(T &&p) : path(filesystem::absolute(std::forward(p))) { } }; /*! \brief A hasher for path */ struct path_hash { std::hash hasher; public: size_t operator()(const path &p) const { return hasher(p.native()); } }; /*! \brief Return a normalised filesystem::path from an AFIO path. On POSIX this passes through its input unchanged. On Windows AFIO exclusively uses NT kernel paths which are not necessarily trivially convertible to Win32 paths. As an example, the Win32 path `C:\\Foo` might be `\\??\\C:\\Foo` or even `\\Device\\HarddiskVolume1\\Foo`. This function will convert any NT kernel path into something which can be fed to normal Win32 APIs - a drive letter if available, else a GUID volume path, and with an extended path prefix if the path is sufficiently long. It also scans the path for characters illegal under Win32 or paths which begin with a space or end with a period, and will extended path prefix such paths as well. \ingroup normalise_path \param p Path to be normalised \param type A path_normalise enum */ #ifdef WIN32 BOOST_AFIO_HEADERS_ONLY_FUNC_SPEC filesystem::path normalise_path(path p, path_normalise type=path_normalise::dos); #else inline filesystem::path normalise_path(path p, path_normalise type=path_normalise::dos) { return p; } #endif #define BOOST_AFIO_DECLARE_CLASS_ENUM_AS_BITFIELD(type) \ inline constexpr type operator&(type a, type b) \ { \ return static_cast(static_cast(a) & static_cast(b)); \ } \ inline constexpr type operator|(type a, type b) \ { \ return static_cast(static_cast(a) | static_cast(b)); \ } \ inline constexpr type operator~(type a) \ { \ return static_cast(~static_cast(a)); \ } \ inline constexpr bool operator!(type a) \ { \ return 0==static_cast(a); \ } /*! \enum file_flags \brief Bitwise file and directory open flags \ingroup file_flags */ enum class file_flags : size_t { none=0, //!< No flags set read=1, //!< Read access write=2, //!< Write access read_write=3, //!< Read and write access append=4, //!< Append only truncate=8, //!< Truncate existing file to zero create=16, //!< Open and create if doesn't exist. Always creates sparse files if possible. create_only_if_not_exist=32, //!< Create and open only if doesn't exist create_compressed=64, //!< Create a compressed file, needs to be combined with one of the other create flags. Only succeeds if supported by the underlying filing system. will_be_sequentially_accessed=128, //!< Will be \em exclusively either read or written sequentially. If you're exclusively writing sequentially, \em strongly consider turning on `os_direct` too. will_be_randomly_accessed=256, //!< Will be randomly accessed, so don't bother with read-ahead. If you're using this, \em strongly consider turning on `os_direct` too. no_sparse=512, //!< Don't create sparse files. May be ignored by some filing systems (e.g. ext4). hold_parent_open=(1<<10), //!< Hold a file handle open to the containing directory of each open file for fast directory enumeration and fast relative path ops. unique_directory_handle=(1<<11), //!< Return a unique directory handle rather than a shared directory handle no_race_protection=(1<<12), //!< Skip taking steps to avoid destruction of data due to filing system races. Most of the performance benefit of enabling this goes away if you enable HoldParentOpen instead, so be especially careful when considering turning this on. temporary_file=(1<<13), //!< On some systems causes dirty cache data to not be written to physical storage until file close. Useful for temporary files and lock files, especially on Windows when combined with `delete_on_close` as this avoids an fsync of the containing directory on file close. delete_on_close=(1<<14), //!< Only when combined with `create_only_if_not_exist`, deletes the file on close. This is especially useful on Windows with temporary and lock files where normally closing a file is an implicit fsync of its containing directory. Note on POSIX this unlinks the file on first close by AFIO, whereas on Windows the operating system unlinks the file on last close including sudden application exit. Note also that AFIO permits you to delete files which are currently open on Windows and the file entry disappears immediately just as on POSIX. os_direct=(1<<16), //!< Bypass the OS file buffers (only really useful for writing large files, or a lot of random reads and writes. Note you must 4Kb align everything if this is on). Be VERY careful mixing this with memory mapped files. os_lockable=(1<<17), // Deliberately undocumented always_sync=(1<<24), //!< Ask the OS to not complete until the data is on the physical storage. Some filing systems do much better with this than `sync_on_close`. sync_on_close=(1<<25), //!< Automatically initiate an asynchronous flush just before file close, and fuse both operations so both must complete for close to complete. int_hold_parent_open_nested=(1<<27), //!< Internal use only. Don't use. int_file_share_delete=(1<<28), //!< Internal use only. Don't use. int_opening_link=(1<<29), //!< Internal use only. Don't use. int_opening_dir=(1<<30) //!< Internal use only. Don't use. }; BOOST_AFIO_DECLARE_CLASS_ENUM_AS_BITFIELD(file_flags) /*! \enum async_op_flags \brief Bitwise async_op_flags flags \ingroup async_op_flags */ enum class async_op_flags : size_t { none=0, //!< No flags set immediate=1 //!< Call chained completion immediately instead of scheduling for later. Make SURE your completion can not block! }; BOOST_AFIO_DECLARE_CLASS_ENUM_AS_BITFIELD(async_op_flags) namespace detail { /*! \enum OpType \brief The type of operation */ enum class OpType { Unknown, UserCompletion, dir, rmdir, file, rmfile, symlink, rmsymlink, sync, close, read, write, truncate, barrier, enumerate, adopt, zero, extents, statfs, lock, Last }; static const char *optypes[]={ "unknown", "UserCompletion", "dir", "rmdir", "file", "rmfile", "symlink", "rmsymlink", "sync", "close", "read", "write", "truncate", "barrier", "enumerate", "adopt", "zero", "extents", "statfs", "lock" }; static_assert(static_cast(OpType::Last)==sizeof(optypes)/sizeof(*optypes), "You forgot to fix up the strings matching OpType"); enum class unit_testing_flags : size_t { none=0, //!< No flags set no_symbol_lookup=(1<<0) //!< Don't bother looking up symbols in stack backtracing as it's horribly slow on POSIX especially }; BOOST_AFIO_DECLARE_CLASS_ENUM_AS_BITFIELD(unit_testing_flags) } class handle; //! A type alias to a shared pointer to handle using handle_ptr = std::shared_ptr; /*! \enum metadata_flags \brief Bitflags for availability of metadata from `struct stat_t` \ingroup metadata_flags See __afio_stat_t__ for explanation of meaning. */ enum class metadata_flags : size_t { None=0, dev=1<<0, ino=1<<1, type=1<<2, perms=1<<3, nlink=1<<4, uid=1<<5, gid=1<<6, rdev=1<<7, atim=1<<8, mtim=1<<9, ctim=1<<10, size=1<<11, allocated=1<<12, blocks=1<<13, blksize=1<<14, flags=1<<15, gen=1<<16, birthtim=1<<17, sparse=1<<24, compressed=1<<25, reparse_point=1<<26, All=(size_t)-1 //!< Return the maximum possible metadata. }; BOOST_AFIO_DECLARE_CLASS_ENUM_AS_BITFIELD(metadata_flags) /*! \struct stat_t \brief Metadata about a directory entry This structure looks somewhat like a `struct stat`, and indeed it was derived from BSD's `struct stat`. However there are a number of changes to better interoperate with modern practice, specifically: - inode value containers are forced to 64 bits. - Timestamps use C++11's `std::chrono::system_clock::time_point` or Boost equivalent. The resolution of these may or may not equal what a `struct timespec` can do depending on your STL. - The type of a file, which is available on Windows and on POSIX without needing an additional syscall, is provided by `st_type` which is one of the values from `filesystem::file_type`. - As type is now separate from permissions, there is no longer a `st_mode`, instead being a `st_perms` which is solely the permissions bits. If you want to test permission bits in `st_perms` but don't want to include platform specific headers, note that `filesystem::perms` contains definitions of the POSIX permissions flags. - The st_sparse and st_compressed flags indicate if your file is sparse and/or compressed, or if the directory will compress newly created files by default. Note that on POSIX, a file is sparse if and only if st_allocated < st_size which can include compressed files if that filing system is mounted with compression enabled (e.g. ZFS with ZLE compression which elides runs of zeros). - The st_reparse_point is a Windows only flag and is never set on POSIX, even on a NTFS volume. */ struct stat_t { #ifndef WIN32 uint64_t st_dev; /*!< inode of device containing file (POSIX only) */ #endif uint64_t st_ino; /*!< inode of file (Windows, POSIX) */ filesystem::file_type st_type; /*!< type of file (Windows, POSIX) */ #ifndef WIN32 #ifndef DOXYGEN_SHOULD_SKIP_THIS uint16_t st_perms; #else filesystem::perms st_perms; /*!< uint16_t bitfield perms of file (POSIX only) */ #endif #endif int16_t st_nlink; /*!< number of hard links (Windows, POSIX) */ #ifndef WIN32 int16_t st_uid; /*!< user ID of the file (POSIX only) */ int16_t st_gid; /*!< group ID of the file (POSIX only) */ dev_t st_rdev; /*!< id of file if special (POSIX only) */ #endif chrono::system_clock::time_point st_atim; /*!< time of last access (Windows, POSIX) */ chrono::system_clock::time_point st_mtim; /*!< time of last data modification (Windows, POSIX) */ chrono::system_clock::time_point st_ctim; /*!< time of last status change (Windows, POSIX) */ off_t st_size; /*!< file size, in bytes (Windows, POSIX) */ off_t st_allocated; /*!< bytes allocated for file (Windows, POSIX) */ off_t st_blocks; /*!< number of blocks allocated (Windows, POSIX) */ uint16_t st_blksize; /*!< block size used by this device (Windows, POSIX) */ uint32_t st_flags; /*!< user defined flags for file (FreeBSD, OS X, zero otherwise) */ uint32_t st_gen; /*!< file generation number (FreeBSD, OS X, zero otherwise)*/ chrono::system_clock::time_point st_birthtim; /*!< time of file creation (Windows, FreeBSD, OS X, zero otherwise) */ unsigned st_sparse : 1; /*!< if this file is sparse, or this directory capable of sparse files (Windows, POSIX) */ unsigned st_compressed : 1; /*!< if this file is compressed, or this directory capable of compressed files (Windows) */ unsigned st_reparse_point : 1; /*!< if this file or directory is a reparse point (Windows) */ //! Constructs a UNINITIALIZED instance i.e. full of random garbage stat_t() { } //! Constructs a zeroed instance stat_t(std::nullptr_t) : #ifndef WIN32 st_dev(0), #endif st_ino(0), #ifdef BOOST_AFIO_USE_LEGACY_FILESYSTEM_SEMANTICS st_type(filesystem::file_type::type_unknown), #else st_type(filesystem::file_type::unknown), #endif #ifndef WIN32 st_perms(0), #endif st_nlink(0), #ifndef WIN32 st_uid(0), st_gid(0), st_rdev(0), #endif st_size(0), st_allocated(0), st_blocks(0), st_blksize(0), st_flags(0), st_gen(0), st_sparse(0), st_compressed(0), st_reparse_point(0) { } }; /*! \enum fs_metadata_flags \brief Bitflags for availability of metadata from `struct statfs_t` \ingroup fs_metadata_flags */ enum class fs_metadata_flags : size_t { None=0, flags=1<<1, bsize=1<<2, iosize=1<<3, blocks=1<<4, bfree=1<<5, bavail=1<<6, files=1<<7, ffree=1<<8, namemax=1<<9, owner=1<<10, fsid=1<<11, fstypename=1<<12, mntfromname=1<<13, mntonname=1<<14, All=(size_t)-1 //!< Return the maximum possible metadata. }; BOOST_AFIO_DECLARE_CLASS_ENUM_AS_BITFIELD(fs_metadata_flags) /*! \struct statfs_t \brief Metadata about a filing system. Unsupported entries are -1. \qbk{ [include generated/struct_statfs_t_1_1f_flags_t.qbk] } */ struct statfs_t { struct f_flags_t { uint32_t rdonly : 1; //!< Filing system is read only (Windows, POSIX) uint32_t noexec : 1; //!< Filing system cannot execute programs (POSIX) uint32_t nosuid : 1; //!< Filing system cannot superuser (POSIX) uint32_t acls : 1; //!< Filing system provides ACLs (Windows, POSIX) uint32_t xattr : 1; //!< Filing system provides extended attributes (Windows, POSIX) uint32_t compression : 1; //!< Filing system provides whole volume compression (Windows, POSIX) uint32_t extents : 1; //!< Filing system provides extent based file storage (sparse files) (Windows, POSIX) uint32_t filecompression : 1; //!< Filing system provides per-file selectable compression (Windows) } f_flags; /*!< copy of mount exported flags (Windows, POSIX) */ uint64_t f_bsize; /*!< fundamental filesystem block size (Windows, POSIX) */ uint64_t f_iosize; /*!< optimal transfer block size (Windows, POSIX) */ uint64_t f_blocks; /*!< total data blocks in filesystem (Windows, POSIX) */ uint64_t f_bfree; /*!< free blocks in filesystem (Windows, POSIX) */ uint64_t f_bavail; /*!< free blocks avail to non-superuser (Windows, POSIX) */ uint64_t f_files; /*!< total file nodes in filesystem (POSIX) */ uint64_t f_ffree; /*!< free nodes avail to non-superuser (POSIX) */ uint32_t f_namemax; /*!< maximum filename length (Windows, POSIX) */ #ifndef WIN32 int16_t f_owner; /*!< user that mounted the filesystem (BSD, OS X) */ #endif uint64_t f_fsid[2]; /*!< filesystem id (Windows, POSIX) */ std::string f_fstypename; /*!< filesystem type name (Windows, POSIX) */ std::string f_mntfromname; /*!< mounted filesystem (Windows, POSIX) */ path f_mntonname; /*!< directory on which mounted (Windows, POSIX) */ statfs_t() { size_t frontbytes=((char *) &f_fstypename)-((char *) this); memset(this, 0xff, frontbytes); memset(this, 0, sizeof(f_flags)); } }; /*! \brief The abstract base class for an entry in a directory with lazily filled metadata. Note that `directory_entry_hash` will hash one of these for you, and a `std::hash` specialisation is defined for you so you ought to be able to use directory_entry directly in an `unordered_map<>`. See `__afio_stat_t__` for explanations of the fields. \qbk{ [include generated/struct_directory_entry_hash.qbk] } */ class BOOST_AFIO_DECL directory_entry { friend class detail::async_file_io_dispatcher_compat; friend class detail::async_file_io_dispatcher_windows; friend class detail::async_file_io_dispatcher_linux; friend class detail::async_file_io_dispatcher_qnx; path::string_type leafname; stat_t stat; metadata_flags have_metadata; BOOST_AFIO_HEADERS_ONLY_MEMFUNC_SPEC void _int_fetch(metadata_flags wanted, handle_ptr dirh); public: //! \constr directory_entry() : stat(nullptr), have_metadata(metadata_flags::None) { } //! \constr directory_entry(path::string_type _leafname, stat_t __stat, metadata_flags _have_metadata) : leafname(_leafname), stat(__stat), have_metadata(_have_metadata) { } directory_entry(const directory_entry &) = default; directory_entry &operator=(const directory_entry &) = default; directory_entry(directory_entry &&o) noexcept : leafname(std::move(o.leafname)), stat(std::move(o.stat)), have_metadata(std::move(o.have_metadata)) { } directory_entry &operator=(directory_entry &&o) noexcept { leafname=std::move(o.leafname); stat=std::move(o.stat); have_metadata=std::move(o.have_metadata); return *this; } bool operator==(const directory_entry& rhs) const noexcept { return leafname == rhs.leafname; } bool operator!=(const directory_entry& rhs) const noexcept { return leafname != rhs.leafname; } bool operator< (const directory_entry& rhs) const noexcept { return leafname < rhs.leafname; } bool operator<=(const directory_entry& rhs) const noexcept { return leafname <= rhs.leafname; } bool operator> (const directory_entry& rhs) const noexcept { return leafname > rhs.leafname; } bool operator>=(const directory_entry& rhs) const noexcept { return leafname >= rhs.leafname; } //! \return The name of the directory entry. May be empty if the file is deleted. path::string_type name() const noexcept { return leafname; } //! \return A bitfield of what metadata is ready right now metadata_flags metadata_ready() const noexcept { return have_metadata; } /*! \brief Fetches the specified metadata, returning that newly available. This is a blocking call if wanted metadata is not yet ready. Note that if the call blocks and the leafname no longer exists or the directory handle is null, an exception is thrown. \return The metadata now available in this directory entry. \param dirh An open handle to the entry's containing directory. You can get this from an op ref using dirop.get_handle(). \param wanted A bitfield of the metadata to fetch. This does not replace existing metadata. */ metadata_flags fetch_metadata(handle_ptr dirh, metadata_flags wanted) { metadata_flags tofetch; wanted=wanted&metadata_supported(); tofetch=wanted&~have_metadata; if(!!tofetch) _int_fetch(tofetch, dirh); return have_metadata; } /*! \brief Returns a copy of the internal `stat_t` structure. This is a blocking call if wanted metadata is not yet ready. Note that if the call blocks and the leafname no longer exists or the directory handle is null, an exception is thrown. \return A copy of the internal `stat_t` structure. \param dirh An open handle to the entry's containing directory. You can get this from an op ref using dirop.get_handle(). \param wanted A bitfield of the metadata to fetch. This does not replace existing metadata. */ stat_t fetch_lstat(handle_ptr dirh, metadata_flags wanted=directory_entry::metadata_fastpath()) { fetch_metadata(dirh, wanted); return stat; } #ifndef DOXYGEN_SHOULD_SKIP_THIS #define BOOST_AFIO_DIRECTORY_ENTRY_ACCESS_METHOD(field) \ decltype(stat_t().st_##field) st_##field() const { if(!(have_metadata&metadata_flags::field)) { BOOST_AFIO_THROW(std::runtime_error("Field st_" #field " not present.")); } return stat.st_##field; } \ decltype(stat_t().st_##field) st_##field(handle_ptr dirh) { if(!(have_metadata&metadata_flags::field)) { _int_fetch(metadata_flags::field, dirh); } return stat.st_##field; } #else #define BOOST_AFIO_DIRECTORY_ENTRY_ACCESS_METHOD(field) \ fieldtype st_##field(handle_ptr dirh=handle_ptr()) { if(!(have_metadata&metadata_flags::field)) { _int_fetch(metadata_flags::field, dirh); } return stat.st_##field; } #endif #ifndef WIN32 //! Returns st_dev \param dirh An optional open handle to the entry's containing directory if fetching missing metadata is desired (an exception is thrown otherwise). You can get this from an op ref using dirop.get_handle(). BOOST_AFIO_DIRECTORY_ENTRY_ACCESS_METHOD(dev) #endif //! Returns st_ino \param dirh An optional open handle to the entry's containing directory if fetching missing metadata is desired (an exception is thrown otherwise). You can get this from an op ref using dirop.get_handle(). BOOST_AFIO_DIRECTORY_ENTRY_ACCESS_METHOD(ino) //! Returns st_type \param dirh An optional open handle to the entry's containing directory if fetching missing metadata is desired (an exception is thrown otherwise). You can get this from an op ref using dirop.get_handle(). BOOST_AFIO_DIRECTORY_ENTRY_ACCESS_METHOD(type) #ifndef WIN32 //! Returns st_perms \param dirh An optional open handle to the entry's containing directory if fetching missing metadata is desired (an exception is thrown otherwise). You can get this from an op ref using dirop.get_handle(). BOOST_AFIO_DIRECTORY_ENTRY_ACCESS_METHOD(perms) #endif //! Returns st_nlink \param dirh An optional open handle to the entry's containing directory if fetching missing metadata is desired (an exception is thrown otherwise). You can get this from an op ref using dirop.get_handle(). BOOST_AFIO_DIRECTORY_ENTRY_ACCESS_METHOD(nlink) #ifndef WIN32 //! Returns st_uid \param dirh An optional open handle to the entry's containing directory if fetching missing metadata is desired (an exception is thrown otherwise). You can get this from an op ref using dirop.get_handle(). BOOST_AFIO_DIRECTORY_ENTRY_ACCESS_METHOD(uid) //! Returns st_gid \param dirh An optional open handle to the entry's containing directory if fetching missing metadata is desired (an exception is thrown otherwise). You can get this from an op ref using dirop.get_handle(). BOOST_AFIO_DIRECTORY_ENTRY_ACCESS_METHOD(gid) //! Returns st_rdev \param dirh An optional open handle to the entry's containing directory if fetching missing metadata is desired (an exception is thrown otherwise). You can get this from an op ref using dirop.get_handle(). BOOST_AFIO_DIRECTORY_ENTRY_ACCESS_METHOD(rdev) #endif //! Returns st_atim \param dirh An optional open handle to the entry's containing directory if fetching missing metadata is desired (an exception is thrown otherwise). You can get this from an op ref using dirop.get_handle(). BOOST_AFIO_DIRECTORY_ENTRY_ACCESS_METHOD(atim) //! Returns st_mtim \param dirh An optional open handle to the entry's containing directory if fetching missing metadata is desired (an exception is thrown otherwise). You can get this from an op ref using dirop.get_handle(). BOOST_AFIO_DIRECTORY_ENTRY_ACCESS_METHOD(mtim) //! Returns st_ctim \param dirh An optional open handle to the entry's containing directory if fetching missing metadata is desired (an exception is thrown otherwise). You can get this from an op ref using dirop.get_handle(). BOOST_AFIO_DIRECTORY_ENTRY_ACCESS_METHOD(ctim) //! Returns st_size \param dirh An optional open handle to the entry's containing directory if fetching missing metadata is desired (an exception is thrown otherwise). You can get this from an op ref using dirop.get_handle(). BOOST_AFIO_DIRECTORY_ENTRY_ACCESS_METHOD(size) //! Returns st_allocated \param dirh An optional open handle to the entry's containing directory if fetching missing metadata is desired (an exception is thrown otherwise). You can get this from an op ref using dirop.get_handle(). BOOST_AFIO_DIRECTORY_ENTRY_ACCESS_METHOD(allocated) //! Returns st_blocks \param dirh An optional open handle to the entry's containing directory if fetching missing metadata is desired (an exception is thrown otherwise). You can get this from an op ref using dirop.get_handle(). BOOST_AFIO_DIRECTORY_ENTRY_ACCESS_METHOD(blocks) //! Returns st_blksize \param dirh An optional open handle to the entry's containing directory if fetching missing metadata is desired (an exception is thrown otherwise). You can get this from an op ref using dirop.get_handle(). BOOST_AFIO_DIRECTORY_ENTRY_ACCESS_METHOD(blksize) //! Returns st_flags \param dirh An optional open handle to the entry's containing directory if fetching missing metadata is desired (an exception is thrown otherwise). You can get this from an op ref using dirop.get_handle(). BOOST_AFIO_DIRECTORY_ENTRY_ACCESS_METHOD(flags) //! Returns st_gen \param dirh An optional open handle to the entry's containing directory if fetching missing metadata is desired (an exception is thrown otherwise). You can get this from an op ref using dirop.get_handle(). BOOST_AFIO_DIRECTORY_ENTRY_ACCESS_METHOD(gen) //! Returns st_birthtim \param dirh An optional open handle to the entry's containing directory if fetching missing metadata is desired (an exception is thrown otherwise). You can get this from an op ref using dirop.get_handle(). BOOST_AFIO_DIRECTORY_ENTRY_ACCESS_METHOD(birthtim) //! A bitfield of what metadata is available on this platform. This doesn't mean all is available for every filing system. static BOOST_AFIO_HEADERS_ONLY_MEMFUNC_SPEC metadata_flags metadata_supported() noexcept; //! A bitfield of what metadata is fast on this platform. This doesn't mean all is available for every filing system. static BOOST_AFIO_HEADERS_ONLY_MEMFUNC_SPEC metadata_flags metadata_fastpath() noexcept; //! The maximum number of entries which is "usual" to fetch at once i.e. what your libc does. static BOOST_AFIO_HEADERS_ONLY_MEMFUNC_SPEC size_t compatibility_maximum() noexcept; }; /*! \brief A hasher for directory_entry, hashing inode and birth time (if available on this platform). */ struct directory_entry_hash { public: #ifdef _MSC_VER #pragma warning(push) #pragma warning(disable: 4310) // cast truncates constant value #endif size_t operator()(const directory_entry &p) const { size_t seed = (size_t) 0x9ddfea08eb382d69ULL; detail::hash_combine(seed, p.st_ino()); if(!!(directory_entry::metadata_supported() & metadata_flags::birthtim)) detail::hash_combine(seed, p.st_birthtim().time_since_epoch().count()); return seed; } #ifdef _MSC_VER #pragma warning(pop) #endif }; /*! \brief The abstract base class encapsulating a platform-specific file handle Note that failure to explicitly schedule closing a file handle in the dispatcher means it will be synchronously closed on last reference count by handle. This can consume considerable time, especially if SyncOnClose is enabled. \qbk{ [include generated/struct_handle_1_1mapped_file.qbk] [include generated/group_async_io_handle__ops.qbk] } */ class handle : public std::enable_shared_from_this { friend class dispatcher; friend struct detail::async_io_handle_posix; friend struct detail::async_io_handle_windows; friend class detail::async_file_io_dispatcher_compat; friend class detail::async_file_io_dispatcher_windows; friend class detail::async_file_io_dispatcher_linux; friend class detail::async_file_io_dispatcher_qnx; dispatcher *_parent; chrono::system_clock::time_point _opened; file_flags _flags; protected: handle_ptr dirh; atomic bytesread, byteswritten, byteswrittenatlastfsync; handle(dispatcher *parent, file_flags flags) : _parent(parent), _opened(chrono::system_clock::now()), _flags(flags), bytesread(0), byteswritten(0), byteswrittenatlastfsync(0) { } //! Calling this directly can cause misoperation. Best to avoid unless you have inspected the source code for the consequences. BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC void close() BOOST_AFIO_HEADERS_ONLY_VIRTUAL_UNDEFINED_SPEC public: BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC ~handle() { } //! Returns the parent of this io handle dispatcher *parent() const { return _parent; } //! Returns a handle to the directory containing this handle. Only works if `file_flags::hold_parent_open` was specified when this handle was opened. handle_ptr container() const { return dirh; } //! In which way this handle is opened or not enum class open_states { closed, //!< This handle is closed. open, //!< This handle is open as a normal handle. opendir //!< This handle is open as a cached directory handle, and therefore closing it explicitly has no effect. }; //! Returns if this handle is opened or not BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC open_states is_open() const BOOST_AFIO_HEADERS_ONLY_VIRTUAL_UNDEFINED_SPEC //! Returns the native handle of this io handle. On POSIX, you can cast this to a fd using `(int)(size_t) native_handle()`. On Windows it's a simple `(HANDLE) native_handle()`. BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC void *native_handle() const BOOST_AFIO_HEADERS_ONLY_VIRTUAL_UNDEFINED_SPEC //! Returns when this handle was opened const chrono::system_clock::time_point &opened() const { return _opened; } /*! \brief Returns the path of this i/o handle right now if the handle is open and \em refresh is true, else last known good. May be null if the file has been deleted. Note the refreshed path completely dereferences any intermediate symbolic links to return a truly absolute canonical path, and therefore may look quite different to before. Some operating systems unfortunately also return any one of the hard links to the file, so if hard links is greater than one the path refreshed will randomly permute. \ntkernelnamespacenote \return The path of this i/o handle right now. \param refresh Whether to ask the OS for the current path of this handle. \ingroup async_io_handle__ops \raceguarantees{ [raceguarantee FreeBSD..Paths are only refreshed for directories, not files.] [raceguarantee Linux, Windows..Paths are always refreshed and ignore other hard links.] [raceguarantee OS X..Paths are only refreshed for directories and files with a single hard link.] } */ BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC BOOST_AFIO_V2_NAMESPACE::path path(bool refresh=false) BOOST_AFIO_HEADERS_ONLY_VIRTUAL_UNDEFINED_SPEC //! Returns the last known good path of this i/o handle. May be null if the file has been deleted. BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC BOOST_AFIO_V2_NAMESPACE::path path() const BOOST_AFIO_HEADERS_ONLY_VIRTUAL_UNDEFINED_SPEC //! Returns the final flags used when this handle was opened file_flags flags() const { return _flags; } //! True if this handle was opened as a file bool opened_as_file() const { return !(_flags&file_flags::int_opening_dir) && !(_flags&file_flags::int_opening_link); } //! True if this handle was opened as a directory bool opened_as_dir() const { return !!(_flags&file_flags::int_opening_dir); } //! True if this handle was opened as a symlink bool opened_as_symlink() const { return !!(_flags&file_flags::int_opening_link); } //! True if this handle is used by the directory handle cache (not UniqueDirectoryHandle and is open for write and not open for write) bool available_to_directory_cache() const { return opened_as_dir() && !(_flags&file_flags::unique_directory_handle) && !!(_flags&file_flags::read) && !(_flags&file_flags::write); } //! Returns how many bytes have been read since this handle was opened. off_t read_count() const { return bytesread; } //! Returns how many bytes have been written since this handle was opened. off_t write_count() const { return byteswritten; } //! Returns how many bytes have been written since this handle was last fsynced. off_t write_count_since_fsync() const { return byteswritten-byteswrittenatlastfsync; } /*! \brief Returns a mostly filled directory_entry for the file or directory referenced by this handle. Use `metadata_flags::All` if you want it as complete as your platform allows, even at the cost of severe performance loss. Related types: `__afio_directory_entry__`, `__afio_stat_t__` \return A directory entry for this handle. \param wanted The metadata wanted. \ingroup async_io_handle__ops \raceguarantees{ [raceguarantee FreeBSD..Race free if handle open for directories and regular files only, else if handle closed or a symlink race free up to the containing directory. All metadata is fetched in a single shot.] [raceguarantee Linux..Race free if handle open, else if handle closed race free up to the containing directory. All metadata is fetched in a single shot.] [raceguarantee OS X..Race free if handle open for directories and regular files only. No guarantees if handle closed or a symlink.] [raceguarantee Windows..Handle must be open and is always race free. Metadata may be fetched in a single shot if at least two categories requested, or else the following categories apply: (i) ino (ii) type, atim, mtim, ctim, birthtim, sparse, compressed (iii) nlink, size, allocated, blocks.] } */ BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC directory_entry direntry(metadata_flags wanted=directory_entry::metadata_fastpath()) BOOST_AFIO_HEADERS_ONLY_VIRTUAL_UNDEFINED_SPEC /*! \brief Returns a mostly filled stat_t structure for the file or directory referenced by this handle. Use `metadata_flags::All` if you want it as complete as your platform allows, even at the cost of severe performance loss. Calls direntry(), so same race guarantees as that call. Related types: `__afio_directory_entry__`, `__afio_stat_t__` */ stat_t lstat(metadata_flags wanted=directory_entry::metadata_fastpath()) { directory_entry de(direntry(wanted)); return de.fetch_lstat(handle_ptr() /* actually unneeded */, wanted); } /*! \brief Returns the target path of this handle if it is a symbolic link. \ntkernelnamespacenote \return The path the symbolic link points to. May not exist or even be valid. \ingroup async_io_handle__ops \raceguarantees{ [raceguarantee FreeBSD..Race free up to the containing directory.] [raceguarantee Linux, Windows..Race free if handle open, else up to the containing directory.] [raceguarantee OS X..No guarantees.] } */ BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC BOOST_AFIO_V2_NAMESPACE::path target() BOOST_AFIO_HEADERS_ONLY_VIRTUAL_UNDEFINED_SPEC //! A holder of a mapped file. struct BOOST_AFIO_DECL mapped_file { friend class handle; handle_ptr h; //!< The file being mapped void *addr; //!< The address in memory of the map size_t length; //!< The length of the map off_t offset; //!< The offset of the map into the file mapped_file(const mapped_file &) = delete; mapped_file(mapped_file &&) = delete; mapped_file &operator=(const mapped_file &) = delete; mapped_file &operator=(mapped_file &&) = delete; mapped_file(handle_ptr _h, void *_addr, size_t _length, off_t _offset) : h(std::move(_h)), addr(_addr), length(_length), offset(_offset) { } ~mapped_file(); }; //! A type alias to a mapped file pointer using mapped_file_ptr = std::unique_ptr; //! Maps the file into memory, returning a null pointer if couldn't map (e.g. address space exhaustion). Do NOT mix this with `file_flags::os_direct`! BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC mapped_file_ptr map_file(size_t length = (size_t)-1, off_t offset = 0, bool read_only = false) { return nullptr; } /*! \brief Hard links the file to a new location on the same volume. If you wish to make a temporary file whose contents are ready appear at a location and error out if a file entry is already there, use link() and if success, unlink() on the former location. If you wish to always overwrite the destination, use atomic_relink() instead. On Windows, the destination directory cannot have any handle opened to it with delete/rename privileges (`file_flags::write`) anywhere in the system. This is an operating system limitation. \ntkernelnamespacenote Related types: `__afio_path_req__` \param req The absolute or relative (in which case precondition specifies a directory) path to create a hard link at. \ingroup async_io_handle__ops \raceguarantees{ [raceguarantee FreeBSD..Race free up to the containing directory for both source and target.] [raceguarantee Linux, Windows..Race free for source if handle open, else up to the containing directory. Race free up to the target directory.] [raceguarantee OS X..No guarantees.] } */ BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC void link(const path_req &req) BOOST_AFIO_HEADERS_ONLY_VIRTUAL_UNDEFINED_SPEC /*! \brief Unlinks the file from its present location as determined by path(true), which could be any hard link on those operating systems with an unstable path(true). Other links may remain to the same file. On Microsoft Windows, this routine unlinks items as follows: 1. It tries to atomically rename the item to the root of the mounted volume it lives in with a .afiodXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX where the X's are a 128 bit crypto random hexadecimal. If that fails, it tries the next directory up, and the next after that until success if any. This rename may fail for any reason, including if it is a directory with open file handles somewhere within. If it fails, the rename is skipped. 2. It marks the item with hidden and system attributes to hide it from normal directory enumeration. 3. It sets the delete on last handle close flag. At some stl_future point Windows will delete the item, until which it will hang around in a zombie state with an unknowable name and location unopenable by any new processes. The reason for such complexity is that this algorithm, if it renames successfully, neatly works around a number of annoying features in Windows, specifically that when you delete a file you actually don't delete it till an unknown amount of time later. This breaks code which tries to delete a directory tree, and finds that the directories won't delete because they still contain files supposedly deleted but actually not quite yet. By renaming the items as far away as possible, this problem ought to go away - unless of course that the user does not have permissions to write into any directory other than the one being eventually deleted, in which case you will still see the strange access denied and directory not empty errors from before. \ntkernelnamespacenote Related types: `__afio_path_req__` \ingroup async_io_handle__ops \raceguarantees{ [raceguarantee FreeBSD, Linux..Race free up to the containing directory.] [raceguarantee Windows..Race free if handle open, else up to the containing directory.] [raceguarantee OS X..No guarantees.] } */ BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC void unlink() BOOST_AFIO_HEADERS_ONLY_VIRTUAL_UNDEFINED_SPEC /*! \brief Links the file to a new location and unlinks the file from its present location as determined by path(true), atomically overwriting any file entry at the new location. Very useful for preparing file content elsewhere and once ready, atomically making it visible at some named location to other processes. Note that operating systems with an unstable path(true) may relink any hard link to the file to the new location. Note that not all filing systems guarantee the atomicity of the relink itself (i.e. the file may appear at two locations in the filing system for a period of time), though all supported platforms do guarantee the atomicity of the replaced location i.e. the location you are relinking to will always refer to some valid file to all readers, and will never be deleted or missing. Some filing systems may also fail to do the unlink if power is lost close to the relinking operation. On Windows, the destination directory cannot have any handle opened to it with delete/rename privileges (`file_flags::write`) anywhere in the system. This is an operating system limitation. \ntkernelnamespacenote Related types: `__afio_path_req__` \param req The absolute or relative (in which case precondition specifies a directory) path to relink to. \ingroup async_io_handle__ops \raceguarantees{ [raceguarantee FreeBSD, Linux..Race free up to the containing directory for both source and target.] [raceguarantee OS X..No guarantees.] [raceguarantee Windows..Race free for source if handle open, else up to the containing directory. Race free up to the target directory.] } */ BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC void atomic_relink(const path_req &req) BOOST_AFIO_HEADERS_ONLY_VIRTUAL_UNDEFINED_SPEC #if 0 // Undocumented deliberately enum class change_flags : size_t { created=(1<<0), // NOTE_EXTEND, IN_CREATE, renamed=(1<<1), // NOTE_RENAME, IN_MOVED_FROM/IN_MOVED_TO, deleted=(1<<2), // NOTE_DELETE, IN_DELETE, attributes=(1<<3), // NOTE_ATTRIB, IN_ATTRIB, opened=(1<<4), // ?, IN_OPEN, closed=(1<<5), // ?, IN_CLOSE_WRITE/IN_CLOSE_NOWRITE, read=(1<<6), // ?, IN_ACCESS, written=(1<<7), // NOTE_WRITE, IN_MODIFY, extended=(1<<8), // NOTE_EXTEND, ?, region_locked=(1<<16), region_timedout=(1<<17), region_unlocked=(1<<18) }; // Undocumented deliberately struct change_listener : public std::enable_shared_from_this { virtual ~change_listener() { } virtual void operator()(handle *h, change_flags changed, void *additional)=0; }; // Undocumented deliberately BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC void listen(const std::vector, std::shared_ptr> &listeners) BOOST_AFIO_HEADERS_ONLY_VIRTUAL_UNDEFINED_SPEC // Undocumented deliberately BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC void unlisten(const std::vector, std::shared_ptr> &listeners) BOOST_AFIO_HEADERS_ONLY_VIRTUAL_UNDEFINED_SPEC #endif }; /*! \brief Retrieves the currently set async_file_io_dispatcher for this thread, optionally setting it to a new dispatcher. \return The current async_file_io_dispatcher. \param new_dispatcher The new async_file_io_dispatcher to set. \ingroup async_file_io_dispatcher */ BOOST_AFIO_HEADERS_ONLY_FUNC_SPEC dispatcher_ptr current_dispatcher(option new_dispatcher = empty); /*! \class current_dispatcher_guard RAII holder for the current async file i/o dispatcher. \ingroup async_file_io_dispatcher */ class current_dispatcher_guard { dispatcher_ptr _old; public: current_dispatcher_guard(dispatcher_ptr _new) : _old(current_dispatcher(_new)) { } ~current_dispatcher_guard() { current_dispatcher(_old); } //! Restore the former async file i/o dispatcher now. void release() { current_dispatcher(_old); _old.reset(); } //! Don't restore the former async file i/o dispatcher. void dismiss() { _old.reset(); } //! Set a different former async file i/o dispatcher on destruction. void reset(dispatcher_ptr p) { _old=p; } }; //! Trait for determining if a type is an afio::future template struct is_future : std::false_type { }; template struct is_future> : std::true_type {}; // Temporary friends for future<> namespace detail { struct barrier_count_completed_state; template inline stl_future> when_all_ops(Iterator first, Iterator last); template inline stl_future when_any_ops(Iterator first, Iterator last); // Shim code for lightweight future continuations template::value, bool return_is_afio_future=is_future::value> struct continuation_return_type { using future_type = future; using promise_type = void; }; template struct continuation_return_type { using future_type = R; using promise_type = typename future_type::promise_type; }; template struct continuation_return_type { using future_type = R; using promise_type = void; }; template struct do_continuation; } /*! \ref future */ template<> class future { // Temporary friends until lightweight future promise comes in friend struct detail::barrier_count_completed_state; template friend inline stl_future> detail::when_all_ops(Iterator first, Iterator last); template friend inline stl_future detail::when_any_ops(Iterator first, Iterator last); dispatcher *_parent; //!< The parent dispatcher size_t _id; //!< A unique id for this operation shared_future _h; //!< A stl_future handle to the item being operated upon public: future(future &&o, stl_future &&result) : future(std::move(o)) { } // NOTE TO SELF: MAKE THE CONSTRUCTORS AND MEMBER FUNCTIONS constexpr WHEN I MERGE LIGHTWEIGHT FUTURE-PROMISES //! The type of value potentially returned using value_type = void; //! The error type potentially returned using error_type = error_code; //! The exception type potentially returned using exception_type = exception_ptr; //! \constr future() : _parent(nullptr), _id(0) { } //! \cconstr future(const future &o) = default; //! \mconstr future(future &&o) = default; /*! Constructs an instance. \param parent The dispatcher this op belongs to. \param id The unique non-zero id of this op. \param handle A shared_ptr to shared state between all instances of this reference. \param check_handle Whether to have validation additionally check if a handle is not null \param validate Whether to check the inputs and shared state for valid (and not errored) values */ future(dispatcher *parent, size_t id, shared_future handle, bool check_handle=true, bool validate=true) : _parent(parent), _id(id), _h(std::move(handle)) { if(validate) _validate(check_handle); } /*! Constructs an instance. \param _handle A shared_ptr to shared state between all instances of this reference. \param check_handle Whether to have validation additionally check if a handle is not null \param validate Whether to check the inputs and shared state for valid (and not errored) values */ future(handle_ptr _handle, bool check_handle=true, bool validate=true) : _parent(_handle->parent()), _id((size_t)-1) { promise p; p.set_value(std::move(_handle)); _h=p.get_future(); if(validate) _validate(check_handle); } /*! Constructs an instance. \param parent The dispatcher this op belongs to. \param id The unique non-zero id of this op. */ future(dispatcher *parent, size_t id) : _parent(parent), _id(id) { } //! \cassign future &operator=(const future &o) { _parent = o._parent; _id = o._id; _h = o._h; return *this; } //! \massign future &operator=(future &&o) noexcept { _parent = std::move(o._parent); _id = std::move(o._id); _h = std::move(o._h); return *this; } //! True if this future is valid bool valid() const noexcept { return _parent && _id; } //! \brief Same as `true_(tribool(*this))` explicit operator bool() const noexcept { return has_value(); } //! \brief True if monad is not empty bool is_ready() const noexcept { return valid() || _h.wait_for(chrono::seconds(0)) == future_status::ready; } //! \brief True if monad contains a value_type bool has_value() const noexcept { return is_ready() && !has_exception(); } //! \brief True if monad contains an error_type bool has_error() const noexcept { if (!is_ready()) return false; error_type ec = get_error(); return ec && ec.category() != monad_category(); } /*! \brief True if monad contains an exception_type or error_type (any error_type is returned as an exception_ptr by get_exception()). This needs to be true for both for compatibility with Boost.Thread's future. If you really want to test only for has exception only, pass true as the argument. */ bool has_exception(bool only_exception = false) const noexcept { if (!is_ready()) return false; return !!get_exception(); } //! The parent dispatcher of this future dispatcher *parent() const noexcept { return _parent; } //! \deprecate{Expected to be removed in the v1.5 engine} size_t id() const noexcept { return _id; } //! Retrieves the handle or exception from the shared state, rethrowing any exception. Returns a null shared pointer if this future is invalid. handle_ptr get_handle(bool return_null_if_errored=false) const { if(!_parent && !_id) return handle_ptr(); // std::shared_future in older libstdc++ does not have a const get(). if(!return_null_if_errored) return const_cast(this)->_h.get(); auto e=get_exception_ptr(_h); return e ? handle_ptr() : const_cast(this)->_h.get(); } //! Retrieves the handle or exception from the shared state, rethrowing any exception but setting _ec if there is an error. Returns a null shared pointer if this future is invalid. handle_ptr get_handle(error_type &ec) const { if (!_parent && !_id) return handle_ptr(); ec = get_error(); return ec ? handle_ptr() : const_cast(this)->_h.get(); } //! Dereferences the handle from the shared state. Same as *h.get_handle(). const handle &operator *() const { return *get_handle(); } //! Dereferences the handle from the shared state. Same as *h.get_handle(). handle &operator *() { return *get_handle(); } //! Dereferences the handle from the shared state. Same as h.get_handle()->get(). const handle *operator->() const { return get_handle().get(); } //! Dereferences the handle from the shared state. Same as h.get_handle()->get(). handle *operator->() { return get_handle().get(); } //! Waits for the future to become ready, rethrowing any exception found. Throws a `future_errc::no_state` if this future is invalid. void get() { if (!valid()) throw future_error(future_errc::no_state); _h.get(); } //! Waits for the future to become ready, returning any error state found error_type get_error() const { if (!valid()) throw future_error(future_errc::no_state); auto e = get_exception_ptr(_h); if (e) { try { rethrow_exception(e); } catch (const system_error &_e) { return _e.code(); } catch (...) { return error_type((int)monad_errc::exception_present, monad_category()); } } return error_type(); } //! Waits for the future to become ready, returning any error state found exception_type get_exception() const { if (!valid()) throw future_error(future_errc::no_state); return get_exception_ptr(_h); } //! Waits for the future to become ready. Throws a `future_errc::no_state` if this future is invalid. void wait() const { if (!valid()) throw future_error(future_errc::no_state); _h.wait(); } //! Waits for the future to become ready for a period. Throws a `future_errc::no_state` if this future is invalid. template future_status wait_for(const chrono::duration &duration) const { if (!valid()) throw future_error(future_errc::no_state); return _h.wait_for(duration); } //! Waits for the future to become ready until a deadline. Throws a `future_errc::no_state` if this future is invalid. template future_status wait_until(const chrono::time_point &deadline) const { if (!valid()) throw future_error(future_errc::no_state); return _h.wait_until(deadline); } //! Schedules a callable to be invoked after this future becomes ready. If this future is null, use the current async file i/o dispatcher. template auto then(U &&f) -> typename detail::continuation_return_type::future_type { using future_type = typename detail::continuation_return_type::future_type; using promise_type = typename detail::continuation_return_type::promise_type; return detail::do_continuation()(parent(), this, std::forward(f)); } //! Validates contents bool validate(bool check_handle=true) const { if(!valid()) return false; // If h is valid and ready and contains an exception, throw it now if(_h.valid() && BOOST_AFIO_V2_NAMESPACE::is_ready(_h)) { if(check_handle) if(!const_cast &>(_h).get().get()) return false; } return true; } protected: void _validate(bool check_handle=true) const { #if BOOST_AFIO_VALIDATE_INPUTS if(!validate(check_handle)) BOOST_AFIO_THROW(std::invalid_argument("Inputs are invalid.")); #endif } }; /*! \class future \tparam T Any returned result. Note this is defaulted to `void` for you, so usually you write `future<>`. \brief The future status of a scheduled asynchronous operation As of v1.4 of the AFIO engine, the legacy `async_io_op` struct has been replaced with this custom future type based on the lightweight future-promise factory toolkit in forthcoming Boost.Monad. This custom future type consists of two pieces of future data each with different semantics: 1. A `handle_ptr` retrieved using `get_handle()`, `operator*` and `operator->` - this is the shared i/o handle returned by the asynchronous operation. This has non-consuming semantics i.e. you can call `get_handle()` as many times as you like. Note that for legacy compatibility reasons, calling `get_handle()` on an invalid instance returns a null shared pointer instead of an exception throw. 2. If T is non-void, any type `T` - this is any additional data returned by an asynchronous operation above and beyond the i/o handle (e.g. `enumerate()` returns a `std::pair, bool>`. This has *consuming* semantics, so calling `get()` returns the result exactly once. The reason for the difference in semantics is because it is very common that you need access to an earlier i/o handle in a sequence if some operation returns an error, and besides the shared pointer encapsulation makes non-consumption cost free. Other than the fact that `get()` returns a `T` and `get_handle()` returns a handle, the errored and excepted state for both is identical and non-consuming for both. Finally, note that there is a freely available type slice from `future` to `future` which moves/copies only the `future` part of the `future`, leaving the `T` behind. This is because the AFIO engine resides behind a stable ABI layer which cannot know anything about arbitrary types, and therefore it accepts only `future`. Equally, this means you can supply a `future` as a precondition to another op safe in the knowledge that any `T` part will remain untouched for later consumption. */ template class future : public future { stl_future _result; public: //! The type of value potentially returned using value_type = T; future() = default; /*! Constructs an instance. \param parent The dispatcher this op belongs to. \param id The unique non-zero id of this op. \param handle A shared_future to shared state between all instances of this reference. \param result A future to any result from the operation. \param check_handle Whether to have validation additionally check if a handle is not null \param validate Whether to check the inputs and shared state for valid (and not errored) values */ future(dispatcher *parent, size_t id, shared_future handle, stl_future result, bool check_handle = true, bool validate = true) : future(parent, id, std::move(handle), check_handle, validate), _result(std::move(result)) { } /*! Constructs an instance from an existing future \param o The future \param result The future to add */ future(future &&o, stl_future &&result) : future(std::move(o)), _result(std::move(result)) { } //! True if this future is valid bool valid(bool just_handle=false) const noexcept { return future::valid() && (just_handle || _result.valid()); } //! Waits for the future to become ready, returning any value or rethrowing any exception found. Throws a `future_errc::no_state` if this future is invalid. T get() { return _result.get(); } //! Schedules a callable to be invoked after this future becomes ready. If this future is null, use the current async file i/o dispatcher. template auto then(U &&f) -> typename detail::continuation_return_type::future_type { using future_type = typename detail::continuation_return_type::future_type; using promise_type = typename detail::continuation_return_type::promise_type; return detail::do_continuation()(parent(), this, std::forward(f)); } }; namespace detail { // For continuations returning lightweight futures template struct do_continuation { template future_type operator()(D *d, future<> *src, U &&f) { if (!d) d = current_dispatcher().get(); auto p = std::make_shared(); d->completion(*src, std::make_pair(async_op_flags::immediate, [f, p](size_t id, future<> _f) -> std::pair { try { using result_type = decltype(f(_f)); f(_f).then([p](const result_type &_f) { auto s(_f.get_state()); try { p->set_state(std::move(s)); } catch (...) { /* Really should filter for no_state but this is shim code */ } }); } catch (...) { p->set_exception(current_exception()); } return std::make_pair(true, _f.get_handle()); })); return p->get_future(); } }; // For continuations returning shim AFIO futures or some naked type template struct do_continuation, void> { template future operator()(D *d, future *src, U &&f) { if (!d) d = current_dispatcher().get(); // TEMPORARY: For continuations taking a future where T is not void // we have no way of passing the correct future to the continuation until // the lightweight future promise refactor // // So simply call the continuation now. When it calls .get() it will block. return f(*src); } template future<> operator()(D *d, future<> *src, U &&f) { if (!d) d = current_dispatcher().get(); return d->completion(*src, std::make_pair(async_op_flags::immediate, [f](size_t id, future<> _f) -> std::pair { f(_f); return std::make_pair(true, _f.get_handle()); })); } }; } // This is a result_of filter to work around the weird mix of brittle decltype(), SFINAE incapable // std::result_of and variadic template overload resolution rules in VS2013. Works on other compilers // too of course, it simply prefilters out the call() overloads not matching the variadic overload. namespace detail { #if 0 template struct vs2013_variadic_overload_resolution_workaround; // Match callable template struct vs2013_variadic_overload_resolution_workaround { typedef typename std::result_of::type type; }; // Match callable template struct vs2013_variadic_overload_resolution_workaround { typedef typename std::result_of::type type; }; // Match callable template struct vs2013_variadic_overload_resolution_workaround { typedef typename std::result_of::type type; }; #else /* call(const std::vector &ops , const std::vector> &callables ); call(const std::vector> &callables ); call(const future &req , std::function callback ); call(const future &req , C callback , Args... args); */ template struct vs2013_variadic_overload_resolution_workaround { typedef typename std::result_of::type type; }; // Disable C being a const std::vector> &callables template struct vs2013_variadic_overload_resolution_workaround, Args...>; #endif template handle_ptr decode_relative_path(path_req &req, bool force_absolute=false); } /*! \class dispatcher \brief Abstract base class for dispatching file i/o asynchronously This is a reference counted instance with platform-specific implementation in object code. Construct an instance using the `boost::afio::make_dispatcher()` function. \qbk{ [/ link afio.reference.functions.async_file_io_dispatcher `async_file_io_dispatcher()`] [/ include generated/group_dispatcher__filter.qbk] [/ include generated/group_dispatcher__completion.qbk] [/ include generated/group_dispatcher__call.qbk] [include generated/group_dispatcher__filedirops.qbk] [include generated/group_dispatcher__enumerate.qbk] [include generated/group_dispatcher__extents.qbk] [include generated/group_dispatcher__statfs.qbk] [/ include generated/group_dispatcher__depends.qbk] [/ include generated/group_dispatcher__barrier.qbk] [include generated/group_dispatcher__misc.qbk] } */ class BOOST_AFIO_DECL dispatcher : public std::enable_shared_from_this { //friend BOOST_AFIO_DECL dispatcher_ptr async_file_io_dispatcher(thread_source &threadpool=process_threadpool(), file_flags flagsforce=file_flags::none, file_flags flagsmask=file_flags::none); template friend handle_ptr detail::decode_relative_path(path_req &req, bool force_absolute); friend struct detail::async_io_handle_posix; friend struct detail::async_io_handle_windows; friend class detail::async_file_io_dispatcher_compat; friend class detail::async_file_io_dispatcher_windows; friend class detail::async_file_io_dispatcher_linux; friend class detail::async_file_io_dispatcher_qnx; detail::dispatcher_p *p; BOOST_AFIO_HEADERS_ONLY_MEMFUNC_SPEC void int_directory_cached_handle_path_changed(path oldpath, path newpath, handle_ptr h); BOOST_AFIO_HEADERS_ONLY_MEMFUNC_SPEC void int_add_io_handle(void *key, handle_ptr h); BOOST_AFIO_HEADERS_ONLY_MEMFUNC_SPEC void int_del_io_handle(void *key); BOOST_AFIO_HEADERS_ONLY_MEMFUNC_SPEC future<> int_op_from_scheduled_id(size_t id) const; protected: BOOST_AFIO_HEADERS_ONLY_MEMFUNC_SPEC dispatcher(std::shared_ptr threadpool, file_flags flagsforce, file_flags flagsmask); std::pair doadopt(size_t, future<>, handle_ptr h) { return std::make_pair(true, h); } public: BOOST_AFIO_HEADERS_ONLY_MEMFUNC_SPEC void testing_flags(detail::unit_testing_flags flags); //! Destroys the dispatcher, blocking inefficiently if any ops are still in flight. BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC ~dispatcher(); //! Returns the thread source used by this dispatcher BOOST_AFIO_HEADERS_ONLY_MEMFUNC_SPEC std::shared_ptr threadsource() const; //! Returns file flags as would be used after forcing and masking bits passed during construction BOOST_AFIO_HEADERS_ONLY_MEMFUNC_SPEC file_flags fileflags(file_flags flags) const; //! Returns the current wait queue depth of this dispatcher BOOST_AFIO_HEADERS_ONLY_MEMFUNC_SPEC size_t wait_queue_depth() const; //! Returns the number of open items in this dispatcher BOOST_AFIO_HEADERS_ONLY_MEMFUNC_SPEC size_t fd_count() const; #ifndef DOXYGEN_SHOULD_SKIP_THIS /* \brief Returns an op ref for a given \b currently scheduled op id, throwing an exception if id not scheduled at the point of call. Can be used to retrieve exception state from some op id, or one's own shared stl_future. \return An future<> with the same shared stl_future as all op refs with this id. \param id The unique integer id for the op. */ BOOST_AFIO_HEADERS_ONLY_MEMFUNC_SPEC future<> op_from_scheduled_id(size_t id) const; // The type of an op filter callback handler \ingroup dispatcher__filter typedef void filter_t(detail::OpType, future<> &); // The type of a readwrite filter callback handler \ingroup dispatcher__filter typedef void filter_readwrite_t(detail::OpType, handle *, const detail::io_req_impl &, off_t, size_t, size_t, const error_code &, size_t); /* \brief Clears the post op and readwrite filters. Not threadsafe. \ingroup dispatcher__filter \complexity{O(1).} \qexample{filter_example} */ BOOST_AFIO_HEADERS_ONLY_MEMFUNC_SPEC void post_op_filter_clear(); /* \brief Install op filters for non-buffer taking ops. Not threadsafe. `std::function` will be called after every op of type `detail::OpType` completes (`detail::OpType::Unknown` means call this filter for all ops) with the op type and op output. Note that filters are currently implemented as a linear scan, so a full iteration of all filters is done for every op completed. The filter is called straight after an op's stl_future is set and before any completions are issued. Any exceptions thrown by the filter are thrown away. \param filters A batch of pairs of op type to be filtered and bound filter handler functions of type `filter_t` \ingroup dispatcher__filter \complexity{O(N) where N is the total number of filters currently configured.} \qexample{filter_example} */ BOOST_AFIO_HEADERS_ONLY_MEMFUNC_SPEC void post_op_filter(std::vector>> filters); /* \brief Install read/write op filters, useful for tight ASIO integration. Not threadsafe. `std::function` will be called after every op of type `detail::OpType` completes (`detail::OpType::Unknown` means call this filter for all ops) with the op type, file handle, op input, file offset, buffers offset, buffers amount, error state and bytes transferred. Any filter other than read() and write() will be ignored, for those use post_op_filter(). Note that buffer filters are currently implemented as a linear scan, so a full iteration of all buffer filters is done for every read/write op completed. The filter is called straight after a read or write operation has completed, and BEFORE any checks that it transferred the data it was supposed to. Any exceptions thrown by the filter are reported as if the read/write operation threw them, and filter processing stops at the filter which threw. \param filters A batch of pairs of op type to be filtered and bound filter handler functions of type `filter_buffers_t` \ingroup dispatcher__filter \complexity{O(N) where N is the total number of filters currently configured.} \qexample{filter_example} */ BOOST_AFIO_HEADERS_ONLY_MEMFUNC_SPEC void post_readwrite_filter(std::vector>> filters); // The type returned by a completion handler \ingroup dispatcher__completion typedef std::pair completion_returntype; // The type of a completion handler \ingroup dispatcher__completion typedef completion_returntype completion_t(size_t, future<>); #ifndef DOXYGEN_SHOULD_SKIP_THIS #if defined(BOOST_AFIO_ENABLE_BENCHMARKING_COMPLETION) || BOOST_AFIO_HEADERS_ONLY==0 // Only really used for benchmarking BOOST_AFIO_HEADERS_ONLY_MEMFUNC_SPEC std::vector> completion(const std::vector> &ops, const std::vector> &callbacks); inline future<> completion(const future<> &req, const std::pair &callback); #endif #endif /* \brief Schedule a batch of asynchronous invocations of the specified functions when their supplied operations complete. \deprecate{This function will be eliminated after lightweight future-promises are merged as one simply calls .then() on the future.} \return A batch of op handles \param ops A batch of precondition op handles. \param callbacks A batch of pairs of op flags and bound completion handler functions of type `completion_t` \ingroup dispatcher__completion \qbk{distinguish, batch bound functions} \complexity{Amortised O(N) to dispatch. Amortised O(N/threadpool) to complete.} \exceptionmodelstd \qexample{completion_example1} */ BOOST_AFIO_HEADERS_ONLY_MEMFUNC_SPEC std::vector> completion(const std::vector> &ops, const std::vector>> &callbacks); /* \brief Schedule the asynchronous invocation of the specified single function when the supplied single operation completes. \deprecate{This function will be eliminated after lightweight future-promises are merged as one simply calls .then() on the future.} \return An op handle \param req A precondition op handle \param callback A pair of op flag and bound completion handler function of type `completion_t` \ingroup dispatcher__completion \qbk{distinguish, single bound function} \complexity{Amortised O(1) to dispatch. Amortised O(1) to complete.} \exceptionmodelstd \qexample{completion_example1} */ inline future<> completion(const future<> &req, const std::pair> &callback); /* \brief Schedule a batch of asynchronous invocations of the specified bound functions when their supplied preconditions complete. \deprecate{This function will be eliminated after lightweight future-promises are merged as one simply calls .then() on the future.} This is effectively a convenience wrapper for `completion()`. It creates an enqueued_task matching the `completion_t` handler specification and calls the specified arbitrary callable, always returning completion on exit. \return A pair with a batch of futures returning the result of each of the callables and a batch of op handles. \tparam "class R" A compiler deduced return type of the bound functions. \param ops A batch of precondition op handles. If default constructed, a precondition is null. \param callables A batch of bound functions to call, returning R. \ingroup dispatcher__call \qbk{distinguish, batch bound functions} \complexity{Amortised O(N) to dispatch. Amortised O(N/threadpool) to complete.} \exceptionmodelstd \qexample{call_example} */ template inline std::vector> call(const std::vector> &ops, const std::vector> &callables); /* \brief Schedule a batch of asynchronous invocations of the specified bound functions when their supplied preconditions complete. \deprecate{This function will be eliminated after lightweight future-promises are merged as one simply calls .then() on the future.} This is effectively a convenience wrapper for `completion()`. It creates an enqueued_task matching the `completion_t` handler specification and calls the specified arbitrary callable, always returning completion on exit. If you are seeing performance issues, using `completion()` directly will have much less overhead. \return A pair with a batch of futures returning the result of each of the callables and a batch of op handles. \tparam "class R" A compiler deduced return type of the bound functions. \param callables A batch of bound functions to call, returning R. \ingroup dispatcher__call \qbk{distinguish, batch bound functions without preconditions} \complexity{Amortised O(N) to dispatch. Amortised O(N/threadpool) to complete.} \exceptionmodelstd \qexample{call_example} */ template std::vector> call(const std::vector> &callables) { return call(std::vector>(), callables); } /* \brief Schedule an asynchronous invocation of the specified bound function when its supplied precondition completes. \deprecate{This function will be eliminated after lightweight future-promises are merged as one simply calls .then() on the future.} This is effectively a convenience wrapper for `completion()`. It creates an enqueued_task matching the `completion_t` handler specification and calls the specified arbitrary callable, always returning completion on exit. If you are seeing performance issues, using `completion()` directly will have much less overhead. \return A pair with a stl_future returning the result of the callable and an op handle. \tparam "class R" A compiler deduced return type of the bound functions. \param req A precondition op handle. If default constructed, the precondition is null. \param callback A bound functions to call, returning R. \ingroup async_file_io_dispatcher__call \qbk{distinguish, single bound function} \complexity{Amortised O(1) to dispatch. Amortised O(1) to complete.} \exceptionmodelstd \qexample{call_example} */ template inline future call(const future<> &req, std::function callback); /* \brief Schedule an asynchronous invocation of the specified unbound callable when its supplied precondition completes. Note that this function essentially calls `std::bind()` on the callable and the args and passes it to the other call() overload taking a `std::function<>`. You should therefore use `std::ref()` etc. as appropriate. This is effectively a convenience wrapper for `completion()`. It creates an enqueued_task matching the `completion_t` handler specification and calls the specified arbitrary callable, always returning completion on exit. If you are seeing performance issues, using `completion()` directly will have much less overhead. \return A pair with a stl_future returning the result of the callable and an op handle. \tparam "class C" Any callable type. \tparam Args Any sequence of argument types. \param req A precondition op handle. If default constructed, the precondition is null. \param callback An unbound callable to call. \param args An arbitrary sequence of arguments to bind to the callable. \ingroup dispatcher__call \qbk{distinguish, single unbound callable} \complexity{Amortised O(1) to dispatch. Amortised O(1) to complete.} \exceptionmodelstd \qexample{call_example} */ #ifndef DOXYGEN_SHOULD_SKIP_THIS template inline future::type> call(const future<> &req, C callback, Args... args); #else template inline future::type> call(const future<> &req, C callback, Args... args); #endif /* \brief Schedule a batch of third party handle adoptions. \docs_adopt \return A batch of op handles. \param hs A batch of handles to adopt. \ingroup dispatcher__filedirops \qbk{distinguish, batch} \complexity{Amortised O(N) to dispatch. Amortised O(N/threadpool) to complete.} \exceptionmodelstd \qexample{adopt_example} */ BOOST_AFIO_HEADERS_ONLY_MEMFUNC_SPEC std::vector> adopt(const std::vector &hs); #endif /*! \brief Schedule a batch of asynchronous directory creations and opens after optional preconditions. \docs_dir \ntkernelnamespacenote \return A batch of op handles. \param reqs A batch of `path_req` structures. \ingroup dir \qbk{distinguish, batch} \raceguarantees{ [raceguarantee FreeBSD, Linux, Windows..Race free up to the containing directory.] [raceguarantee OS X..No guarantees.] } \complexity{Amortised O(N) to dispatch. Amortised O(N/threadpool) to complete if directory creation is constant time.} \exceptionmodelstd \qexample{filedir_example} */ BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC std::vector> dir(const std::vector &reqs) BOOST_AFIO_HEADERS_ONLY_VIRTUAL_UNDEFINED_SPEC /*! \brief Schedule a batch of asynchronous directory deletions after optional preconditions. \docs_rmdir \ntkernelnamespacenote \return A batch of op handles. \param reqs A batch of `path_req` structures. \ingroup rmdir \qbk{distinguish, batch} \raceguarantees{ [raceguarantee FreeBSD, Linux..Race free up to the containing directory.] [raceguarantee Windows..Race free if handle open, else up to the containing directory.] [raceguarantee OS X..No guarantees.] } \complexity{Amortised O(N) to dispatch. Amortised O(N/threadpool) to complete if directory deletion is constant time.} \exceptionmodelstd \qexample{filedir_example} */ BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC std::vector> rmdir(const std::vector &reqs) BOOST_AFIO_HEADERS_ONLY_VIRTUAL_UNDEFINED_SPEC /*! \brief Schedule a batch of asynchronous file creations and opens after optional preconditions. \docs_file \ntkernelnamespacenote \return A batch of op handles. \param reqs A batch of `path_req` structures. \ingroup file \qbk{distinguish, batch} \raceguarantees{ [raceguarantee FreeBSD, Linux, Windows..Race free up to the containing directory.] [raceguarantee OS X..No guarantees.] } \complexity{Amortised O(N) to dispatch. Amortised O(N/threadpool) to complete if file creation is constant time.} \exceptionmodelstd \qexample{filedir_example} */ BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC std::vector> file(const std::vector &reqs) BOOST_AFIO_HEADERS_ONLY_VIRTUAL_UNDEFINED_SPEC /*! \brief Schedule a batch of asynchronous file deletions after optional preconditions. \docs_rmfile \ntkernelnamespacenote \return A batch of op handles. \param reqs A batch of `path_req` structures. \ingroup rmfile \qbk{distinguish, batch} \raceguarantees{ [raceguarantee FreeBSD, Linux..Race free up to the containing directory.] [raceguarantee Windows..Race free if handle open, else up to the containing directory.] [raceguarantee OS X..No guarantees.] } \complexity{Amortised O(N) to dispatch. Amortised O(N/threadpool) to complete if file deletion is constant time.} \exceptionmodelstd \qexample{filedir_example} */ BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC std::vector> rmfile(const std::vector &reqs) BOOST_AFIO_HEADERS_ONLY_VIRTUAL_UNDEFINED_SPEC /*! \brief Schedule a batch of asynchronous symlink creations and opens after a precondition. \docs_symlink \ntkernelnamespacenote \return A batch of op handles. \param reqs A batch of `path_req` structures. \param targets An optional batch of targets if creating symlinks. \ingroup symlink \qbk{distinguish, batch} \raceguarantees{ [raceguarantee FreeBSD, Linux, Windows..Link creation is race free up to the containing directory. Destination is unavoidably racy.] [raceguarantee OS X..No guarantees.] } \complexity{Amortised O(N) to dispatch. Amortised O(N/threadpool) to complete if symlink creation is constant time.} \exceptionmodelstd \qexample{filedir_example} */ BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC std::vector> symlink(const std::vector &reqs, const std::vector> &targets=std::vector>()) BOOST_AFIO_HEADERS_ONLY_VIRTUAL_UNDEFINED_SPEC /*! \brief Schedule a batch of asynchronous symlink deletions after optional preconditions. \docs_rmsymlink \return A batch of op handles. \param reqs A batch of `path_req` structures. \ingroup rmsymlink \qbk{distinguish, batch} \raceguarantees{ [raceguarantee FreeBSD, Linux..Race free up to the containing directory.] [raceguarantee Windows..Race free if handle open, else up to the containing directory.] [raceguarantee OS X..No guarantees.] } \complexity{Amortised O(N) to dispatch. Amortised O(N/threadpool) to complete if symlink deletion is constant time.} \exceptionmodelstd \qexample{filedir_example} */ BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC std::vector> rmsymlink(const std::vector &reqs) BOOST_AFIO_HEADERS_ONLY_VIRTUAL_UNDEFINED_SPEC /*! \brief Schedule a batch of asynchronous content synchronisations with physical storage after preceding operations. \docs_sync \return A batch of op handles. \param ops A batch of op handles. \ingroup sync \qbk{distinguish, batch} \complexity{Amortised O(N) to dispatch. Amortised O(N/threadpool) to complete if content synchronisation is constant time (which is extremely unlikely).} \exceptionmodelstd \qexample{readwrite_example} */ BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC std::vector> sync(const std::vector> &ops) BOOST_AFIO_HEADERS_ONLY_VIRTUAL_UNDEFINED_SPEC /*! \brief Schedule a batch of asynchronous zeroing and deallocations of physical storage ("hole punching") after preceding operations. \docs_zero \return A batch of op handles. \param ops A batch of op handles. \param ranges A batch of vectors of extents to zero and deallocate. \ingroup zero \qbk{distinguish, batch} \complexity{Amortised O(N) to dispatch. Amortised O(N/threadpool) to complete if deallocation is constant time.} \exceptionmodelstd \qexample{extents_example} */ BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC std::vector> zero(const std::vector> &ops, const std::vector>> &ranges) BOOST_AFIO_HEADERS_ONLY_VIRTUAL_UNDEFINED_SPEC /*! \brief Schedule a batch of asynchronous file or directory handle closes after preceding operations. \docs_close \return A batch of op handles. \param ops A batch of op handles. \ingroup close \qbk{distinguish, batch} \complexity{Amortised O(N) to dispatch. Amortised O(N/threadpool) to complete if closing handles is constant time.} \exceptionmodelstd \qexample{filedir_example} */ BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC std::vector> close(const std::vector> &ops) BOOST_AFIO_HEADERS_ONLY_VIRTUAL_UNDEFINED_SPEC /*! \brief Schedule a batch of asynchronous data reads after preceding operations, where offset and total data read must not exceed the present file size. \docs_read \direct_io_note \return A batch of op handles. \tparam "class T" Any type. \param ops A batch of io_req structures. \ingroup read \qbk{distinguish, batch} \complexity{Amortised O(N) to dispatch. Amortised O(N/threadpool) to complete if reading data is constant time.} \exceptionmodelstd \qexample{readwrite_example} */ #ifndef DOXYGEN_SHOULD_SKIP_THIS BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC std::vector> read(const std::vector> &ops) BOOST_AFIO_HEADERS_ONLY_VIRTUAL_UNDEFINED_SPEC template inline std::vector> read(const std::vector> &ops); #else template BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC std::vector> read(const std::vector> &ops) BOOST_AFIO_HEADERS_ONLY_VIRTUAL_UNDEFINED_SPEC #endif /*! \brief Schedule a batch of asynchronous data writes after preceding operations, where offset and total data written must not exceed the present file size. \docs_write \direct_io_note \return A batch of op handles. \tparam "class T" Any type. \param ops A batch of io_req structures. \ingroup write \qbk{distinguish, batch} \complexity{Amortised O(N) to dispatch. Amortised O(N/threadpool) to complete if writing data is constant time.} \exceptionmodelstd \qexample{readwrite_example} */ #ifndef DOXYGEN_SHOULD_SKIP_THIS BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC std::vector> write(const std::vector> &ops) BOOST_AFIO_HEADERS_ONLY_VIRTUAL_UNDEFINED_SPEC template inline std::vector> write(const std::vector> &ops); #else template BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC std::vector> write(const std::vector> &ops) BOOST_AFIO_HEADERS_ONLY_VIRTUAL_UNDEFINED_SPEC #endif /*! \brief Schedule a batch of asynchronous file length truncations after preceding operations. \docs_truncate \return A batch of op handles. \param ops A batch of op handles. \param sizes A batch of new lengths. \ingroup truncate \qbk{distinguish, batch} \complexity{Amortised O(N) to dispatch. Amortised O(N/threadpool) to complete if truncating file lengths is constant time.} \exceptionmodelstd \qexample{readwrite_example} */ BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC std::vector> truncate(const std::vector> &ops, const std::vector &sizes) BOOST_AFIO_HEADERS_ONLY_VIRTUAL_UNDEFINED_SPEC /*! \brief Schedule a batch of asynchronous directory enumerations after preceding operations. \docs_enumerate \return A batch of stl_future vectors of directory entries with boolean returning false if done. \param reqs A batch of enumeration requests. \ingroup enumerate \qbk{distinguish, batch} \raceguarantees{ [raceguarantee FreeBSD, Linux, OS X..Race free per batch of up to ['maxitems] for ino and type only. Remember that many filing systems will recycle inodes such that a created file will get the inode of a just deleted file, so comparing inodes for equivalence to a direntry() won't help you.] [raceguarantee Windows..Race free per batch of up to ['maxitems] for ino, type, atim, mtim, ctim, size, allocated, birthtim, sparse, compressed.] } \complexity{Amortised O(N) to dispatch. Amortised O(N/threadpool*M) to complete where M is the average number of entries in each directory.} \exceptionmodelstd \qexample{enumerate_example} */ BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC std::vector, bool>>> enumerate(const std::vector &reqs) BOOST_AFIO_HEADERS_ONLY_VIRTUAL_UNDEFINED_SPEC /*! \brief Schedule a batch of asynchronous extent enumerations after preceding operations. \docs_extents \return A batch of stl_future vectors of extents. \param ops A batch of op handles. \ingroup extents \qbk{distinguish, batch} \raceguarantees{ [raceguarantee FreeBSD, Linux, OS X..Very racy, even individual extent offset and length can race. The following filters are applied before returning results: (i) Any extent whose end appears before its start is retried (ii) Sequences of contiguous extents are merged into single extents.] [raceguarantee Windows..Race free.] } \complexity{Amortised O(N) to dispatch. Amortised O(N/threadpool*M) to complete where M is the average number of extents in each file.} \exceptionmodelstd \qexample{extents_example} */ BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC std::vector>>> extents(const std::vector> &ops) BOOST_AFIO_HEADERS_ONLY_VIRTUAL_UNDEFINED_SPEC /*! \brief Schedule a batch of asynchronous volume enumerations after preceding operations. \docs_statfs \return A batch of stl_future volume metadatas. \param ops A batch of op handles. \param reqs A batch of metadata requests. \ingroup statfs \qbk{distinguish, batch} \raceguarantees{ [raceguarantee FreeBSD, OS X..Race free.] [raceguarantee Linux..The following items are fetched in a single snapshot: bsize, iosize, blocks, bfree, bavail, files, ffree, namemax, fsid, flags.rdonly, flags.noexec, flags.nosuid.] [raceguarantee Windows..The following snapshot categories apply: (i) flags, namemax, fstypename (ii) bsize, blocks, bfree, bavail. Everything else is fetched separately.] } \complexity{Amortised O(N) to dispatch. Amortised O(N/threadpool*M) to complete where M is the average number of entries in each directory.} \exceptionmodelstd \qexample{statfs_example} */ BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC std::vector> statfs(const std::vector> &ops, const std::vector &reqs) BOOST_AFIO_HEADERS_ONLY_VIRTUAL_UNDEFINED_SPEC #ifndef DOXYGEN_SHOULD_SKIP_THIS inline future<> adopt(handle_ptr h); inline future<> dir(const path_req &req); inline future<> rmdir(const path_req &req); inline future<> file(const path_req &req); inline future<> rmfile(const path_req &req); inline future<> symlink(const path_req &req, const future<> &target=future<>()); inline future<> rmsymlink(const path_req &req); inline future<> sync(const future<> &req); inline future<> zero(const future<> &req, const std::vector> &ranges); inline future<> close(const future<> &req); inline future<> read(const detail::io_req_impl &req); inline future<> write(const detail::io_req_impl &req); inline future<> truncate(const future<> &op, off_t newsize); inline future, bool>> enumerate(const enumerate_req &req); inline future>> extents(const future<> &op); inline future statfs(const future<> &op, const fs_metadata_flags &req); // Undocumented deliberately BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC std::vector> lock(const std::vector &req) BOOST_AFIO_HEADERS_ONLY_VIRTUAL_UNDEFINED_SPEC /*! \brief Schedule an asynchronous synchronisation of preceding operations. \deprecate{This function will be eliminated after lightweight future-promises are merged as one simply calls when_all_p() on the futures.} If you perform many asynchronous operations of unequal duration but wish to schedule one of more operations to occur only after \b all of those operations have completed, this is the correct function to use. The returned batch of ops exactly match the input batch of ops (including their exception states), but they will only complete when the last of the input batch of ops completes. \note If an input op is in an exceptioned state at the point of entry into this function, this function will propagate the exception there and then. \em Only error states which occur \em after this function has been scheduled are propagated into the output set of ops. \return A batch of op handles. \param ops A batch of op handles. \ingroup dispatcher__barrier \qbk{distinguish, batch} \complexity{Amortised O(N) to dispatch. Amortised O(N) to complete.} \exceptionmodel{See detailed description above.} \qexample{barrier_example} */ BOOST_AFIO_HEADERS_ONLY_MEMFUNC_SPEC std::vector> barrier(const std::vector> &ops); #endif /*! \brief Schedule the return of an op handle after another op handle completes. This is useful when you need to supply one op handle to a function but it must not begin until another op handle has finished. \return The op handle op. \param precondition The op handle which must complete for op to be passed through. \param op The op handle to return. \ingroup dispatcher__depends \complexity{Amortised O(1) to dispatch. Amortised O(1) to complete.} \exceptionmodelstd \qexample{filecopy_example} */ inline future<> depends(future<> precondition, future<> op); /*! \brief Completes an operation with a handle or an error, usually used when an operation was previously deferred. \ingroup dispatcher__misc \qbk{distinguish, normal} \complexity{O(N) where N is the number of completions dependent on this op.} \exceptionmodel{Should not throw any exception except for out of memory.} */ BOOST_AFIO_HEADERS_ONLY_MEMFUNC_SPEC void complete_async_op(size_t id, handle_ptr h, exception_ptr e=exception_ptr()); /*! \brief Completes an operation with an error, usually used when an operation was previously deferred. \ingroup dispatcher__misc \qbk{distinguish, errored} \complexity{O(N) where N is the number of completions dependent on this op.} \exceptionmodel{Should not throw any exception except for out of memory.} */ void complete_async_op(size_t id, exception_ptr e) { complete_async_op(id, handle_ptr(), e); } protected: template BOOST_AFIO_HEADERS_ONLY_MEMFUNC_SPEC handle_ptr int_get_handle_to_containing_dir(F *parent, size_t id, path_req req, completion_returntype(F::*dofile)(size_t, future<>, path_req)); BOOST_AFIO_HEADERS_ONLY_MEMFUNC_SPEC completion_returntype invoke_user_completion_fast(size_t id, future<> h, completion_t *callback); BOOST_AFIO_HEADERS_ONLY_MEMFUNC_SPEC completion_returntype invoke_user_completion_slow(size_t id, future<> h, std::function callback); template BOOST_AFIO_HEADERS_ONLY_MEMFUNC_SPEC std::vector> chain_async_ops(int optype, const std::vector> &preconditions, async_op_flags flags, completion_returntype(F::*f)(size_t, future<>, future<>)); template BOOST_AFIO_HEADERS_ONLY_MEMFUNC_SPEC std::vector> chain_async_ops(int optype, const std::vector> &preconditions, const std::vector &container, async_op_flags flags, completion_returntype(F::*f)(size_t, future<>, T)); template BOOST_AFIO_HEADERS_ONLY_MEMFUNC_SPEC std::vector> chain_async_ops(int optype, const std::vector> &preconditions, async_op_flags flags, completion_returntype(F::*f)(size_t, future<>, std::shared_ptr>)); template BOOST_AFIO_HEADERS_ONLY_MEMFUNC_SPEC std::vector> chain_async_ops(int optype, const std::vector> &preconditions, const std::vector &container, async_op_flags flags, completion_returntype(F::*f)(size_t, future<>, T, std::shared_ptr>)); template BOOST_AFIO_HEADERS_ONLY_MEMFUNC_SPEC std::vector> chain_async_ops(int optype, const std::vector &container, async_op_flags flags, completion_returntype(F::*f)(size_t, future<>, T)); template BOOST_AFIO_HEADERS_ONLY_MEMFUNC_SPEC std::vector> chain_async_ops(int optype, const std::vector &container, async_op_flags flags, completion_returntype(F::*f)(size_t, future<>, T, std::shared_ptr>)); template BOOST_AFIO_HEADERS_ONLY_MEMFUNC_SPEC dispatcher::completion_returntype dobarrier(size_t id, future<> h, T); template BOOST_AFIO_HEADERS_ONLY_MEMFUNC_SPEC handle_ptr invoke_async_op_completions(size_t id, future<> h, completion_returntype(F::*f)(size_t, future<>, Args...), Args... args); template BOOST_AFIO_HEADERS_ONLY_MEMFUNC_SPEC future<> chain_async_op(detail::immediate_async_ops &immediates, int optype, const future<> &precondition, async_op_flags flags, completion_returntype(F::*f)(size_t, future<>, Args...), Args... args); }; /*! \brief Instatiates the best available async_file_io_dispatcher implementation for this system for the given uri. Note that the number of threads in the threadpool supplied is the maximum non-async op queue depth (e.g. file opens, closes etc.). For fast SSDs, there isn't much gain after eight-sixteen threads, so the process threadpool is set to eight by default. For slow hard drives, or worse, SANs, a queue depth of 64 or higher might deliver significant benefits. URIs currently supported by AFIO: - `__fileurl__` The dispatcher will refer to the local filesystem of this machine. \return A shared_ptr to the best available async_file_io_dispatcher implementation for this system for the given uri. \param uri Where to open the dispatcher upon. \param flagsforce The flags to bitwise OR with any opened file flags. Used to force on certain flags. \param flagsmask The flags to bitwise AND with any opened file flags. Used to force off certain flags. \param threadpool The threadpool instance to use for asynchronous dispatch. \ingroup async_file_io_dispatcher \qbk{ [heading Example] [call_example] } */ #ifdef DOXYGEN_SHOULD_SKIP_THIS BOOST_AFIO_HEADERS_ONLY_FUNC_SPEC outcome make_dispatcher(std::string uri="file : / / /", file_flags flagsforce = file_flags::none, file_flags flagsmask = file_flags::none, std::shared_ptr threadpool = process_threadpool()) noexcept; #else BOOST_AFIO_HEADERS_ONLY_FUNC_SPEC outcome make_dispatcher(std::string uri="file:///", file_flags flagsforce=file_flags::none, file_flags flagsmask=file_flags::none, std::shared_ptr threadpool = process_threadpool()) noexcept; #endif namespace detail { struct when_all_state : std::enable_shared_from_this { promise> out; std::vector> in; }; template inline void when_all_ops_do(std::shared_ptr state) { // If we're on Boost.Thread, coalesce all wait ops into a single #if BOOST_AFIO_USE_BOOST_THREAD boost::wait_for_all(state->in.begin(), state->in.end()); #endif std::vector ret; ret.reserve(state->in.size()); for(auto &i: state->in) { auto e(get_exception_ptr(i)); if(e) { if(rethrow) { state->out.set_exception(e); return; } ret.push_back(handle_ptr()); } else ret.push_back(i.get()); } state->out.set_value(ret); } template inline stl_future> when_all_ops(Iterator first, Iterator last) { auto state=std::make_shared(); state->in.reserve(std::distance(first, last)); for(; first!=last; ++first) state->in.push_back(first->_h); auto ret=state->out.get_future(); process_threadpool()->enqueue([BOOST_AFIO_LAMBDA_MOVE_CAPTURE(state)] { when_all_ops_do(std::move(state)); }); return ret; } struct when_any_state : std::enable_shared_from_this { atomic count; promise out; std::vector> in; when_any_state() : count(0) { } }; #if BOOST_AFIO_USE_BOOST_THREAD // Boost.Thread has wait_for_any() which lets us be more efficient here and wait directly on the futures template inline void when_any_ops_do(std::shared_ptr state) { auto &i=*boost::wait_for_any(state->in.begin(), state->in.end()); auto e(get_exception_ptr(i)); if(e) { if(rethrow) { state->out.set_exception(e); return; } state->out.set_value(handle_ptr()); } else state->out.set_value(i.get()); } template inline stl_future when_any_ops(Iterator first, Iterator last) { auto state=std::make_shared(); state->in.reserve(std::distance(first, last)); for(; first!=last; ++first) state->in.push_back(first->h); auto ret=state->out.get_future(); process_threadpool()->enqueue([BOOST_AFIO_LAMBDA_MOVE_CAPTURE(state)]{ when_any_ops_do(std::move(state)); }); return ret; } #else // Without wait_for_any, schedule a completion onto every op and the first to fire wins template inline std::pair when_any_ops_do(std::shared_ptr state, size_t idx, size_t id, future<> h) { auto &i=state->in[idx]; if(0==state->count.fetch_add(1, memory_order_relaxed)) // Will be zero exactly once { auto e(get_exception_ptr(i)); if(e) { if(rethrow) { state->out.set_exception(e); return std::make_pair(true, handle_ptr()); } state->out.set_value(handle_ptr()); } else state->out.set_value(i.get()); } return std::make_pair(true, handle_ptr()); } template inline stl_future when_any_ops(Iterator first, Iterator last) { auto state=std::make_shared(); auto dispatcher=first->parent(); std::vector> ops(first, last); state->in.reserve(ops.size()); for(auto &op : ops) state->in.push_back(op._h); auto ret=state->out.get_future(); typedef std::function ft; std::vector> completions; completions.reserve(ops.size()); for(size_t n=0; n, state, n, std::placeholders::_1, std::placeholders::_2))); dispatcher->completion(ops, completions); return ret; } #endif template struct select_when_ops_return_type { typedef stl_future> type; // when_all_p() }; template<> struct select_when_ops_return_type { typedef stl_future type; // when_any() }; template struct enable_if_async_op { //static_assert(std::is_same::value, "Not an iterator of future<>"); }; template struct enable_if_async_op> { typedef typename select_when_ops_return_type::type type; }; } /*! \brief Returns a result when all the supplied ops complete. Does not propagate exception states. \deprecate{This will be replaced with the latest Concurrency TS specification (which has changed since AFIO was first designed).} \return A stl_future vector of shared_ptr's to handle. \tparam "class Iterator" An iterator type. \param _ An instance of std::nothrow_t. \param first An iterator pointing to the first future<> to wait upon. \param last An iterator pointing after the last future<> to wait upon. \ingroup when_all_ops \qbk{distinguish, iterator batch of ops not exception propagating} \complexity{O(N).} \exceptionmodel{Non propagating} */ template inline typename detail::enable_if_async_op::type when_all_p(std::nothrow_t _, Iterator first, Iterator last) { if(first==last) return stl_future>(); return detail::when_all_ops(first, last); } /*! \brief Returns a result when any the supplied ops complete. Does not propagate exception states. \deprecate{This will be replaced with the latest Concurrency TS specification (which has changed since AFIO was first designed).} \return A stl_future vector of shared_ptr's to handle. \tparam "class Iterator" An iterator type. \param _ An instance of std::nothrow_t. \param first An iterator pointing to the first future<> to wait upon. \param last An iterator pointing after the last future<> to wait upon. \ingroup when_all_ops \qbk{distinguish, iterator batch of ops not exception propagating} \complexity{O(N).} \exceptionmodel{Non propagating} */ template inline typename detail::enable_if_async_op::type when_any(std::nothrow_t _, Iterator first, Iterator last) { if(first==last) return stl_future(); return detail::when_any_ops(first, last); } /*! \brief Returns a result when all the supplied ops complete. Does not propagate exception states. \deprecate{This will be replaced with the latest Concurrency TS specification (which has changed since AFIO was first designed).} \return A stl_future vector of shared_ptr's to handle. \param _ An instance of std::nothrow_t. \param ops A vector of the async_io_ops to wait upon. \ingroup when_all_ops \qbk{distinguish, vector batch of ops not exception propagating} \complexity{O(N).} \exceptionmodel{Non propagating} */ template inline stl_future> when_all_p(std::nothrow_t _, std::vector> ops) { if(ops.empty()) return stl_future>(); return detail::when_all_ops(ops.begin(), ops.end()); } /*! \brief Returns a result when any the supplied ops complete. Does not propagate exception states. \deprecate{This will be replaced with the latest Concurrency TS specification (which has changed since AFIO was first designed).} \return A stl_future vector of shared_ptr's to handle. \param _ An instance of std::nothrow_t. \param ops A vector of the async_io_ops to wait upon. \ingroup when_all_ops \qbk{distinguish, vector batch of ops not exception propagating} \complexity{O(N).} \exceptionmodel{Non propagating} */ template inline stl_future when_any(std::nothrow_t _, std::vector> ops) { if(ops.empty()) return stl_future(); return detail::when_any_ops(ops.begin(), ops.end()); } /*! \brief Returns a result when all the supplied ops complete. Propagates exception states. \deprecate{This will be replaced with the latest Concurrency TS specification (which has changed since AFIO was first designed).} \return A stl_future vector of shared_ptr's to handle. \tparam "class Iterator" An iterator type. \param first An iterator pointing to the first future<> to wait upon. \param last An iterator pointing after the last future<> to wait upon. \ingroup when_all_ops \qbk{distinguish, iterator batch of ops exception propagating} \complexity{O(N).} \exceptionmodel{Propagating} */ template inline typename detail::enable_if_async_op::type when_all_p(Iterator first, Iterator last) { if(first==last) return stl_future>(); return detail::when_all_ops(first, last); } /*! \brief Returns a result when any the supplied ops complete. Propagates exception states. \deprecate{This will be replaced with the latest Concurrency TS specification (which has changed since AFIO was first designed).} \return A stl_future vector of shared_ptr's to handle. \tparam "class Iterator" An iterator type. \param first An iterator pointing to the first future<> to wait upon. \param last An iterator pointing after the last future<> to wait upon. \ingroup when_all_ops \qbk{distinguish, iterator batch of ops exception propagating} \complexity{O(N).} \exceptionmodel{Propagating} */ template inline typename detail::enable_if_async_op::type when_any(Iterator first, Iterator last) { if(first==last) return stl_future(); return detail::when_any_ops(first, last); } /*! \brief Returns a result when all the supplied ops complete. Propagates exception states. \deprecate{This will be replaced with the latest Concurrency TS specification (which has changed since AFIO was first designed).} \return A stl_future vector of shared_ptr's to handle. \param ops A vector of the async_io_ops to wait upon. \ingroup when_all_ops \qbk{distinguish, vector batch of ops exception propagating} \complexity{O(N).} \exceptionmodel{Propagating} */ template inline stl_future> when_all_p(std::vector> ops) { if(ops.empty()) return stl_future>(); return detail::when_all_ops(ops.begin(), ops.end()); } /*! \brief Returns a result when any the supplied ops complete. Propagates exception states. \deprecate{This will be replaced with the latest Concurrency TS specification (which has changed since AFIO was first designed).} \return A stl_future vector of shared_ptr's to handle. \param ops A vector of the async_io_ops to wait upon. \ingroup when_all_ops \qbk{distinguish, vector batch of ops exception propagating} \complexity{O(N).} \exceptionmodel{Propagating} */ template inline stl_future when_any(std::vector> ops) { if(ops.empty()) return stl_future(); return detail::when_any_ops(ops.begin(), ops.end()); } /*! \brief Returns a result when the supplied op completes. Does not propagate exception states. \deprecate{This will be replaced with the latest Concurrency TS specification (which has changed since AFIO was first designed).} \return A stl_future vector of shared_ptr's to handle. \param _ An instance of std::nothrow_t. \param op An future<> to wait upon. \ingroup when_all_ops \qbk{distinguish, convenience single op not exception propagating} \complexity{O(1).} \exceptionmodel{Non propagating} */ template inline stl_future> when_all_p(std::nothrow_t _, future op) { std::vector> ops(1, op); return when_all_p(_, ops); } /*! \brief Returns a result when the supplied op completes. Propagates exception states. \deprecate{This will be replaced with the latest Concurrency TS specification (which has changed since AFIO was first designed).} \return A stl_future vector of shared_ptr's to handle. \param ops A sequence of future<> to wait upon. \ingroup when_all_ops \qbk{distinguish, convenience multiple op exception propagating} \complexity{O(N).} \exceptionmodel{Propagating} */ template inline stl_future> when_all_p(future &... ops) { std::vector> _ops = { std::forward &>(ops)... }; return when_all_p(_ops); } /*! \struct path_req \brief A convenience bundle of path and flags, with optional precondition. Paths may be a path fragment (relative to the precondition) or absolute, in which case if necessary they are made canonical and absolute in the constructor according to the current working directory. \qbk{ [include generated/struct_path_req_1_1absolute.qbk] [include generated/struct_path_req_1_1relative.qbk] } */ struct path_req { bool is_relative; //!< Whether the precondition is also where this path begins BOOST_AFIO_V2_NAMESPACE::path path; //!< The filing system path to be used for this operation file_flags flags; //!< The flags to be used for this operation (note they can be overriden by flags passed during dispatcher construction). future<> precondition; //!< An optional precondition for this operation //! \brief Tags the path as being absolute struct absolute; //! \brief Tags the path as being relative struct relative; //! \constr path_req() : is_relative(false), flags(file_flags::none) { } //! \cconstr path_req(const path_req &o) = default; //! \mconstr path_req(path_req &&o) noexcept : is_relative(o.is_relative), path(std::move(o.path)), flags(std::move(o.flags)), precondition(std::move(o.precondition)) { } //! \mconstr inline path_req(absolute &&o); //! \mconstr inline path_req(relative &&o); /*! \brief Constructs an instance. \tparam "class T" The type of path to be used. \param _path The filing system path to be used. \param _flags The flags to be used. */ template::value && !std::is_constructible, T>::value>::type> path_req(T &&_path, file_flags _flags=file_flags::none) : is_relative(false), path(BOOST_AFIO_V2_NAMESPACE::path::make_absolute(std::forward(_path))), flags(_flags) { } /*! \brief Constructs an instance. \tparam "class T" The type of path to be used. \param _is_relative Whether the precondition is where the path begins \param _precondition The precondition for this operation. \param _path The filing system path to be used. \param _flags The flags to be used. */ template::value>::type> path_req(bool _is_relative, future<> _precondition, T &&_path, file_flags _flags=file_flags::none) : is_relative(_is_relative), path(_is_relative ? BOOST_AFIO_V2_NAMESPACE::path(std::forward(_path)) : BOOST_AFIO_V2_NAMESPACE::path(BOOST_AFIO_V2_NAMESPACE::path::make_absolute(std::forward(_path)))), flags(_flags), precondition(std::move(_precondition)) { _validate(); } //! \overload path_req(bool _is_relative, future<> _precondition, BOOST_AFIO_V2_NAMESPACE::path _path, file_flags _flags=file_flags::none) : is_relative(_is_relative), path(std::move(_path)), flags(_flags), precondition(std::move(_precondition)) { _validate(); } /*! \brief Constructs an instance. \param _precondition The precondition for this operation (used as the path). \param _flags The flags to be used. */ path_req(future<> _precondition, file_flags _flags=file_flags::none) : is_relative(true), flags(_flags), precondition(std::move(_precondition)) { _validate(); } //! Validates contents bool validate() const { if(!is_relative && path.empty()) return false; return !precondition.valid() || precondition.validate(); } protected: void _validate() const { #if BOOST_AFIO_VALIDATE_INPUTS if(!validate()) BOOST_AFIO_THROW(std::invalid_argument("Inputs are invalid.")); #endif } }; //! Convenience tag type constructing a relative path path_req struct path_req::relative : path_req { /*! \brief Constructs an instance. \tparam "class T" The type of path to be used. \param _precondition The precondition for this operation. \param _path The filing system path to be used. \param _flags The flags to be used. */ template relative(future<> _precondition, T &&_path, file_flags _flags=file_flags::none) : path_req(true, std::move(_precondition), std::forward(_path), _flags) { _validate(); } /*! \brief Constructs an instance. \param _precondition The precondition for this operation. \param _flags The flags to be used. */ relative(future<> _precondition, file_flags _flags=file_flags::none) : path_req(std::move(_precondition), _flags) { _validate(); } }; //! Convenience tag type constructing an absolute path path_req struct path_req::absolute : path_req { /*! \brief Constructs an instance. \tparam "class T" The type of path to be used. \param _precondition The precondition for this operation. \param _path The filing system path to be used. \param _flags The flags to be used. */ template absolute(future<> _precondition, T &&_path, file_flags _flags=file_flags::none) : path_req(false, std::move(_precondition), std::move(BOOST_AFIO_V2_NAMESPACE::path::make_absolute(std::forward(_path))), _flags) { _validate(); } }; inline path_req::path_req(path_req::absolute &&o) : is_relative(o.is_relative), path(std::move(o.path)), flags(std::move(o.flags)), precondition(std::move(o.precondition)) { } inline path_req::path_req(path_req::relative &&o) : is_relative(o.is_relative), path(std::move(o.path)), flags(std::move(o.flags)), precondition(std::move(o.precondition)) { } /*! \defgroup to_asio_buffers Overloadable free functions converting the types passed to io_req<> into an asio buffer sequence for read() and write(). You can add your own free function overloads to tell AFIO how to convert your custom types into ASIO scatter gather buffers. Note that const types must convert into asio::const_buffer, and non-const types must convert into asio::mutable_buffer. It is entirely acceptable for types to allow writing (const) only and not reading. Default overloads provided are as follows: - Any trivial type T * and number of items. - void * and const void * with number of bytes. - C array types are treated as if a std::array. - asio::const_buffer and asio::mutable_buffer are passed through as-is. - Any container type holding a trivial type T. This includes std::basic_string (write only), std::vector and std::array, all three of which are specially collapsed into a single scatter gather as they guarantee storing their contents contiguously. - Any container type holding any of the above, including other containers. These will be converted into scatter gather lists for you. Note that the constness of the type returned by the container's iterator is respected, so if the container iterator returns a const reference (e.g. std::basic_string) then you cannot gather read into that container, and instead should receive a compile time error. */ template inline std::vector to_asio_buffers(T &v); template inline std::vector to_asio_buffers(const T &v); template inline std::vector to_asio_buffers(T (&v)[N]); template inline std::vector to_asio_buffers(const T (&v)[N]); /*! \brief Passing through asio::mutable_buffer \return A vector of ASIO buffers \ingroup to_asio_buffers \qbk{distinguish, asio mutable_buffer} */ inline std::vector to_asio_buffers(asio::mutable_buffer &v) { return std::vector(1, v); } /*! \brief Passing through asio::const_buffer \return A vector of ASIO buffers \ingroup to_asio_buffers \qbk{distinguish, asio const_buffer} */ inline std::vector to_asio_buffers(asio::const_buffer &v) { return std::vector(1, v); } /*! \brief A buffer at v sized length*sizeof(T) \tparam "class T" Any trivial type T \return A vector of ASIO buffers \ingroup to_asio_buffers \qbk{distinguish, buffer of T} */ template inline std::vector to_asio_buffers(T *v, size_t length) { static_assert(std::is_trivial::value, "to_asio_buffers has not been specialised for this non-trivial type, which suggests you are trying to read or write a complex C++ type! Either add a custom specialisation, or directly instantiate an io_req with a void * and size_t length to some serialised representation."); return std::vector(1, asio::mutable_buffer((void *) v, length*sizeof(T))); } /*! \brief A buffer at v sized length*sizeof(T) \tparam "class T" Any trivial type T \return A vector of ASIO buffers \ingroup to_asio_buffers \qbk{distinguish, const buffer of T} */ template inline std::vector to_asio_buffers(const T *v, size_t length) { static_assert(std::is_trivial::value, "to_asio_buffers has not been specialised for this non-trivial type, which suggests you are trying to read or write a complex C++ type! Either add a custom specialisation, or directly instantiate an io_req with a void * and size_t length to some serialised representation."); return std::vector(1, asio::const_buffer((void *) v, length*sizeof(T))); } /*! \brief A buffer at v sized length \return A vector of ASIO buffers \ingroup to_asio_buffers \qbk{distinguish, buffer} */ inline std::vector to_asio_buffers(void *v, size_t length) { return std::vector(1, asio::mutable_buffer(v, length)); } /*! \brief A buffer at v sized length \return A vector of ASIO buffers \ingroup to_asio_buffers \qbk{distinguish, const buffer of T} */ inline std::vector to_asio_buffers(const void *v, size_t length) { return std::vector(1, asio::const_buffer(v, length)); } namespace detail { // Length deducing asio buffer conversions template::value, bool is_container=is_container::value> struct to_asio_buffers_helper { template std::vector operator()(U &v) const { static_assert(!std::is_same::value, "to_asio_buffers(T) called with type T which is neither trivial nor a container. Did you mean to call io_req with a void * and a byte length, or do you need to overload to_asio_buffers()?"); static_assert(!std::is_same::value || !is_const, "This type is const, so you cannot generate an asio::mutable_buffer from it."); return std::vector(); } }; // Trivial types get sent as is template struct to_asio_buffers_helper { template std::vector operator()(U &v) const { static_assert(!std::is_same::value || !is_const, "This type is const, so you cannot generate an asio::mutable_buffer from it."); return std::vector(1, R(&v, sizeof(v))); } }; // Container types build a scatter gather list of their contents template::value, bool is_trivial=std::is_trivial::value> struct container_to_asio_buffers_helper { template std::vector operator()(U &v) const { static_assert(!std::is_same::value || !is_const, "This container only permits const access to its iterators, so you cannot generate an asio::mutable_buffer from it."); std::vector ret; for(auto &i : v) { std::vector item(to_asio_buffers(i)); ret.reserve(ret.size()+item.size()); ret.insert(ret.end(), std::make_move_iterator(item.begin()), std::make_move_iterator(item.end())); } return ret; } }; // Container specialisations where we know we can skip scatter gather template struct container_to_asio_buffers_helper, _Ct, is_const, true> { template std::vector operator()(U &v) const { static_assert(!std::is_same::value || !is_const, "This container only permits const access to its iterators, so you cannot generate an asio::mutable_buffer from it."); return std::vector(1, R(&v.front(), v.size()*sizeof(C))); } }; template struct container_to_asio_buffers_helper, _T, is_const, true> { template std::vector operator()(U &v) const { static_assert(!std::is_same::value || !is_const, "This container only permits const access to its iterators, so you cannot generate an asio::mutable_buffer from it."); return std::vector(1, R(v.data(), v.size()*sizeof(T))); } }; template struct container_to_asio_buffers_helper, _T, is_const, true> { template std::vector operator()(U &v) const { static_assert(!std::is_same::value || !is_const, "This container only permits const access to its iterators, so you cannot generate an asio::mutable_buffer from it."); std::vector ret(1, R(v.data(), v.size()*sizeof(T))); return ret; } }; template struct to_asio_buffers_helper : container_to_asio_buffers_helper::type> { }; // Pass through vectors and arrays of asio buffers template struct to_asio_buffers_helper, false, true> { template std::vector operator()(U &v) const { std::vector ret(v.begin(), v.end()); return ret; } }; template struct to_asio_buffers_helper, false, true> { template std::vector operator()(U &v) const { return v; } }; template struct to_asio_buffers_helper, false, true> { template std::vector operator()(U &v) const { std::vector ret(v.begin(), v.end()); return ret; } }; template struct to_asio_buffers_helper, false, true> { template std::vector operator()(U &v) const { return v; } }; template struct to_asio_buffers_helper, false, true> { template std::vector operator()(U &v) const { std::vector ret(v.begin(), v.end()); return ret; } }; template struct to_asio_buffers_helper, false, true> { template std::vector operator()(U &v) const { std::vector ret(v.begin(), v.end()); return ret; } }; } /*! \brief Any trivial type T or STL container. Trivial types turn into a buffer of &v sized sizeof(T). Container types have their value type deduced and to_asio_buffers() called on that value_type. Additional specialisations are provided for string, vector and array to collapse the scatter gather buffers into a single one for contiguous storage. \tparam "class T" Any trivial type T or STL container \return A vector of ASIO buffers \ingroup to_asio_buffers \qbk{distinguish, trivial and container types} */ template inline std::vector to_asio_buffers(T &v) { static_assert(!std::is_pointer::value, "You cannot assemble scatter gather buffers from raw pointers, you need to specify a length or supply a type carrying a length"); return detail::to_asio_buffers_helper()(v); } /*! \brief Any trivial type T or STL container. Trivial types turn into a buffer of &v sized sizeof(T). Container types have their value type deduced and to_asio_buffers() called on that value_type. Additional specialisations are provided for string, vector and array to collapse the scatter gather buffers into a single one for contiguous storage. \tparam "class T" Any trivial type T or STL container \return A vector of ASIO buffers \ingroup to_asio_buffers \qbk{distinguish, const trivial and container types} */ template inline std::vector to_asio_buffers(const T &v) { static_assert(!std::is_pointer::value, "You cannot assemble scatter gather buffers from raw pointers, you need to specify a length or supply a type carrying a length"); return detail::to_asio_buffers_helper()(v); } /*! \brief A buffer at v sized N*sizeof(T) \tparam "class T" Any trivial type T \return A vector of ASIO buffers \ingroup to_asio_buffers \qbk{distinguish, C arrays} */ template inline std::vector to_asio_buffers(T (&v)[N]) { return to_asio_buffers(reinterpret_cast &>(v)); } /*! \brief A buffer at v sized N*sizeof(T) \tparam "class T" Any trivial type T \return A vector of ASIO buffers \ingroup to_asio_buffers \qbk{distinguish, const C arrays} */ template inline std::vector to_asio_buffers(const T (&v)[N]) { return to_asio_buffers(reinterpret_cast &>(v)); } namespace detail { //! \brief The implementation of all io_req specialisations. \tparam for_writing Whether this implementation is for writing data. \ingroup io_req template class io_req_impl; template<> class io_req_impl { public: //! An optional precondition for this operation future<> precondition; //! A sequence of mutable Boost.ASIO buffers to read into std::vector buffers; //! The offset from which to read off_t where; //! \constr io_req_impl() { } //! \cconstr io_req_impl(const io_req_impl &o) : precondition(o.precondition), buffers(o.buffers), where(o.where) { } //! \mconstr io_req_impl(io_req_impl &&o) noexcept : precondition(std::move(o.precondition)), buffers(std::move(o.buffers)), where(std::move(o.where)) { } //! \cassign io_req_impl &operator=(const io_req_impl &o) { precondition=o.precondition; buffers=o.buffers; where=o.where; return *this; } //! \massign io_req_impl &operator=(io_req_impl &&o) noexcept { precondition=std::move(o.precondition); buffers=std::move(o.buffers); where=std::move(o.where); return *this; } //! \io_req2 io_req_impl(future<> _precondition, std::vector _buffers, off_t _where) : precondition(std::move(_precondition)), buffers(std::move(_buffers)), where(_where) { _validate(); } //! Validates contents for correctness \return True if contents are correct bool validate() const { //if(!precondition.validate()) return false; if(buffers.empty()) return false; for(auto &b: buffers) { if(!asio::buffer_cast(b) || !asio::buffer_size(b)) return false; if(precondition.parent() && !!(precondition.parent()->fileflags(file_flags::none)&file_flags::os_direct)) { if(((size_t) asio::buffer_cast(b) & 4095) || (asio::buffer_size(b) & 4095)) return false; } } return true; } private: void _validate() const { #if BOOST_AFIO_VALIDATE_INPUTS if(!validate()) BOOST_AFIO_THROW(std::invalid_argument("Inputs are invalid.")); #endif } }; template<> class io_req_impl { public: //! An optional precondition for this operation future<> precondition; //! A sequence of mutable Boost.ASIO buffers to read into std::vector buffers; //! The offset from which to read off_t where; //! \constr io_req_impl() { } //! \cconstr io_req_impl(const io_req_impl &o) : precondition(o.precondition), buffers(o.buffers), where(o.where) { } //! \mconstr io_req_impl(io_req_impl &&o) noexcept : precondition(std::move(o.precondition)), buffers(std::move(o.buffers)), where(std::move(o.where)) { } //! \cconstr io_req_impl(const io_req_impl &o) : precondition(o.precondition), where(o.where) { buffers.reserve(o.buffers.capacity()); for(auto &i: o.buffers){ buffers.push_back(i); } } //! \mconstr io_req_impl(io_req_impl &&o) noexcept : precondition(std::move(o.precondition)), where(std::move(o.where)) { buffers.reserve(o.buffers.capacity()); for(auto &&i: o.buffers){ buffers.push_back(std::move(i)); } } //! \cassign io_req_impl &operator=(const io_req_impl &o) { precondition=o.precondition; buffers=o.buffers; where=o.where; return *this; } //! \massign io_req_impl &operator=(io_req_impl &&o) noexcept { precondition=std::move(o.precondition); buffers=std::move(o.buffers); where=std::move(o.where); return *this; } //! \io_req2 io_req_impl(future<> _precondition, std::vector _buffers, off_t _where) : precondition(std::move(_precondition)), buffers(std::move(_buffers)), where(_where) { _validate(); } //! \io_req2 io_req_impl(future<> _precondition, std::vector _buffers, off_t _where) : precondition(std::move(_precondition)), where(_where) { buffers.reserve(_buffers.capacity()); for(auto &&i: _buffers) buffers.push_back(std::move(i)); _validate(); } //! Validates contents for correctness \return True if contents are correct bool validate() const { //if(!precondition.validate()) return false; if(buffers.empty()) return false; for(auto &b: buffers) { if(!asio::buffer_cast(b) || !asio::buffer_size(b)) return false; if(precondition.parent() && !!(precondition.parent()->fileflags(file_flags::none)&file_flags::os_direct)) { if(((size_t) asio::buffer_cast(b) & 4095) || (asio::buffer_size(b) & 4095)) return false; } } return true; } private: void _validate() const { #if BOOST_AFIO_VALIDATE_INPUTS if(!validate()) BOOST_AFIO_THROW(std::invalid_argument("Inputs are invalid.")); #endif } }; } /*! \struct io_req \brief A convenience bundle of precondition, data and where for reading into a T as specified by its to_asio_buffers() overload. Data \b MUST stay around until the operation completes. \tparam "class T" Any readable (if const) or writable (if non-const) type T as specified by its to_asio_buffers() overload. \ingroup io_req */ template struct io_req : public detail::io_req_impl { #ifdef DOXYGEN_SHOULD_SKIP_THIS //! A precondition containing an open file handle for this operation future<> precondition; //! A sequence of mutable Boost.ASIO buffers to read into std::vector buffers; //! The offset from which to read off_t where; #endif //! \constr io_req() { } //! \cconstr io_req(const io_req &o) : detail::io_req_impl(o) { } //! \mconstr io_req(io_req &&o) noexcept : detail::io_req_impl(std::move(o)) { } //! \cassign io_req &operator=(const io_req &o) { static_cast>(*this)=o; return *this; } //! \massign io_req &operator=(io_req &&o) noexcept { static_cast>(*this)=std::move(o); return *this; } //! \io_req1 \param _length The number of items to transfer io_req(future<> _precondition, T *v, size_t _length, off_t _where) : detail::io_req_impl(std::move(_precondition), to_asio_buffers(v, _length), _where) { } //! \io_req1 template io_req(future<> _precondition, U &v, off_t _where) : detail::io_req_impl(std::move(_precondition), to_asio_buffers(v), _where) { } //! \io_req1 \tparam N The number of items in the array template io_req(future<> _precondition, U (&v)[N], off_t _where) : detail::io_req_impl(std::move(_precondition), to_asio_buffers(v), _where) { } }; /*! \brief A convenience bundle of precondition, data and where for reading into a T as specified by its to_asio_buffers() overload. Data \b MUST stay around until the operation completes. \tparam "class T" Any readable (if const) or writable (if non-const) type T as specified by its to_asio_buffers() overload. \ingroup io_req */ template struct io_req : public detail::io_req_impl { #ifdef DOXYGEN_SHOULD_SKIP_THIS //! A precondition containing an open file handle for this operation future<> precondition; //! A sequence of const Boost.ASIO buffers to write from std::vector buffers; //! The offset at which to write off_t where; #endif //! \constr io_req() { } //! \cconstr io_req(const io_req &o) : detail::io_req_impl(o) { } //! \mconstr io_req(io_req &&o) noexcept : detail::io_req_impl(std::move(o)) { } //! \cconstr io_req(const io_req &o) : detail::io_req_impl(o) { } //! \mconstr io_req(io_req &&o) noexcept : detail::io_req_impl(std::move(o)) { } //! \cassign io_req &operator=(const io_req &o) { static_cast>(*this)=o; return *this; } //! \massign io_req &operator=(io_req &&o) noexcept { static_cast>(*this)=std::move(o); return *this; } //! \io_req1 \param _length The number of items to transfer io_req(future<> _precondition, const T *v, size_t _length, off_t _where) : detail::io_req_impl(std::move(_precondition), to_asio_buffers(v, _length), _where) { } //! \io_req1 template io_req(future<> _precondition, const U &v, off_t _where) : detail::io_req_impl(std::move(_precondition), to_asio_buffers(v), _where) { } //! \io_req1 \tparam N The number of items in the array template io_req(future<> _precondition, const U (&v)[N], off_t _where) : detail::io_req_impl(std::move(_precondition), to_asio_buffers(v), _where) { } }; /*! \brief A convenience bundle of precondition, data and where for reading into a T as specified by its to_asio_buffers() overload. Data \b MUST stay around until the operation completes. \ingroup io_req */ template<> struct io_req : public detail::io_req_impl { #ifdef DOXYGEN_SHOULD_SKIP_THIS //! A precondition containing an open file handle for this operation future<> precondition; //! A sequence of mutable Boost.ASIO buffers to read into std::vector buffers; //! The offset from which to read off_t where; #endif //! \constr io_req() { } //! \cconstr io_req(const io_req &o) : detail::io_req_impl(o) { } //! \mconstr io_req(io_req &&o) noexcept : detail::io_req_impl(std::move(o)) { } //! \cassign io_req &operator=(const io_req &o) { static_cast>(*this)=o; return *this; } //! \massign io_req &operator=(io_req &&o) noexcept { static_cast>(*this)=std::move(o); return *this; } //! \io_req1 \param _length The number of items to transfer io_req(future<> _precondition, void *v, size_t _length, off_t _where) : detail::io_req_impl(std::move(_precondition), to_asio_buffers(v, _length), _where) { } }; /*! \brief A convenience bundle of precondition, data and where for reading into a T as specified by its to_asio_buffers() overload. Data \b MUST stay around until the operation completes. \ingroup io_req */ template<> struct io_req : public detail::io_req_impl { #ifdef DOXYGEN_SHOULD_SKIP_THIS //! A precondition containing an open file handle for this operation future<> precondition; //! A sequence of const Boost.ASIO buffers to write from std::vector buffers; //! The offset at which to write off_t where; #endif //! \constr io_req() { } //! \cconstr io_req(const io_req &o) : detail::io_req_impl(o) { } //! \mconstr io_req(io_req &&o) noexcept : detail::io_req_impl(std::move(o)) { } //! \cconstr io_req(const io_req &o) : detail::io_req_impl(o) { } //! \mconstr io_req(io_req &&o) noexcept : detail::io_req_impl(std::move(o)) { } //! \cassign io_req &operator=(const io_req &o) { static_cast>(*this)=o; return *this; } //! \massign io_req &operator=(io_req &&o) noexcept { static_cast>(*this)=std::move(o); return *this; } //! \io_req1 \param _length The number of items to transfer io_req(future<> _precondition, const void *v, size_t _length, off_t _where) : detail::io_req_impl(std::move(_precondition), to_asio_buffers(v, _length), _where) { } }; namespace detail { template::value> struct make_io_req { typedef typename std::remove_pointer::type>::type _T; typedef io_req<_T> type; template type operator()(future<> _precondition, U &&v, off_t _where) const { return type(std::move(_precondition), std::forward(v), _where); } template type operator()(future<> _precondition, U &&v, size_t _length, off_t _where) const { return type(std::move(_precondition), std::forward(v), _length, _where); } }; // If T is a container and that container's value_type is const, make sure we only create an asio::const_buffer template struct make_io_req { typedef typename detail::is_container::type container_value_type; static BOOST_CONSTEXPR_OR_CONST bool is_container_contents_const=std::is_const::value || std::is_base_of::value; typedef typename std::remove_pointer::type>::type __T; typedef typename std::conditional::type, __T>::type _T; typedef io_req<_T> type; template type operator()(future<> _precondition, U &&v, off_t _where) const { return type(std::move(_precondition), std::forward(v), _where); } template type operator()(future<> _precondition, U &&v, size_t _length, off_t _where) const { return type(std::move(_precondition), std::forward(v), _length, _where); } }; } /*! \brief Convenience instantiator of a io_req, letting the compiler deduce the template specialisation to use. \return An io_req matching the supplied parameter type. \io_req1 \ingroup make_io_req \qbk{distinguish, length deducing} \qbk{ [heading Example] [readwrite_example] } */ template inline auto make_io_req(future<> _precondition, T &&v, off_t _where) -> decltype(detail::make_io_req()(std::move(_precondition), std::forward(v), _where)) { return detail::make_io_req()(std::move(_precondition), std::forward(v), _where); } /*! \brief Convenience instantiator of a io_req, letting the compiler deduce the template specialisation to use. \return An io_req matching the supplied parameter type. \io_req1 \ingroup make_io_req \qbk{distinguish, length deducing} \qbk{ [heading Example] [readwrite_example] } */ template inline io_req> make_io_req(future<> _precondition, const std::initializer_list &v, off_t _where) { return io_req>(std::move(_precondition), v, _where); } /*! \brief Convenience instantiator of a io_req, letting the compiler deduce the template specialisation to use. \return An io_req matching the supplied parameter type. \io_req1 \param _length The number of bytes to transfer \ingroup make_io_req \qbk{distinguish, length specifying} \qbk{ [heading Example] [readwrite_example] } */ template inline auto make_io_req(future<> _precondition, T &&v, size_t _length, off_t _where) -> decltype(detail::make_io_req()(std::move(_precondition), std::forward(v), _length, _where)) { return detail::make_io_req()(std::move(_precondition), std::forward(v), _length, _where); } /*! \struct enumerate_req \brief A convenience bundle of precondition, number of items to enumerate, item pattern match and metadata to prefetch. You should note that shell globs must use a restricted form for portability: * Microsoft Windows NT oddly does not specify what wildcards are permitted, but I think the documentation for the kernel function FsRtlIsNameInExpression() is probably sound: * means zero or more characters, ? means any one character. Do not use <, > or " as these have special MS-DOS compatibility inducing consequences. Do not use ^ as this is the Windows wildcard escape character. * POSIX further extends NT's wildcards with \\[seq\\] which is a subset of characters and \\[!seq\\] which is not any subset of characters. Here a \\ is the wildcard escape character. */ struct enumerate_req { future<> precondition; //!< A precondition for this operation. size_t maxitems; //!< The maximum number of items to return in this request. Note that setting to one will often invoke two syscalls. bool restart; //!< Restarts the enumeration for this open directory handle. path glob; //!< An optional shell glob by which to filter the items returned. Done kernel side on Windows, user side on POSIX. metadata_flags metadata; //!< The metadata to prefetch for each item enumerated. AFIO may fetch more metadata than requested if it is cost free. //! How to do deleted file elimination on Windows enum class filter { none, //!< Do no filtering at all fastdeleted //!< Filter out AFIO deleted files based on their filename (fast and fairly reliable) }; filter filtering; //!< Any filtering you want AFIO to do for you. //! \constr enumerate_req() : maxitems(0), restart(false), metadata(metadata_flags::None), filtering(filter::fastdeleted) { } /*! \brief Constructs an instance. \param _precondition The precondition for this operation. \param _maxitems The maximum number of items to return in this request. Note that setting to one will often invoke two syscalls. \param _restart Restarts the enumeration for this open directory handle. \param _glob An optional shell glob by which to filter the items returned. Done kernel side on Windows, user side on POSIX. \param _metadata The metadata to prefetch for each item enumerated. AFIO may fetch more metadata than requested if it is cost free. \param _filtering Any filtering you want AFIO to do for you. */ enumerate_req(future<> _precondition, size_t _maxitems=2, bool _restart=true, path _glob=path(), metadata_flags _metadata=metadata_flags::None, filter _filtering=filter::fastdeleted) : precondition(std::move(_precondition)), maxitems(_maxitems), restart(_restart), glob(std::move(_glob)), metadata(_metadata), filtering(_filtering) { _validate(); } /*! \brief Constructs an instance. \param _precondition The precondition for this operation. \param _glob A shell glob by which to filter the items returned. Done kernel side on Windows, user side on POSIX. \param _maxitems The maximum number of items to return in this request. Note that setting to one will often invoke two syscalls. \param _restart Restarts the enumeration for this open directory handle. \param _metadata The metadata to prefetch for each item enumerated. AFIO may fetch more metadata than requested if it is cost free. \param _filtering Any filtering you want AFIO to do for you. */ enumerate_req(future<> _precondition, path _glob, size_t _maxitems=2, bool _restart=true, metadata_flags _metadata=metadata_flags::None, filter _filtering=filter::fastdeleted) : precondition(std::move(_precondition)), maxitems(_maxitems), restart(_restart), glob(std::move(_glob)), metadata(_metadata), filtering(_filtering) { _validate(); } /*! \brief Constructs an instance. \param _precondition The precondition for this operation. \param _metadata The metadata to prefetch for each item enumerated. AFIO may fetch more metadata than requested if it is cost free. \param _maxitems The maximum number of items to return in this request. Note that setting to one will often invoke two syscalls. \param _restart Restarts the enumeration for this open directory handle. \param _glob An optional shell glob by which to filter the items returned. Done kernel side on Windows, user side on POSIX. \param _filtering Any filtering you want AFIO to do for you. */ enumerate_req(future<> _precondition, metadata_flags _metadata, size_t _maxitems=2, bool _restart=true, path _glob=path(), filter _filtering=filter::fastdeleted) : precondition(std::move(_precondition)), maxitems(_maxitems), restart(_restart), glob(std::move(_glob)), metadata(_metadata), filtering(_filtering) { _validate(); } //! Validates contents bool validate() const { if(!maxitems) return false; return !precondition.valid() || precondition.validate(); } private: void _validate() const { #if BOOST_AFIO_VALIDATE_INPUTS if(!validate()) BOOST_AFIO_THROW(std::invalid_argument("Inputs are invalid.")); #endif } }; // Undocumented deliberately struct lock_req { future<> precondition; enum class Type { unknown, read_lock, write_lock, unlock } type; off_t offset, length; //chrono::time_point deadline; lock_req() : type(Type::unknown), offset(0), length(0) { } lock_req(future<> _precondition, Type _type=Type::write_lock) : precondition(_precondition), type(_type), offset(0), length((off_t)-1) { _validate(); } lock_req(future<> _precondition, std::nullptr_t) : precondition(_precondition), type(Type::unlock), offset(0), length((off_t)-1) { _validate(); } lock_req(future<> _precondition, Type _type, off_t _offset, off_t _length) : precondition(_precondition), type(_type), offset(_offset), length(_length) { _validate(); } lock_req(future<> _precondition, off_t _offset, off_t _length, Type _type=Type::write_lock) : precondition(_precondition), type(_type), offset(_offset), length(_length) { _validate(); } //! Validates contents bool validate() const { if(type==Type::unknown) return false; if(offset+length struct async_file_io_dispatcher_rwconverter { typedef detail::io_req_impl return_type; const std::vector &operator()(const std::vector> &ops) const { typedef io_req reqT; static_assert(std::is_convertible::value, "io_req is not convertible to detail::io_req_impl"); static_assert(sizeof(return_type)==sizeof(reqT), "io_req does not have the same size as detail::io_req_impl"); return reinterpret_cast &>(ops); } }; } #if defined(BOOST_AFIO_ENABLE_BENCHMARKING_COMPLETION) // Only really used for benchmarking inline future<> dispatcher::completion(const future<> &req, const std::pair &callback) { std::vector> r; std::vector> i; r.reserve(1); i.reserve(1); r.push_back(req); i.push_back(callback); auto ret(std::move(completion(r, i).front())); return ret; } #endif inline future<> dispatcher::completion(const future<> &req, const std::pair> &callback) { std::vector> r; std::vector>> i; r.reserve(1); i.reserve(1); r.push_back(req); i.push_back(callback); auto ret(std::move(completion(r, i).front())); return ret; } namespace detail { template std::pair doCall(size_t, future<> _, std::shared_ptr c) { (*c)(); return std::make_pair(true, _.get_handle(true)); } } template inline std::vector> dispatcher::call(const std::vector> &ops, const std::vector> &callables) { typedef packaged_task tasktype; std::vector> retfutures; std::vector>> callbacks; retfutures.reserve(callables.size()); callbacks.reserve(callables.size()); for(auto &t: callables) { std::shared_ptr c(std::make_shared(std::function(t))); retfutures.push_back(c->get_future()); callbacks.push_back(std::make_pair(async_op_flags::none, std::bind(&detail::doCall, std::placeholders::_1, std::placeholders::_2, std::move(c)))); } auto _ret(completion(ops, callbacks)); std::vector> ret; ret.reserve(_ret.size()); for (size_t n = 0; n < _ret.size(); n++) ret.push_back(future(std::move(_ret[n]), std::move(retfutures[n]))); return ret; } template inline future dispatcher::call(const future<> &req, std::function callback) { std::vector> i; std::vector> c; i.reserve(1); c.reserve(1); i.push_back(req); c.push_back(std::move(callback)); auto ret(std::move(call(i, c).front())); return ret; } #ifndef DOXYGEN_SHOULD_SKIP_THIS template inline future::type> dispatcher::call(const future<> &req, C callback, Args... args) #else template inline future::type> dispatcher::call(const future<> &req, C callback, Args... args) #endif { typedef typename std::result_of::type rettype; return call(req, std::function(std::bind(callback, args...))); } inline future<> dispatcher::adopt(handle_ptr h) { std::vector i; i.reserve(1); i.push_back(std::move(h)); auto ret(std::move(adopt(i).front())); return ret; } inline future<> dispatcher::dir(const path_req &req) { std::vector i; i.reserve(1); i.push_back(req); auto ret(std::move(dir(i).front())); return ret; } inline future<> dispatcher::rmdir(const path_req &req) { std::vector i; i.reserve(1); i.push_back(req); auto ret(std::move(rmdir(i).front())); return ret; } inline future<> dispatcher::file(const path_req &req) { std::vector i; i.reserve(1); i.push_back(req); auto ret(std::move(file(i).front())); return ret; } inline future<> dispatcher::rmfile(const path_req &req) { std::vector i; i.reserve(1); i.push_back(req); auto ret(std::move(rmfile(i).front())); return ret; } inline future<> dispatcher::symlink(const path_req &req, const future<> &target) { std::vector i(1, req); std::vector> t(1, target); auto ret(std::move(symlink(i, t).front())); return ret; } inline future<> dispatcher::rmsymlink(const path_req &req) { std::vector i; i.reserve(1); i.push_back(req); auto ret(std::move(rmsymlink(i).front())); return ret; } inline future<> dispatcher::sync(const future<> &req) { std::vector> i; i.reserve(1); i.push_back(req); auto ret(std::move(sync(i).front())); return ret; } inline future<> dispatcher::zero(const future<> &req, const std::vector> &ranges) { std::vector> i; std::vector>> r; i.reserve(1); i.push_back(req); r.reserve(1); r.push_back(ranges); auto ret(std::move(zero(i, r).front())); return ret; } inline future<> dispatcher::close(const future<> &req) { std::vector> i; i.reserve(1); i.push_back(req); auto ret(std::move(close(i).front())); return ret; } #ifndef DOXYGEN_SHOULD_SKIP_THIS inline future<> dispatcher::read(const detail::io_req_impl &req) { std::vector> i; i.reserve(1); i.push_back(req); auto ret(std::move(read(i).front())); return ret; } inline future<> dispatcher::write(const detail::io_req_impl &req) { std::vector> i; i.reserve(1); i.push_back(req); auto ret(std::move(write(i).front())); return ret; } #endif template inline std::vector> dispatcher::read(const std::vector> &ops) { return read(detail::async_file_io_dispatcher_rwconverter()(ops)); } template inline std::vector> dispatcher::write(const std::vector> &ops) { return write(detail::async_file_io_dispatcher_rwconverter()(ops)); } inline future<> dispatcher::truncate(const future<> &op, off_t newsize) { std::vector> o; std::vector i; o.reserve(1); o.push_back(op); i.reserve(1); i.push_back(newsize); auto ret(std::move(truncate(o, i).front())); return ret; } inline future, bool>> dispatcher::enumerate(const enumerate_req &req) { std::vector i; i.reserve(1); i.push_back(req); auto ret(std::move(enumerate(i).front())); return ret; } inline future>> dispatcher::extents(const future<> &op) { std::vector> o; o.reserve(1); o.push_back(op); auto ret(std::move(extents(o).front())); return ret; } inline future dispatcher::statfs(const future<> &op, const fs_metadata_flags &req) { std::vector> o; std::vector i; o.reserve(1); o.push_back(op); i.reserve(1); i.push_back(req); auto ret(std::move(statfs(o, i).front())); return ret; } inline future<> dispatcher::depends(future<> precondition, future<> op) { std::pair> callback(std::make_pair(async_op_flags::immediate, [BOOST_AFIO_LAMBDA_MOVE_CAPTURE(op)](size_t, future<>) { return std::make_pair(true, op.get_handle()); })); std::vector> r; std::vector>> i; r.reserve(1); i.reserve(1); r.push_back(precondition); i.push_back(std::move(callback)); auto ret(std::move(completion(r, i).front())); return ret; } namespace detail { template struct async_dir { T path; file_flags flags; async_dir(T _path, file_flags _flags) : path(std::move(_path)), flags(_flags) { } future<> operator()(future<> f=future<>()) { dispatcher *dispatcher = f.parent(); path_req req(!dispatcher ? ( dispatcher = current_dispatcher().get(), path_req(path_req::absolute(std::move(f), std::move(path), std::move(flags))) ) : path_req(path_req::relative(std::move(f), std::move(path), std::move(flags)))); #if BOOST_AFIO_VALIDATE_INPUTS if (!req.validate()) BOOST_AFIO_THROW(std::invalid_argument("Inputs are invalid.")); #endif auto ret(std::move(dispatcher->dir(std::vector(1, std::move(req))).front())); return ret; } }; template struct async_rmdir { T path; file_flags flags; async_rmdir(T _path, file_flags _flags) : path(std::move(_path)), flags(_flags) { } future<> operator()(future<> f = future<>()) { dispatcher *dispatcher = f.parent(); path_req req(!dispatcher ? ( dispatcher = current_dispatcher().get(), path_req(path_req::absolute(std::move(f), std::move(path), std::move(flags))) ) : path_req(path_req::relative(std::move(f), std::move(path), std::move(flags)))); #if BOOST_AFIO_VALIDATE_INPUTS if (!req.validate()) BOOST_AFIO_THROW(std::invalid_argument("Inputs are invalid.")); #endif auto ret(std::move(dispatcher->rmdir(std::vector(1, std::move(req))).front())); return ret; } }; template struct async_file { T path; file_flags flags; async_file(T _path, file_flags _flags) : path(std::move(_path)), flags(_flags) { } future<> operator()(future<> f = future<>()) { dispatcher *dispatcher = f.parent(); path_req req(!dispatcher ? ( dispatcher = current_dispatcher().get(), path_req(path_req::absolute(std::move(f), std::move(path), std::move(flags))) ) : path_req(path_req::relative(std::move(f), std::move(path), std::move(flags)))); #if BOOST_AFIO_VALIDATE_INPUTS if (!req.validate()) BOOST_AFIO_THROW(std::invalid_argument("Inputs are invalid.")); #endif auto ret(std::move(dispatcher->file(std::vector(1, std::move(req))).front())); return ret; } }; template struct async_rmfile { T path; file_flags flags; async_rmfile(T _path, file_flags _flags) : path(std::move(_path)), flags(_flags) { } future<> operator()(future<> f = future<>()) { dispatcher *dispatcher = f.parent(); path_req req(!dispatcher ? ( dispatcher = current_dispatcher().get(), path_req(path_req::absolute(std::move(f), std::move(path), std::move(flags))) ) : path_req(path_req::relative(std::move(f), std::move(path), std::move(flags)))); #if BOOST_AFIO_VALIDATE_INPUTS if (!req.validate()) BOOST_AFIO_THROW(std::invalid_argument("Inputs are invalid.")); #endif auto ret(std::move(dispatcher->rmfile(std::vector(1, std::move(req))).front())); return ret; } }; template struct async_symlink { T path; file_flags flags; future<> target; async_symlink(T _path, file_flags _flags, future<> _target) : path(std::move(_path)), flags(_flags), target(std::move(_target)) { } future<> operator()(future<> f = future<>()) { dispatcher *dispatcher = f.parent(); path_req req(!dispatcher ? ( dispatcher = current_dispatcher().get(), path_req(path_req::absolute(std::move(f), std::move(path), std::move(flags))) ) : path_req(path_req::relative(std::move(f), std::move(path), std::move(flags)))); #if BOOST_AFIO_VALIDATE_INPUTS if (!req.validate()) BOOST_AFIO_THROW(std::invalid_argument("Inputs are invalid.")); #endif auto ret(std::move(dispatcher->symlink(std::vector(1, std::move(req)), std::vector>(1, std::move(target))).front())); return ret; } }; template struct async_rmsymlink { T path; file_flags flags; async_rmsymlink(T _path, file_flags _flags) : path(std::move(_path)), flags(_flags) { } future<> operator()(future<> f = future<>()) { dispatcher *dispatcher = f.parent(); path_req req(!dispatcher ? ( dispatcher = current_dispatcher().get(), path_req(path_req::absolute(std::move(f), std::move(path), std::move(flags))) ) : path_req(path_req::relative(std::move(f), std::move(path), std::move(flags)))); #if BOOST_AFIO_VALIDATE_INPUTS if (!req.validate()) BOOST_AFIO_THROW(std::invalid_argument("Inputs are invalid.")); #endif auto ret(std::move(dispatcher->rmsymlink(std::vector(1, std::move(req))).front())); return ret; } }; struct async_sync { future<> operator()(future<> f = future<>()) { dispatcher *dispatcher = f.parent(); if (!dispatcher) dispatcher = current_dispatcher().get(); auto ret(std::move(dispatcher->sync(std::vector>(1, std::move(f))).front())); return ret; } }; struct async_close { future<> operator()(future<> f = future<>()) { dispatcher *dispatcher = f.parent(); if (!dispatcher) dispatcher = current_dispatcher().get(); auto ret(std::move(dispatcher->close(std::vector>(1, std::move(f))).front())); return ret; } }; struct async_read { io_req_impl req; template async_read(U &&v, off_t _where) : req(BOOST_AFIO_V2_NAMESPACE::make_io_req(future<>(), std::forward(v), _where)) { } template async_read(U &&v, size_t _length, off_t _where) : req(BOOST_AFIO_V2_NAMESPACE::make_io_req(future<>(), std::forward(v), _length, _where)) { } future<> operator()(future<> f = future<>()) { dispatcher *dispatcher = f.parent(); if (!dispatcher) dispatcher = current_dispatcher().get(); req.precondition = f; #if BOOST_AFIO_VALIDATE_INPUTS if (!req.validate()) BOOST_AFIO_THROW(std::invalid_argument("Inputs are invalid.")); #endif auto ret(std::move(dispatcher->read(std::vector>(1, std::move(req))).front())); return ret; } }; struct async_write { io_req_impl req; template async_write(U &&v, off_t _where) : req(BOOST_AFIO_V2_NAMESPACE::make_io_req(future<>(), std::forward(v), _where)) { } template async_write(U &&v, size_t _length, off_t _where) : req(BOOST_AFIO_V2_NAMESPACE::make_io_req(future<>(), std::forward(v), _length, _where)) { } future<> operator()(future<> f = future<>()) { dispatcher *dispatcher = f.parent(); if (!dispatcher) dispatcher = current_dispatcher().get(); req.precondition = f; #if BOOST_AFIO_VALIDATE_INPUTS if (!req.validate()) BOOST_AFIO_THROW(std::invalid_argument("Inputs are invalid.")); #endif auto ret(std::move(dispatcher->write(std::vector>(1, std::move(req))).front())); return ret; } }; struct async_truncate { off_t _size; async_truncate(off_t size) : _size(size) { } future<> operator()(future<> f = future<>()) { dispatcher *dispatcher = f.parent(); if (!dispatcher) dispatcher = current_dispatcher().get(); auto ret(std::move(dispatcher->truncate(std::vector>(1, std::move(f)), std::vector(1, _size)).front())); return ret; } }; struct async_enumerate { size_t maxitems; bool restart; path glob; metadata_flags metadata; enumerate_req::filter filtering; async_enumerate(size_t _maxitems, bool _restart, path _glob, metadata_flags _metadata, enumerate_req::filter _filtering) : maxitems(_maxitems), restart(_restart), glob(_glob), metadata(_metadata), filtering(_filtering) { } future, bool>> operator()(future<> f = future<>()) { enumerate_req req(std::move(f), maxitems, restart, std::move(glob), metadata, filtering); dispatcher *dispatcher = f.parent(); if (!dispatcher) dispatcher = current_dispatcher().get(); #if BOOST_AFIO_VALIDATE_INPUTS if (!req.validate()) BOOST_AFIO_THROW(std::invalid_argument("Inputs are invalid.")); #endif auto ret(std::move(dispatcher->enumerate(std::vector(1, std::move(req))).front())); return ret; } }; struct async_zero { std::vector> ranges; async_zero(std::vector> _ranges) : ranges(_ranges) { } future<> operator()(future<> f = future<>()) { dispatcher *dispatcher = f.parent(); if (!dispatcher) dispatcher = current_dispatcher().get(); auto ret(std::move(dispatcher->zero(std::vector>(1, std::move(f)), std::vector>>(1, std::move(ranges))).front())); return ret; } }; struct async_extents { future>> operator()(future<> f = future<>()) { dispatcher *dispatcher = f.parent(); if (!dispatcher) dispatcher = current_dispatcher().get(); auto ret(std::move(dispatcher->extents(std::vector>(1, std::move(f))).front())); return ret; } }; struct async_statfs { fs_metadata_flags req; async_statfs(fs_metadata_flags _req) : req(_req) { } future operator()(future<> f = future<>()) { dispatcher *dispatcher = f.parent(); if (!dispatcher) dispatcher = current_dispatcher().get(); auto ret(std::move(dispatcher->statfs(std::vector>(1, std::move(f)), std::vector(1, req)).front())); return ret; } }; template struct _is_not_handle : public std::true_type { }; template struct _is_not_handle> : public std::false_type { }; template<> struct _is_not_handle : public std::false_type { }; template struct is_not_handle : public _is_not_handle::type> { }; } /*! \brief Asynchronous directory creation and open after an optional precondition. \docs_dir \ntkernelnamespacenote \tparam "class T" The type of path to be used. \return A future \param _precondition The precondition to use. \param _path The filing system path to be used. \param _flags The flags to be used. \ingroup dir \qbk{distinguish, relative} \raceguarantees{ [raceguarantee FreeBSD, Linux, OS X, Windows..Race free up to the containing directory.] } \complexity{Amortised O(1) to dispatch. Amortised O(1) to complete if directory creation is constant time.} \exceptionmodelfree \qexample{filedir_example} */ template inline future<> async_dir(future<> _precondition, T _path, file_flags _flags = file_flags::none) { return detail::async_dir(std::move(_path), _flags)(std::move(_precondition)); } /*! \brief Asynchronous directory creation and open after an optional precondition. \docs_dir \ntkernelnamespacenote \tparam "class T" The type of path to be used. \return A future \param _path The filing system path to be used. \param _flags The flags to be used. \ingroup dir \qbk{distinguish, absolute} \raceguarantees{ [raceguarantee FreeBSD, Linux, OS X, Windows..No guarantees.] } \complexity{Amortised O(1) to dispatch. Amortised O(1) to complete if directory creation is constant time.} \exceptionmodelfree \qexample{filedir_example} */ template::value>::type> inline future<> async_dir(T _path, file_flags _flags = file_flags::none) { return detail::async_dir(std::move(_path), _flags)(future<>()); } /*! \brief Synchronous directory creation and open after an optional precondition. \docs_dir \ntkernelnamespacenote \tparam "class T" The type of path to use. \return A handle to the directory. \param _precondition The precondition to use. \param _path The filing system path to use. \param _flags The flags to use. \ingroup dir \qbk{distinguish, relative throwing} \raceguarantees{ [raceguarantee FreeBSD, Linux, OS X, Windows..Race free up to the containing directory.] } \complexity{Amortised O(1) to dispatch. Amortised O(1) to complete if directory creation is constant time.} \exceptionmodelfree \qexample{filedir_example} */ template inline handle_ptr dir(future<> _precondition, T _path, file_flags _flags = file_flags::none) { return detail::async_dir(std::move(_path), _flags)(std::move(_precondition)).get_handle(); } /*! \brief Synchronous directory creation and open after an optional precondition. \docs_dir \ntkernelnamespacenote \tparam "class T" The type of path to use. \return A handle to the directory. \param _path The filing system path to use. \param _flags The flags to use. \ingroup dir \qbk{distinguish, absolute throwing} \raceguarantees{ [raceguarantee FreeBSD, Linux, OS X, Windows..No guarantees.] } \complexity{Amortised O(1) to dispatch. Amortised O(1) to complete if directory creation is constant time.} \exceptionmodelfree \qexample{filedir_example} */ template::value>::type> inline handle_ptr dir(T _path, file_flags _flags = file_flags::none) { return detail::async_dir(std::move(_path), _flags)(future<>()).get_handle(); } /*! \brief Synchronous directory creation and open after an optional precondition. \docs_dir \ntkernelnamespacenote \tparam "class T" The type of path to use. \return A handle to the directory. \param _ec Error code to set. \param _precondition The precondition to use. \param _path The filing system path to use. \param _flags The flags to use. \ingroup dir \qbk{distinguish, relative non throwing} \raceguarantees{ [raceguarantee FreeBSD, Linux, OS X, Windows..Race free up to the containing directory.] } \complexity{Amortised O(1) to dispatch. Amortised O(1) to complete if directory creation is constant time.} \exceptionmodelfree \qexample{filedir_example} */ template inline handle_ptr dir(error_code &_ec, future<> _precondition, T _path, file_flags _flags = file_flags::none) { return detail::async_dir(std::move(_path), _flags)(std::move(_precondition)).get_handle(_ec); } /*! \brief Synchronous directory creation and open after an optional precondition. \docs_dir \ntkernelnamespacenote \tparam "class T" The type of path to use. \return A handle to the directory. \param _ec Error code to set. \param _path The filing system path to use. \param _flags The flags to use. \ingroup dir \qbk{distinguish, absolute non throwing} \raceguarantees{ [raceguarantee FreeBSD, Linux, OS X, Windows..No guarantees.] } \complexity{Amortised O(1) to dispatch. Amortised O(1) to complete if directory creation is constant time.} \exceptionmodelfree \qexample{filedir_example} */ template::value>::type> inline handle_ptr dir(error_code &_ec, T _path, file_flags _flags = file_flags::none) { return detail::async_dir(std::move(_path), _flags)(future<>()).get_handle(_ec); } /*! \brief Asynchronous directory deletion after an optional precondition. \docs_rmdir \ntkernelnamespacenote \tparam "class T" The type of path to use. \return A future \param _precondition The precondition to use. \param _path The filing system path to use. \param _flags The flags to use. \ingroup rmdir \qbk{distinguish, relative} \raceguarantees{ [raceguarantee FreeBSD, Linux..Race free up to the containing directory.] [raceguarantee Windows..Race free if handle open, else up to the containing directory.] [raceguarantee OS X..No guarantees.] } \complexity{Amortised O(1) to dispatch. Amortised O(1) to complete if directory deletion is constant time.} \exceptionmodelfree \qexample{filedir_example} */ template inline future<> async_rmdir(future<> _precondition, T _path = path(), file_flags _flags = file_flags::none) { return detail::async_rmdir(std::move(_path), _flags)(std::move(_precondition)); } /*! \brief Asynchronous directory deletion after an optional precondition. \docs_rmdir \ntkernelnamespacenote \tparam "class T" The type of path to use. \return A future \param _path The filing system path to use. \param _flags The flags to use. \ingroup rmdir \qbk{distinguish, absolute} \raceguarantees{ [raceguarantee FreeBSD, Linux, OS X, Windows..No guarantees.] } \complexity{Amortised O(1) to dispatch. Amortised O(1) to complete if directory deletion is constant time.} \exceptionmodelfree \qexample{filedir_example} */ template::value>::type> inline future<> async_rmdir(T _path, file_flags _flags = file_flags::none) { return detail::async_rmdir(std::move(_path), _flags)(future<>()); } /*! \brief Synchronous directory deletion after an optional precondition. \docs_rmdir \ntkernelnamespacenote \tparam "class T" The type of path to use. \param _precondition The precondition to use. \param _path The filing system path to use. \param _flags The flags to use. \ingroup rmdir \qbk{distinguish, relative throwing} \raceguarantees{ [raceguarantee FreeBSD, Linux..Race free up to the containing directory.] [raceguarantee Windows..Race free if handle open, else up to the containing directory.] [raceguarantee OS X..No guarantees.] } \complexity{Amortised O(1) to dispatch. Amortised O(1) to complete if directory deletion is constant time.} \exceptionmodelfree \qexample{filedir_example} */ template inline void rmdir(future<> _precondition, T _path = path(), file_flags _flags = file_flags::none) { detail::async_rmdir(std::move(_path), _flags)(std::move(_precondition)).get_handle(); } /*! \brief Synchronous directory deletion after an optional precondition. \docs_rmdir \ntkernelnamespacenote \tparam "class T" The type of path to use. \param _path The filing system path to use. \param _flags The flags to use. \ingroup rmdir \qbk{distinguish, absolute throwing} \raceguarantees{ [raceguarantee FreeBSD, Linux, OS X, Windows..No guarantees.] } \complexity{Amortised O(1) to dispatch. Amortised O(1) to complete if directory deletion is constant time.} \exceptionmodelfree \qexample{filedir_example} */ template::value>::type> inline void rmdir(T _path, file_flags _flags = file_flags::none) { detail::async_rmdir(std::move(_path), _flags)(future<>()).get_handle(); } /*! \brief Synchronous directory deletion after an optional precondition. \docs_rmdir \ntkernelnamespacenote \tparam "class T" The type of path to use. \param _ec Error code to set. \param _precondition The precondition to use. \param _path The filing system path to use. \param _flags The flags to use. \ingroup rmdir \qbk{distinguish, relative non throwing} \raceguarantees{ [raceguarantee FreeBSD, Linux..Race free up to the containing directory.] [raceguarantee Windows..Race free if handle open, else up to the containing directory.] [raceguarantee OS X..No guarantees.] } \complexity{Amortised O(1) to dispatch. Amortised O(1) to complete if directory deletion is constant time.} \exceptionmodelfree \qexample{filedir_example} */ template inline void rmdir(error_code &_ec, future<> _precondition, T _path = path(), file_flags _flags = file_flags::none) { detail::async_rmdir(std::move(_path), _flags)(std::move(_precondition)).get_handle(_ec); } /*! \brief Synchronous directory deletion after an optional precondition. \docs_rmdir \ntkernelnamespacenote \tparam "class T" The type of path to use. \param _ec Error code to set. \param _path The filing system path to use. \param _flags The flags to use. \ingroup rmdir \qbk{distinguish, absolute non throwing} \raceguarantees{ [raceguarantee FreeBSD, Linux, OS X, Windows..No guarantees.] } \complexity{Amortised O(1) to dispatch. Amortised O(1) to complete if directory deletion is constant time.} \exceptionmodelfree \qexample{filedir_example} */ template::value>::type> inline void rmdir(error_code &_ec, T _path, file_flags _flags = file_flags::none) { detail::async_rmdir(std::move(_path), _flags)(future<>()).get_handle(_ec); } /*! \brief Asynchronous file creation and open after an optional precondition. \docs_file \ntkernelnamespacenote \tparam "class T" The type of path to use. \return A future \param _precondition The precondition to use. \param _path The filing system path to use. \param _flags The flags to use. \ingroup file \qbk{distinguish, relative} \raceguarantees{ [raceguarantee FreeBSD, Linux, Windows..Race free up to the containing directory.] [raceguarantee OS X..No guarantees.] } \complexity{Amortised O(1) to dispatch. Amortised O(1) to complete if file creation is constant time.} \exceptionmodelfree \qexample{filedir_example} */ template inline future<> async_file(future<> _precondition, T _path, file_flags _flags = file_flags::none) { return detail::async_file(std::move(_path), _flags)(std::move(_precondition)); } /*! \brief Asynchronous file creation and open after an optional precondition. \docs_file \ntkernelnamespacenote \tparam "class T" The type of path to use. \return A future \param _path The filing system path to use. \param _flags The flags to use. \ingroup file \qbk{distinguish, absolute} \raceguarantees{ [raceguarantee FreeBSD, Linux, OS X, Windows..No guarantees.] } \complexity{Amortised O(1) to dispatch. Amortised O(1) to complete if file creation is constant time.} \exceptionmodelfree \qexample{filedir_example} */ template::value>::type> inline future<> async_file(T _path, file_flags _flags = file_flags::none) { return detail::async_file(std::move(_path), _flags)(future<>()); } /*! \brief Synchronous file creation and open after an optional precondition. \docs_file \ntkernelnamespacenote \tparam "class T" The type of path to use. \return A handle to the file. \param _precondition The precondition to use. \param _path The filing system path to use. \param _flags The flags to use. \ingroup file \qbk{distinguish, relative throwing} \raceguarantees{ [raceguarantee FreeBSD, Linux, Windows..Race free up to the containing directory.] [raceguarantee OS X..No guarantees.] } \complexity{Amortised O(1) to dispatch. Amortised O(1) to complete if file creation is constant time.} \exceptionmodelfree \qexample{filedir_example} */ template inline handle_ptr file(future<> _precondition, T _path, file_flags _flags = file_flags::none) { return detail::async_file(std::move(_path), _flags)(std::move(_precondition)).get_handle(); } /*! \brief Synchronous file creation and open after an optional precondition. \docs_file \ntkernelnamespacenote \tparam "class T" The type of path to use. \return A handle to the file. \param _path The filing system path to use. \param _flags The flags to use. \ingroup file \qbk{distinguish, absolute throwing} \raceguarantees{ [raceguarantee FreeBSD, Linux, OS X, Windows..No guarantees.] } \complexity{Amortised O(1) to dispatch. Amortised O(1) to complete if file creation is constant time.} \exceptionmodelfree \qexample{filedir_example} */ template::value>::type> inline handle_ptr file(T _path, file_flags _flags = file_flags::none) { return detail::async_file(std::move(_path), _flags)(future<>()).get_handle(); } /*! \brief Synchronous file creation and open after an optional precondition. \docs_file \ntkernelnamespacenote \tparam "class T" The type of path to use. \return A handle to the file. \param _ec Error code to set. \param _precondition The precondition to use. \param _path The filing system path to use. \param _flags The flags to use. \ingroup file \qbk{distinguish, relative non throwing} \raceguarantees{ [raceguarantee FreeBSD, Linux, Windows..Race free up to the containing directory.] [raceguarantee OS X..No guarantees.] } \complexity{Amortised O(1) to dispatch. Amortised O(1) to complete if file creation is constant time.} \exceptionmodelfree \qexample{filedir_example} */ template inline handle_ptr file(error_code &_ec, future<> _precondition, T _path, file_flags _flags = file_flags::none) { return detail::async_file(std::move(_path), _flags)(std::move(_precondition)).get_handle(_ec); } /*! \brief Synchronous file creation and open after an optional precondition. \docs_file \ntkernelnamespacenote \tparam "class T" The type of path to use. \return A handle to the file. \param _ec Error code to set. \param _path The filing system path to use. \param _flags The flags to use. \ingroup file \qbk{distinguish, absolute non throwing} \raceguarantees{ [raceguarantee FreeBSD, Linux, OS X, Windows..No guarantees.] } \complexity{Amortised O(1) to dispatch. Amortised O(1) to complete if file creation is constant time.} \exceptionmodelfree \qexample{filedir_example} */ template::value>::type> inline handle_ptr file(error_code &_ec, T _path, file_flags _flags = file_flags::none) { return detail::async_file(std::move(_path), _flags)(future<>()).get_handle(_ec); } /*! \brief Asynchronous file deletion after an optional precondition. \docs_rmfile \ntkernelnamespacenote \tparam "class T" The type of path to be used. \return A future \param _precondition The precondition to use. \param _path The filing system path to be used. \param _flags The flags to be used. \ingroup rmfile \qbk{distinguish, relative} \raceguarantees{ [raceguarantee FreeBSD, Linux..Race free up to the containing directory.] [raceguarantee Windows..Race free if handle open, else up to the containing directory.] [raceguarantee OS X..No guarantees.] } \complexity{Amortised O(1) to dispatch. Amortised O(1) to complete if file deletion is constant time.} \exceptionmodelfree \qexample{filedir_example} */ template inline future<> async_rmfile(future<> _precondition, T _path = path(), file_flags _flags = file_flags::none) { return detail::async_rmfile(std::move(_path), _flags)(std::move(_precondition)); } /*! \brief Asynchronous file deletion after an optional precondition. \docs_rmfile \ntkernelnamespacenote \tparam "class T" The type of path to be used. \return A future \param _path The filing system path to be used. \param _flags The flags to be used. \ingroup rmfile \qbk{distinguish, absolute} \raceguarantees{ [raceguarantee FreeBSD, Linux, OS X, Windows..No guarantees.] } \complexity{Amortised O(1) to dispatch. Amortised O(1) to complete if file deletion is constant time.} \exceptionmodelfree \qexample{filedir_example} */ template::value>::type> inline future<> async_rmfile(T _path, file_flags _flags = file_flags::none) { return detail::async_rmfile(std::move(_path), _flags)(future<>()); } /*! \brief Synchronous file deletion after an optional precondition. \docs_rmfile \ntkernelnamespacenote \tparam "class T" The type of path to be used. \param _precondition The precondition to use. \param _path The filing system path to be used. \param _flags The flags to be used. \ingroup rmfile \qbk{distinguish, relative throwing} \raceguarantees{ [raceguarantee FreeBSD, Linux..Race free up to the containing directory.] [raceguarantee Windows..Race free if handle open, else up to the containing directory.] [raceguarantee OS X..No guarantees.] } \complexity{Amortised O(1) to dispatch. Amortised O(1) to complete if file deletion is constant time.} \exceptionmodelfree \qexample{filedir_example} */ template inline void rmfile(future<> _precondition, T _path = path(), file_flags _flags = file_flags::none) { detail::async_rmfile(std::move(_path), _flags)(std::move(_precondition)).get_handle(); } /*! \brief Synchronous file deletion after an optional precondition. \docs_rmfile \ntkernelnamespacenote \tparam "class T" The type of path to be used. \param _path The filing system path to be used. \param _flags The flags to be used. \ingroup rmfile \qbk{distinguish, absolute throwing} \raceguarantees{ [raceguarantee FreeBSD, Linux, OS X, Windows..No guarantees.] } \complexity{Amortised O(1) to dispatch. Amortised O(1) to complete if file deletion is constant time.} \exceptionmodelfree \qexample{filedir_example} */ template::value>::type> inline void rmfile(T _path, file_flags _flags = file_flags::none) { detail::async_rmfile(std::move(_path), _flags)(future<>()).get_handle(); } /*! \brief Synchronous file deletion after an optional precondition. \docs_rmfile \ntkernelnamespacenote \tparam "class T" The type of path to be used. \param _ec Error code to set. \param _precondition The precondition to use. \param _path The filing system path to be used. \param _flags The flags to be used. \ingroup rmfile \qbk{distinguish, relative non throwing} \raceguarantees{ [raceguarantee FreeBSD, Linux..Race free up to the containing directory.] [raceguarantee Windows..Race free if handle open, else up to the containing directory.] [raceguarantee OS X..No guarantees.] } \complexity{Amortised O(1) to dispatch. Amortised O(1) to complete if file deletion is constant time.} \exceptionmodelfree \qexample{filedir_example} */ template inline void rmfile(error_code &_ec, future<> _precondition, T _path = path(), file_flags _flags = file_flags::none) { detail::async_rmfile(std::move(_path), _flags)(std::move(_precondition)).get_handle(_ec); } /*! \brief Synchronous file deletion after an optional precondition. \docs_rmfile \ntkernelnamespacenote \tparam "class T" The type of path to be used. \param _ec Error code to set. \param _path The filing system path to be used. \param _flags The flags to be used. \ingroup rmfile \qbk{distinguish, absolute non throwing} \raceguarantees{ [raceguarantee FreeBSD, Linux, OS X, Windows..No guarantees.] } \complexity{Amortised O(1) to dispatch. Amortised O(1) to complete if file deletion is constant time.} \exceptionmodelfree \qexample{filedir_example} */ template::value>::type> inline void rmfile(error_code &_ec, T _path, file_flags _flags = file_flags::none) { detail::async_rmfile(std::move(_path), _flags)(future<>()).get_handle(_ec); } /*! \brief Asynchronous symlink creation and open after a precondition. \docs_symlink \ntkernelnamespacenote \tparam "class T" The type of path to use. \return A future \param _precondition The precondition to use. \param _path The filing system path to use. \param _target The item to link to if creating. \param _flags The flags to use. \ingroup symlink \qbk{distinguish, relative} \raceguarantees{ [raceguarantee FreeBSD, Linux, Windows..Link creation is race free up to the containing directory. Destination is unavoidably racy.] [raceguarantee OS X..No guarantees.] } \complexity{Amortised O(1) to dispatch. Amortised O(1) to complete if symlink creation is constant time.} \exceptionmodelfree \qexample{filedir_example} */ template inline future<> async_symlink(future<> _precondition, T _path, future<> _target=future<>(), file_flags _flags = file_flags::none) { return detail::async_symlink(std::move(_path), _flags, std::move(_target))(std::move(_precondition)); } /*! \brief Asynchronous symlink creation and open after a precondition. \docs_symlink \ntkernelnamespacenote \tparam "class T" The type of path to use. \return A future \param _path The filing system path to use. \param _target The item to link to if creating. \param _flags The flags to use. \ingroup symlink \qbk{distinguish, absolute} \raceguarantees{ [raceguarantee FreeBSD, Linux, OS X, Windows..No guarantees.] } \complexity{Amortised O(1) to dispatch. Amortised O(1) to complete if symlink creation is constant time.} \exceptionmodelfree \qexample{filedir_example} */ template::value>::type> inline future<> async_symlink(T _path, future<> _target=future<>(), file_flags _flags = file_flags::none) { return detail::async_symlink(std::move(_path), _flags, std::move(_target))(future<>()); } /*! \brief Synchronous symlink creation and open after a precondition.. \docs_symlink \ntkernelnamespacenote \tparam "class T" The type of path to use. \return A handle to the symlink. \param _precondition The precondition to use. \param _path The filing system path to use. \param _target The item to link to if creating. \param _flags The flags to use. \ingroup symlink \qbk{distinguish, relative throwing} \raceguarantees{ [raceguarantee FreeBSD, Linux, Windows..Link creation is race free up to the containing directory. Destination is unavoidably racy.] [raceguarantee OS X..No guarantees.] } \complexity{Amortised O(1) to dispatch. Amortised O(1) to complete if symlink creation is constant time.} \exceptionmodelfree \qexample{filedir_example} */ template inline handle_ptr symlink(future<> _precondition, T _path, future<> _target = future<>(), file_flags _flags = file_flags::none) { return detail::async_symlink(std::move(_path), _flags, std::move(_target))(std::move(_precondition)).get_handle(); } /*! \brief Synchronous symlink creation and open after a precondition. \docs_symlink \ntkernelnamespacenote \tparam "class T" The type of path to use. \return A handle to the symlink. \param _path The filing system path to use. \param _target The item to link to if creating. \param _flags The flags to use. \ingroup symlink \qbk{distinguish, absolute throwing} \raceguarantees{ [raceguarantee FreeBSD, Linux, OS X, Windows..No guarantees.] } \complexity{Amortised O(1) to dispatch. Amortised O(1) to complete if symlink creation is constant time.} \exceptionmodelfree \qexample{filedir_example} */ template::value>::type> inline handle_ptr symlink(T _path, future<> _target = future<>(), file_flags _flags = file_flags::none) { return detail::async_symlink(std::move(_path), _flags, std::move(_target))(future<>()).get_handle(); } /*! \brief Synchronous symlink creation and open after a precondition. \docs_symlink \ntkernelnamespacenote \tparam "class T" The type of path to use. \return A handle to the symlink. \param _ec Error code to set. \param _precondition The precondition to use. \param _path The filing system path to use. \param _target The item to link to if creating. \param _flags The flags to use. \ingroup symlink \qbk{distinguish, relative non throwing} \raceguarantees{ [raceguarantee FreeBSD, Linux, Windows..Link creation is race free up to the containing directory. Destination is unavoidably racy.] [raceguarantee OS X..No guarantees.] } \complexity{Amortised O(1) to dispatch. Amortised O(1) to complete if symlink creation is constant time.} \exceptionmodelfree \qexample{filedir_example} */ template inline handle_ptr symlink(error_code &_ec, future<> _precondition, T _path, future<> _target = future<>(), file_flags _flags = file_flags::none) { return detail::async_symlink(std::move(_path), _flags, std::move(_target))(std::move(_precondition)).get_handle(_ec); } /*! \brief Synchronous symlink creation and open after a precondition. \docs_symlink \ntkernelnamespacenote \tparam "class T" The type of path to use. \return A handle to the symlink. \param _ec Error code to set. \param _path The filing system path to use. \param _target The item to link to if creating. \param _flags The flags to use. \ingroup symlink \qbk{distinguish, absolute non throwing} \raceguarantees{ [raceguarantee FreeBSD, Linux, OS X, Windows..No guarantees.] } \complexity{Amortised O(1) to dispatch. Amortised O(1) to complete if symlink creation is constant time.} \exceptionmodelfree \qexample{filedir_example} */ template::value>::type> inline handle_ptr symlink(error_code &_ec, T _path, future<> _target = future<>(), file_flags _flags = file_flags::none) { return detail::async_symlink(std::move(_path), _flags, std::move(_target))(future<>()).get_handle(_ec); } /*! \brief Asynchronous symlink deletion after an optional precondition. \docs_rmsymlink \ntkernelnamespacenote \tparam "class T" The type of path to be used. \return A future \param _precondition The precondition to use. \param _path The filing system path to be used. \param _flags The flags to be used. \ingroup rmsymlink \qbk{distinguish, relative} \raceguarantees{ [raceguarantee FreeBSD, Linux..Race free up to the containing directory.] [raceguarantee Windows..Race free if handle open, else up to the containing directory.] [raceguarantee OS X..No guarantees.] } \complexity{Amortised O(1) to dispatch. Amortised O(1) to complete if symlink deletion is constant time.} \exceptionmodelfree \qexample{filedir_example} */ template inline future<> async_rmsymlink(future<> _precondition, T _path = path(), file_flags _flags = file_flags::none) { return detail::async_rmsymlink(std::move(_path), _flags)(std::move(_precondition)); } /*! \brief Asynchronous symlink deletion after an optional precondition. \docs_rmsymlink \ntkernelnamespacenote \tparam "class T" The type of path to be used. \return A future \param _path The filing system path to be used. \param _flags The flags to be used. \ingroup rmsymlink \qbk{distinguish, absolute} \raceguarantees{ [raceguarantee FreeBSD, Linux, OS X, Windows..No guarantees.] } \complexity{Amortised O(1) to dispatch. Amortised O(1) to complete if symlink deletion is constant time.} \exceptionmodelfree \qexample{filedir_example} */ template::value>::type> inline future<> async_rmsymlink(T _path, file_flags _flags = file_flags::none) { return detail::async_rmsymlink(std::move(_path), _flags)(future<>()); } /*! \brief Synchronous symlink deletion after an optional precondition. \docs_rmsymlink \ntkernelnamespacenote \tparam "class T" The type of path to be used. \param _precondition The precondition to use. \param _path The filing system path to be used. \param _flags The flags to be used. \ingroup rmsymlink \qbk{distinguish, relative throwing} \raceguarantees{ [raceguarantee FreeBSD, Linux..Race free up to the containing directory.] [raceguarantee Windows..Race free if handle open, else up to the containing directory.] [raceguarantee OS X..No guarantees.] } \complexity{Amortised O(1) to dispatch. Amortised O(1) to complete if symlink deletion is constant time.} \exceptionmodelfree \qexample{filedir_example} */ template inline void rmsymlink(future<> _precondition, T _path = path(), file_flags _flags = file_flags::none) { detail::async_rmsymlink(std::move(_path), _flags)(std::move(_precondition)).get_handle(); } /*! \brief Synchronous symlink deletion after an optional precondition. \docs_rmsymlink \ntkernelnamespacenote \tparam "class T" The type of path to be used. \param _path The filing system path to be used. \param _flags The flags to be used. \ingroup rmsymlink \qbk{distinguish, absolute throwing} \raceguarantees{ [raceguarantee FreeBSD, Linux, OS X, Windows..No guarantees.] } \complexity{Amortised O(1) to dispatch. Amortised O(1) to complete if symlink deletion is constant time.} \exceptionmodelfree \qexample{filedir_example} */ template::value>::type> inline void rmsymlink(T _path, file_flags _flags = file_flags::none) { detail::async_rmsymlink(std::move(_path), _flags)(future<>()).get_handle(); } /*! \brief Synchronous symlink deletion after an optional precondition. \docs_rmsymlink \ntkernelnamespacenote \tparam "class T" The type of path to be used. \param _ec Error code to set. \param _precondition The precondition to use. \param _path The filing system path to be used. \param _flags The flags to be used. \ingroup rmsymlink \qbk{distinguish, relative non throwing} \raceguarantees{ [raceguarantee FreeBSD, Linux..Race free up to the containing directory.] [raceguarantee Windows..Race free if handle open, else up to the containing directory.] [raceguarantee OS X..No guarantees.] } \complexity{Amortised O(1) to dispatch. Amortised O(1) to complete if symlink deletion is constant time.} \exceptionmodelfree \qexample{filedir_example} */ template inline void rmsymlink(error_code &_ec, future<> _precondition, T _path = path(), file_flags _flags = file_flags::none) { detail::async_rmsymlink(std::move(_path), _flags)(std::move(_precondition)).get_handle(_ec); } /*! \brief Synchronous symlink deletion after an optional precondition. \docs_rmsymlink \ntkernelnamespacenote \tparam "class T" The type of path to be used. \param _ec Error code to set. \param _path The filing system path to be used. \param _flags The flags to be used. \ingroup rmsymlink \qbk{distinguish, absolute non throwing} \raceguarantees{ [raceguarantee FreeBSD, Linux, OS X, Windows..No guarantees.] } \complexity{Amortised O(1) to dispatch. Amortised O(1) to complete if symlink deletion is constant time.} \exceptionmodelfree \qexample{filedir_example} */ template::value>::type> inline void rmsymlink(error_code &_ec, T _path, file_flags _flags = file_flags::none) { detail::async_rmsymlink(std::move(_path), _flags)(future<>()).get_handle(_ec); } /*! \brief Asynchronous content synchronisation with physical storage after a preceding operation. \docs_sync \return A future \param _precondition The precondition to use. \ingroup sync \complexity{Amortised O(1) to dispatch. Amortised O(1) to complete if content synchronisation is constant time (which is extremely unlikely).} \exceptionmodelfree \qexample{readwrite_example} */ inline future<> async_sync(future<> _precondition) { return detail::async_sync()(std::move(_precondition)); } /*! \brief Synchronous content synchronisation with physical storage after a preceding operation. \docs_sync \param _precondition The precondition to use. \ingroup sync \qbk{distinguish, throwing} \complexity{Amortised O(1) to dispatch. Amortised O(1) to complete if content synchronisation is constant time (which is extremely unlikely).} \exceptionmodelfree \qexample{readwrite_example} */ inline void sync(future<> _precondition) { detail::async_sync()(std::move(_precondition)).get_handle(); } /*! \brief Synchronous content synchronisation with physical storage after a preceding operation. \docs_sync \param _ec Error code to set. \param _precondition The precondition to use. \ingroup sync \qbk{distinguish, non throwing} \complexity{Amortised O(1) to dispatch. Amortised O(1) to complete if content synchronisation is constant time (which is extremely unlikely).} \exceptionmodelfree \qexample{readwrite_example} */ inline void sync(error_code &_ec, future<> _precondition) { detail::async_sync()(std::move(_precondition)).get_handle(_ec); } /*! \brief Asynchronous zeroing and deallocation of physical storage ("hole punching") after a preceding operation. \docs_zero \return A future \param _precondition The precondition to use. \param ranges A sequence of extents to zero and deallocate \ingroup zero \complexity{Amortised O(1) to dispatch. Amortised O(1) to complete if deallocation is constant time.} \exceptionmodelfree \qexample{extents_example} */ inline future<> async_zero(future<> _precondition, std::vector> ranges) { return detail::async_zero(std::move(ranges))(std::move(_precondition)); } /*! \brief Synchronous zeroing and deallocation of physical storage ("hole punching") after a preceding operation. \docs_zero \param _precondition The precondition to use. \param ranges A sequence of extents to zero and deallocate \ingroup zero \qbk{distinguish, throwing} \complexity{Amortised O(1) to dispatch. Amortised O(1) to complete if deallocation is constant time.} \exceptionmodelfree \qexample{extents_example} */ inline void zero(future<> _precondition, std::vector> ranges) { detail::async_zero(std::move(ranges))(std::move(_precondition)).get_handle(); } /*! \brief Synchronous zeroing and deallocation of physical storage ("hole punching") after a preceding operation. \docs_zero \param _ec Error code to set. \param _precondition The precondition to use. \param ranges A sequence of extents to zero and deallocate \ingroup zero \qbk{distinguish, non throwing} \complexity{Amortised O(1) to dispatch. Amortised O(1) to complete if deallocation is constant time.} \exceptionmodelfree \qexample{extents_example} */ inline void zero(error_code &_ec, future<> _precondition, std::vector> ranges) { detail::async_zero(std::move(ranges))(std::move(_precondition)).get_handle(_ec); } /*! \brief Asynchronous file or directory handle close after a preceding operation. \docs_close \return A future \param _precondition The precondition to use. \ingroup close \complexity{Amortised O(1) to dispatch. Amortised O(1) to complete if closing handles is constant time.} \exceptionmodelfree \qexample{filedir_example} */ inline future<> async_close(future<> _precondition) { return detail::async_close()(std::move(_precondition)); } /*! \brief Synchronous file or directory handle close after a preceding operation. \docs_close \param _precondition The precondition to use. \ingroup close \qbk{distinguish, throwing} \complexity{Amortised O(1) to dispatch. Amortised O(1) to complete if closing handles is constant time.} \exceptionmodelfree \qexample{filedir_example} */ inline void close(future<> _precondition) { detail::async_close()(std::move(_precondition)).get_handle(); } /*! \brief Synchronous file or directory handle close after a preceding operation. \docs_close \param _ec Error code to set. \param _precondition The precondition to use. \ingroup close \qbk{distinguish, non throwing} \complexity{Amortised O(1) to dispatch. Amortised O(1) to complete if closing handles is constant time.} \exceptionmodelfree \qexample{filedir_example} */ inline void close(error_code &_ec, future<> _precondition) { detail::async_close()(std::move(_precondition)).get_handle(_ec); } /*! \brief Asynchronous data read after a preceding operation, where offset and total data read must not exceed the present file size. \docs_read \direct_io_note \tparam "class T" Any type. \return A future \param _precondition The precondition to use. \param v Some item understood by `to_asio_buffers()` \param _where The file offset to do the i/o \ingroup read \qbk{distinguish, length deducing} \complexity{Amortised O(1) to dispatch. Amortised O(1) to complete if reading data is constant time.} \exceptionmodelfree \qexample{readwrite_example} */ template inline future<> async_read(future<> _precondition, T &&v, off_t _where) { return detail::async_read(std::forward(v), _where)(std::move(_precondition)); } /*! \brief Asynchronous data read after a preceding operation, where offset and total data read must not exceed the present file size. \docs_read \direct_io_note \tparam "class T" Any type. \return A future \param _precondition The precondition to use. \param v Some item understood by `to_asio_buffers()` \param _length The length of the item \param _where The file offset to do the i/o \ingroup read \qbk{distinguish, length specifying} \complexity{Amortised O(1) to dispatch. Amortised O(1) to complete if reading data is constant time.} \exceptionmodelfree \qexample{readwrite_example} */ template inline future<> async_read(future<> _precondition, T &&v, size_t _length, off_t _where) { return detail::async_read(std::forward(v), _length, _where)(std::move(_precondition)); } /*! \brief Synchronous data read after a preceding operation, where offset and total data read must not exceed the present file size. \docs_read \direct_io_note \tparam "class T" Any type. \param _precondition The precondition to use. \param v Some item understood by `to_asio_buffers()` \param _where The file offset to do the i/o \ingroup read \qbk{distinguish, length deducing throwing} \complexity{Amortised O(1) to dispatch. Amortised O(1) to complete if reading data is constant time.} \exceptionmodelfree \qexample{readwrite_example} */ template inline void read(future<> _precondition, T &&v, off_t _where) { detail::async_read(std::forward(v), _where)(std::move(_precondition)).get_handle(); } /*! \brief Synchronous data read after a preceding operation, where offset and total data read must not exceed the present file size. \docs_read \direct_io_note \tparam "class T" Any type. \param _precondition The precondition to use. \param v Some item understood by `to_asio_buffers()` \param _length The length of the item \param _where The file offset to do the i/o \ingroup read \qbk{distinguish, length specifying throwing} \complexity{Amortised O(1) to dispatch. Amortised O(1) to complete if reading data is constant time.} \exceptionmodelfree \qexample{readwrite_example} */ template inline void read(future<> _precondition, T &&v, size_t _length, off_t _where) { detail::async_read(std::forward(v), _length, _where)(std::move(_precondition)).get_handle(); } /*! \brief Synchronous data read after a preceding operation, where offset and total data read must not exceed the present file size. \docs_read \direct_io_note \tparam "class T" Any type. \param _ec Error code to set. \param _precondition The precondition to use. \param v Some item understood by `to_asio_buffers()` \param _where The file offset to do the i/o \ingroup read \qbk{distinguish, length deducing non throwing} \complexity{Amortised O(1) to dispatch. Amortised O(1) to complete if reading data is constant time.} \exceptionmodelfree \qexample{readwrite_example} */ template inline void read(error_code &_ec, future<> _precondition, T &&v, off_t _where) { detail::async_read(std::forward(v), _where)(std::move(_precondition)).get_handle(_ec); } /*! \brief Synchronous data read after a preceding operation, where offset and total data read must not exceed the present file size. \docs_read \direct_io_note \tparam "class T" Any type. \param _ec Error code to set. \param _precondition The precondition to use. \param v Some item understood by `to_asio_buffers()` \param _length The length of the item \param _where The file offset to do the i/o \ingroup read \qbk{distinguish, length specifying non throwing} \complexity{Amortised O(1) to dispatch. Amortised O(1) to complete if reading data is constant time.} \exceptionmodelfree \qexample{readwrite_example} */ template inline void read(error_code &_ec, future<> _precondition, T &&v, size_t _length, off_t _where) { detail::async_read(std::forward(v), _length, _where)(std::move(_precondition)).get_handle(_ec); } /*! \brief Asynchronous data write after a preceding operation, where offset and total data written must not exceed the present file size. \docs_write \direct_io_note \tparam "class T" Any type. \return A future \param _precondition The precondition to use. \param v Some item understood by `to_asio_buffers()` \param _where The file offset to do the i/o \ingroup write \qbk{distinguish, length deducing} \complexity{Amortised O(1) to dispatch. Amortised O(1) to complete if writing data is constant time.} \exceptionmodelfree \qexample{readwrite_example} */ template inline future<> async_write(future<> _precondition, T &&v, off_t _where) { return detail::async_write(std::forward(v), _where)(std::move(_precondition)); } /*! \brief Asynchronous data write after a preceding operation, where offset and total data written must not exceed the present file size. \docs_write \direct_io_note \tparam "class T" Any type. \return A future \param _precondition The precondition to use. \param v Some item understood by `to_asio_buffers()` \param _length The length of the item \param _where The file offset to do the i/o \ingroup write \qbk{distinguish, length specifying} \complexity{Amortised O(1) to dispatch. Amortised O(1) to complete if writing data is constant time.} \exceptionmodelfree \qexample{readwrite_example} */ template inline future<> async_write(future<> _precondition, T &&v, size_t _length, off_t _where) { return detail::async_write(std::forward(v), _length, _where)(std::move(_precondition)); } /*! \brief Synchronous data write after a preceding operation, where offset and total data written must not exceed the present file size. \docs_write \direct_io_note \tparam "class T" Any type. \param _precondition The precondition to use. \param v Some item understood by `to_asio_buffers()` \param _where The file offset to do the i/o \ingroup write \qbk{distinguish, length deducing throwing} \complexity{Amortised O(1) to dispatch. Amortised O(1) to complete if writing data is constant time.} \exceptionmodelfree \qexample{readwrite_example} */ template inline void write(future<> _precondition, T &&v, off_t _where) { detail::async_write(std::forward(v), _where)(std::move(_precondition)).get_handle(); } /*! \brief Synchronous data write after a preceding operation, where offset and total data written must not exceed the present file size. \docs_write \direct_io_note \tparam "class T" Any type. \param _precondition The precondition to use. \param v Some item understood by `to_asio_buffers()` \param _length The length of the item \param _where The file offset to do the i/o \ingroup write \qbk{distinguish, length specifying throwing} \complexity{Amortised O(1) to dispatch. Amortised O(1) to complete if writing data is constant time.} \exceptionmodelfree \qexample{readwrite_example} */ template inline void write(future<> _precondition, T &&v, size_t _length, off_t _where) { detail::async_write(std::forward(v), _length, _where)(std::move(_precondition)).get_handle(); } /*! \brief Synchronous data write after a preceding operation, where offset and total data written must not exceed the present file size. \docs_write \direct_io_note \tparam "class T" Any type. \param _ec Error code to set. \param _precondition The precondition to use. \param v Some item understood by `to_asio_buffers()` \param _where The file offset to do the i/o \ingroup write \qbk{distinguish, length deducing non throwing} \complexity{Amortised O(1) to dispatch. Amortised O(1) to complete if writing data is constant time.} \exceptionmodelfree \qexample{readwrite_example} */ template inline void write(error_code &_ec, future<> _precondition, T &&v, off_t _where) { detail::async_write(std::forward(v), _where)(std::move(_precondition)).get_handle(_ec); } /*! \brief Synchronous data write after a preceding operation, where offset and total data written must not exceed the present file size. \docs_write \direct_io_note \tparam "class T" Any type. \param _ec Error code to set. \param _precondition The precondition to use. \param v Some item understood by `to_asio_buffers()` \param _length The length of the item \param _where The file offset to do the i/o \ingroup write \qbk{distinguish, length specifying non throwing} \complexity{Amortised O(1) to dispatch. Amortised O(1) to complete if writing data is constant time.} \exceptionmodelfree \qexample{readwrite_example} */ template inline void write(error_code &_ec, future<> _precondition, T &&v, size_t _length, off_t _where) { detail::async_write(std::forward(v), _length, _where)(std::move(_precondition)).get_handle(_ec); } /*! \brief Asynchronous file length truncation after a preceding operation. \docs_truncate \return A future \param _precondition The precondition to use. \param newsize The new size for the file. \ingroup truncate \complexity{Amortised O(1) to dispatch. Amortised O(1) to complete if truncating file lengths is constant time.} \exceptionmodelfree \qexample{readwrite_example} */ inline future<> async_truncate(future<> _precondition, off_t newsize) { return detail::async_truncate(newsize)(std::move(_precondition)); } /*! \brief Synchronous file length truncation after a preceding operation. \docs_truncate \param _precondition The precondition to use. \param newsize The new size for the file. \ingroup truncate \qbk{distinguish, throwing} \complexity{Amortised O(1) to dispatch. Amortised O(1) to complete if truncating file lengths is constant time.} \exceptionmodelfree \qexample{readwrite_example} */ inline void truncate(future<> _precondition, off_t newsize) { auto h=detail::async_truncate(newsize)(std::move(_precondition)).get_handle(); } /*! \brief Synchronous file length truncation after a preceding operation. \docs_truncate \param _ec Error code to set. \param _precondition The precondition to use. \param newsize The new size for the file. \ingroup truncate \qbk{distinguish, non throwing} \complexity{Amortised O(1) to dispatch. Amortised O(1) to complete if truncating file lengths is constant time.} \exceptionmodelfree \qexample{readwrite_example} */ inline void truncate(error_code &_ec, future<> _precondition, off_t newsize) { auto h=detail::async_truncate(newsize)(std::move(_precondition)).get_handle(_ec); } /*! \brief Asynchronous directory enumeration after a preceding operation. \docs_enumerate \return A `future, bool>>` \param _precondition The precondition to use. \param _maxitems The maximum number of items to return in this request. Note that setting to one will often invoke two syscalls. \param _restart Restarts the enumeration for this open directory handle. \param _glob An optional shell glob by which to filter the items returned. Done kernel side on Windows, user side on POSIX. \param _metadata The metadata to prefetch for each item enumerated. AFIO may fetch more metadata than requested if it is cost free. \param _filtering Any filtering you want AFIO to do for you. \ingroup enumerate \qbk{distinguish, maxitems first} \raceguarantees{ [raceguarantee FreeBSD, Linux, OS X..Race free per batch of up to ['maxitems] for ino and type only. Remember that many filing systems will recycle inodes such that a created file will get the inode of a just deleted file, so comparing inodes for equivalence to a direntry() won't help you.] [raceguarantee Windows..Race free per batch of up to ['maxitems] for ino, type, atim, mtim, ctim, size, allocated, birthtim, sparse, compressed.] } \complexity{Amortised O(1) to dispatch. Amortised O(M) to complete where M is the average number of entries in each directory.} \exceptionmodelfree \qexample{enumerate_example} */ inline future, bool>> async_enumerate(future<> _precondition, size_t _maxitems = 2, bool _restart = true, path _glob = path(), metadata_flags _metadata = metadata_flags::None, enumerate_req::filter _filtering = enumerate_req::filter::fastdeleted) { return detail::async_enumerate(_maxitems, _restart, std::move(_glob), _metadata, _filtering)(std::move(_precondition)); } /*! \brief Synchronous directory enumeration after a preceding operation. \docs_enumerate \return A vector of results and a bool indicating if there is more. \param _precondition The precondition to use. \param _maxitems The maximum number of items to return in this request. Note that setting to one will often invoke two syscalls. \param _restart Restarts the enumeration for this open directory handle. \param _glob An optional shell glob by which to filter the items returned. Done kernel side on Windows, user side on POSIX. \param _metadata The metadata to prefetch for each item enumerated. AFIO may fetch more metadata than requested if it is cost free. \param _filtering Any filtering you want AFIO to do for you. \ingroup enumerate \qbk{distinguish, max items first throwing} \raceguarantees{ [raceguarantee FreeBSD, Linux, OS X..Race free per batch of up to ['maxitems] for ino and type only. Remember that many filing systems will recycle inodes such that a created file will get the inode of a just deleted file, so comparing inodes for equivalence to a direntry() won't help you.] [raceguarantee Windows..Race free per batch of up to ['maxitems] for ino, type, atim, mtim, ctim, size, allocated, birthtim, sparse, compressed.] } \complexity{Amortised O(1) to dispatch. Amortised O(M) to complete where M is the average number of entries in each directory.} \exceptionmodelfree \qexample{enumerate_example} */ inline std::pair, bool> enumerate(future<> _precondition, size_t _maxitems = 2, bool _restart = true, path _glob = path(), metadata_flags _metadata = metadata_flags::None, enumerate_req::filter _filtering = enumerate_req::filter::fastdeleted) { return detail::async_enumerate(_maxitems, _restart, std::move(_glob), _metadata, _filtering)(std::move(_precondition)).get(); } /*! \brief Synchronous directory enumeration after a preceding operation. \docs_enumerate \return A vector of results and a bool indicating if there is more. \param _ec Error code to set. \param _precondition The precondition to use. \param _maxitems The maximum number of items to return in this request. Note that setting to one will often invoke two syscalls. \param _restart Restarts the enumeration for this open directory handle. \param _glob An optional shell glob by which to filter the items returned. Done kernel side on Windows, user side on POSIX. \param _metadata The metadata to prefetch for each item enumerated. AFIO may fetch more metadata than requested if it is cost free. \param _filtering Any filtering you want AFIO to do for you. \ingroup enumerate \qbk{distinguish, max items first non throwing} \raceguarantees{ [raceguarantee FreeBSD, Linux, OS X..Race free per batch of up to ['maxitems] for ino and type only. Remember that many filing systems will recycle inodes such that a created file will get the inode of a just deleted file, so comparing inodes for equivalence to a direntry() won't help you.] [raceguarantee Windows..Race free per batch of up to ['maxitems] for ino, type, atim, mtim, ctim, size, allocated, birthtim, sparse, compressed.] } \complexity{Amortised O(1) to dispatch. Amortised O(M) to complete where M is the average number of entries in each directory.} \exceptionmodelfree \qexample{enumerate_example} */ inline std::pair, bool> enumerate(error_code &_ec, future<> _precondition, size_t _maxitems = 2, bool _restart = true, path _glob = path(), metadata_flags _metadata = metadata_flags::None, enumerate_req::filter _filtering = enumerate_req::filter::fastdeleted) { auto ret= detail::async_enumerate(_maxitems, _restart, std::move(_glob), _metadata, _filtering)(std::move(_precondition)); if(!(_ec=ret.get_error())) return ret.get(); return std::pair, bool>(); } /*! \brief Asynchronous directory enumeration after a preceding operation. \docs_enumerate \return A `future, bool>>` \param _precondition The precondition to use. \param _glob A shell glob by which to filter the items returned. Done kernel side on Windows, user side on POSIX. \param _maxitems The maximum number of items to return in this request. Note that setting to one will often invoke two syscalls. \param _restart Restarts the enumeration for this open directory handle. \param _metadata The metadata to prefetch for each item enumerated. AFIO may fetch more metadata than requested if it is cost free. \param _filtering Any filtering you want AFIO to do for you. \ingroup enumerate \qbk{distinguish, glob first} \raceguarantees{ [raceguarantee FreeBSD, Linux, OS X..Race free per batch of up to ['maxitems] for ino and type only. Remember that many filing systems will recycle inodes such that a created file will get the inode of a just deleted file, so comparing inodes for equivalence to a direntry() won't help you.] [raceguarantee Windows..Race free per batch of up to ['maxitems] for ino, type, atim, mtim, ctim, size, allocated, birthtim, sparse, compressed.] } \complexity{Amortised O(1) to dispatch. Amortised O(M) to complete where M is the average number of entries in each directory.} \exceptionmodelfree \qexample{enumerate_example} */ inline future, bool>> async_enumerate(future<> _precondition, path _glob, size_t _maxitems = 2, bool _restart = true, metadata_flags _metadata = metadata_flags::None, enumerate_req::filter _filtering = enumerate_req::filter::fastdeleted) { return detail::async_enumerate(_maxitems, _restart, std::move(_glob), _metadata, _filtering)(std::move(_precondition)); } /*! \brief Synchronous directory enumeration after a preceding operation. \docs_enumerate \return A vector of results and a bool indicating if there is more. \param _precondition The precondition to use. \param _glob A shell glob by which to filter the items returned. Done kernel side on Windows, user side on POSIX. \param _maxitems The maximum number of items to return in this request. Note that setting to one will often invoke two syscalls. \param _restart Restarts the enumeration for this open directory handle. \param _metadata The metadata to prefetch for each item enumerated. AFIO may fetch more metadata than requested if it is cost free. \param _filtering Any filtering you want AFIO to do for you. \ingroup enumerate \qbk{distinguish, glob first throwing} \raceguarantees{ [raceguarantee FreeBSD, Linux, OS X..Race free per batch of up to ['maxitems] for ino and type only. Remember that many filing systems will recycle inodes such that a created file will get the inode of a just deleted file, so comparing inodes for equivalence to a direntry() won't help you.] [raceguarantee Windows..Race free per batch of up to ['maxitems] for ino, type, atim, mtim, ctim, size, allocated, birthtim, sparse, compressed.] } \complexity{Amortised O(1) to dispatch. Amortised O(M) to complete where M is the average number of entries in each directory.} \exceptionmodelfree \qexample{enumerate_example} */ inline std::pair, bool> enumerate(future<> _precondition, path _glob, size_t _maxitems = 2, bool _restart = true, metadata_flags _metadata = metadata_flags::None, enumerate_req::filter _filtering = enumerate_req::filter::fastdeleted) { return detail::async_enumerate(_maxitems, _restart, std::move(_glob), _metadata, _filtering)(std::move(_precondition)).get(); } /*! \brief Synchronous directory enumeration after a preceding operation. \docs_enumerate \return A vector of results and a bool indicating if there is more. \param _ec Error code to set. \param _precondition The precondition to use. \param _glob A shell glob by which to filter the items returned. Done kernel side on Windows, user side on POSIX. \param _maxitems The maximum number of items to return in this request. Note that setting to one will often invoke two syscalls. \param _restart Restarts the enumeration for this open directory handle. \param _metadata The metadata to prefetch for each item enumerated. AFIO may fetch more metadata than requested if it is cost free. \param _filtering Any filtering you want AFIO to do for you. \ingroup enumerate \qbk{distinguish, glob first non throwing} \raceguarantees{ [raceguarantee FreeBSD, Linux, OS X..Race free per batch of up to ['maxitems] for ino and type only. Remember that many filing systems will recycle inodes such that a created file will get the inode of a just deleted file, so comparing inodes for equivalence to a direntry() won't help you.] [raceguarantee Windows..Race free per batch of up to ['maxitems] for ino, type, atim, mtim, ctim, size, allocated, birthtim, sparse, compressed.] } \complexity{Amortised O(1) to dispatch. Amortised O(M) to complete where M is the average number of entries in each directory.} \exceptionmodelfree \qexample{enumerate_example} */ inline std::pair, bool> enumerate(error_code &_ec, future<> _precondition, path _glob, size_t _maxitems = 2, bool _restart = true, metadata_flags _metadata = metadata_flags::None, enumerate_req::filter _filtering = enumerate_req::filter::fastdeleted) { auto ret = detail::async_enumerate(_maxitems, _restart, std::move(_glob), _metadata, _filtering)(std::move(_precondition)); if (!(_ec = ret.get_error())) return ret.get(); return std::pair, bool>(); } /*! \brief Asynchronous directory enumeration after a preceding operation. \docs_enumerate \return A `future, bool>>` \param _precondition The precondition to use. \param _metadata The metadata to prefetch for each item enumerated. AFIO may fetch more metadata than requested if it is cost free. \param _maxitems The maximum number of items to return in this request. Note that setting to one will often invoke two syscalls. \param _restart Restarts the enumeration for this open directory handle. \param _glob An optional shell glob by which to filter the items returned. Done kernel side on Windows, user side on POSIX. \param _filtering Any filtering you want AFIO to do for you. \ingroup enumerate \qbk{distinguish, metadata first} \raceguarantees{ [raceguarantee FreeBSD, Linux, OS X..Race free per batch of up to ['maxitems] for ino and type only. Remember that many filing systems will recycle inodes such that a created file will get the inode of a just deleted file, so comparing inodes for equivalence to a direntry() won't help you.] [raceguarantee Windows..Race free per batch of up to ['maxitems] for ino, type, atim, mtim, ctim, size, allocated, birthtim, sparse, compressed.] } \complexity{Amortised O(1) to dispatch. Amortised O(M) to complete where M is the average number of entries in each directory.} \exceptionmodelfree \qexample{enumerate_example} */ inline future, bool>> async_enumerate(future<> _precondition, metadata_flags _metadata, size_t _maxitems = 2, bool _restart = true, path _glob = path(), enumerate_req::filter _filtering = enumerate_req::filter::fastdeleted) { return detail::async_enumerate(_maxitems, _restart, _glob, _metadata, _filtering)(std::move(_precondition)); } /*! \brief Synchronous directory enumeration after a preceding operation. \docs_enumerate \return A vector of results and a bool indicating if there is more. \param _precondition The precondition to use. \param _metadata The metadata to prefetch for each item enumerated. AFIO may fetch more metadata than requested if it is cost free. \param _maxitems The maximum number of items to return in this request. Note that setting to one will often invoke two syscalls. \param _restart Restarts the enumeration for this open directory handle. \param _glob An optional shell glob by which to filter the items returned. Done kernel side on Windows, user side on POSIX. \param _filtering Any filtering you want AFIO to do for you. \ingroup enumerate \qbk{distinguish, metadata first throwing} \raceguarantees{ [raceguarantee FreeBSD, Linux, OS X..Race free per batch of up to ['maxitems] for ino and type only. Remember that many filing systems will recycle inodes such that a created file will get the inode of a just deleted file, so comparing inodes for equivalence to a direntry() won't help you.] [raceguarantee Windows..Race free per batch of up to ['maxitems] for ino, type, atim, mtim, ctim, size, allocated, birthtim, sparse, compressed.] } \complexity{Amortised O(1) to dispatch. Amortised O(M) to complete where M is the average number of entries in each directory.} \exceptionmodelfree \qexample{enumerate_example} */ inline std::pair, bool> enumerate(future<> _precondition, metadata_flags _metadata, size_t _maxitems = 2, bool _restart = true, path _glob = path(), enumerate_req::filter _filtering = enumerate_req::filter::fastdeleted) { return detail::async_enumerate(_maxitems, _restart, std::move(_glob), _metadata, _filtering)(std::move(_precondition)).get(); } /*! \brief Synchronous directory enumeration after a preceding operation. \docs_enumerate \return A vector of results and a bool indicating if there is more. \param _ec Error code to set. \param _precondition The precondition to use. \param _metadata The metadata to prefetch for each item enumerated. AFIO may fetch more metadata than requested if it is cost free. \param _maxitems The maximum number of items to return in this request. Note that setting to one will often invoke two syscalls. \param _restart Restarts the enumeration for this open directory handle. \param _glob An optional shell glob by which to filter the items returned. Done kernel side on Windows, user side on POSIX. \param _filtering Any filtering you want AFIO to do for you. \ingroup enumerate \qbk{distinguish, metadata first non throwing} \raceguarantees{ [raceguarantee FreeBSD, Linux, OS X..Race free per batch of up to ['maxitems] for ino and type only. Remember that many filing systems will recycle inodes such that a created file will get the inode of a just deleted file, so comparing inodes for equivalence to a direntry() won't help you.] [raceguarantee Windows..Race free per batch of up to ['maxitems] for ino, type, atim, mtim, ctim, size, allocated, birthtim, sparse, compressed.] } \complexity{Amortised O(1) to dispatch. Amortised O(M) to complete where M is the average number of entries in each directory.} \exceptionmodelfree \qexample{enumerate_example} */ inline std::pair, bool> enumerate(error_code &_ec, future<> _precondition, metadata_flags _metadata, size_t _maxitems = 2, bool _restart = true, path _glob = path(), enumerate_req::filter _filtering = enumerate_req::filter::fastdeleted) { auto ret = detail::async_enumerate(_maxitems, _restart, std::move(_glob), _metadata, _filtering)(std::move(_precondition)); if (!(_ec = ret.get_error())) return ret.get(); return std::pair, bool>(); } /*! \brief Asynchronous extent enumeration after a preceding operation. \docs_extents \return A `future>>` \param _precondition The precondition to use. \ingroup extents \raceguarantees{ [raceguarantee FreeBSD, Linux, OS X..Very racy, even individual extent offset and length can race. The following filters are applied before returning results: (i) Any extent whose end appears before its start is retried (ii) Sequences of contiguous extents are merged into single extents.] [raceguarantee Windows..Race free.] } \complexity{Amortised O(1) to dispatch. Amortised O(M) to complete where M is the average number of extents in each file.} \exceptionmodelfree \qexample{extents_example} */ inline future>> async_extents(future<> _precondition) { return detail::async_extents()(std::move(_precondition)); } /*! \brief Synchronous extent enumeration after a preceding operation. \docs_extents \return A vector of extents \param _precondition The precondition to use. \ingroup extents \qbk{distinguish, throwing} \raceguarantees{ [raceguarantee FreeBSD, Linux, OS X..Very racy, even individual extent offset and length can race. The following filters are applied before returning results: (i) Any extent whose end appears before its start is retried (ii) Sequences of contiguous extents are merged into single extents.] [raceguarantee Windows..Race free.] } \complexity{Amortised O(1) to dispatch. Amortised O(M) to complete where M is the average number of extents in each file.} \exceptionmodelfree \qexample{extents_example} */ inline std::vector> extents(future<> _precondition) { return detail::async_extents()(std::move(_precondition)).get(); } /*! \brief Synchronous extent enumeration after a preceding operation. \docs_extents \return A vector of extents \param _ec Error code to set. \param _precondition The precondition to use. \ingroup extents \qbk{distinguish, non throwing} \raceguarantees{ [raceguarantee FreeBSD, Linux, OS X..Very racy, even individual extent offset and length can race. The following filters are applied before returning results: (i) Any extent whose end appears before its start is retried (ii) Sequences of contiguous extents are merged into single extents.] [raceguarantee Windows..Race free.] } \complexity{Amortised O(1) to dispatch. Amortised O(M) to complete where M is the average number of extents in each file.} \exceptionmodelfree \qexample{extents_example} */ inline std::vector> extents(error_code &_ec, future<> _precondition) { auto ret = detail::async_extents()(std::move(_precondition)); if (!(_ec = ret.get_error())) return ret.get(); return std::vector>(); } /*! \brief Asynchronous volume enumeration after a preceding operation. \docs_statfs \return A `future` \param _precondition The precondition to use. \param req A metadata request. \ingroup statfs \raceguarantees{ [raceguarantee FreeBSD, OS X..Race free.] [raceguarantee Linux..The following items are fetched in a single snapshot: bsize, iosize, blocks, bfree, bavail, files, ffree, namemax, fsid, flags.rdonly, flags.noexec, flags.nosuid.] [raceguarantee Windows..The following snapshot categories apply: (i) flags, namemax, fstypename (ii) bsize, blocks, bfree, bavail. Everything else is fetched separately.] } \complexity{Amortised O(1) to dispatch. Amortised O(1) to complete.} \exceptionmodelfree \qexample{statfs_example} */ inline future async_statfs(future<> _precondition, fs_metadata_flags req) { return detail::async_statfs(req)(std::move(_precondition)); } /*! \brief Synchronous volume enumeration after a preceding operation. \docs_statfs \return The volume metadata requested. \param _precondition The precondition to use. \param req A metadata request. \ingroup statfs \qbk{distinguish, throwing} \raceguarantees{ [raceguarantee FreeBSD, OS X..Race free.] [raceguarantee Linux..The following items are fetched in a single snapshot: bsize, iosize, blocks, bfree, bavail, files, ffree, namemax, fsid, flags.rdonly, flags.noexec, flags.nosuid.] [raceguarantee Windows..The following snapshot categories apply: (i) flags, namemax, fstypename (ii) bsize, blocks, bfree, bavail. Everything else is fetched separately.] } \complexity{Amortised O(1) to dispatch. Amortised O(1) to complete.} \exceptionmodelfree \qexample{statfs_example} */ inline statfs_t statfs(future<> _precondition, fs_metadata_flags req) { return detail::async_statfs(req)(std::move(_precondition)).get(); } /*! \brief Synchronous volume enumeration after a preceding operation. \docs_statfs \return The volume metadata requested. \param _ec Error code to set. \param _precondition The precondition to use. \param req A metadata request. \ingroup statfs \qbk{distinguish, nonthrowing} \raceguarantees{ [raceguarantee FreeBSD, OS X..Race free.] [raceguarantee Linux..The following items are fetched in a single snapshot: bsize, iosize, blocks, bfree, bavail, files, ffree, namemax, fsid, flags.rdonly, flags.noexec, flags.nosuid.] [raceguarantee Windows..The following snapshot categories apply: (i) flags, namemax, fstypename (ii) bsize, blocks, bfree, bavail. Everything else is fetched separately.] } \complexity{Amortised O(1) to dispatch. Amortised O(1) to complete.} \exceptionmodelfree \qexample{statfs_example} */ inline statfs_t statfs(error_code &_ec, future<> _precondition, fs_metadata_flags req) { auto ret = detail::async_statfs(req)(std::move(_precondition)); if (!(_ec = ret.get_error())) return ret.get(); return statfs_t(); } /*! \brief Make ready a future after a precondition future readies. \return A future which returns out after precondition signals. \param precondition The future which must signal before the returned future signals. \param out The future to return. \ingroup async_file_io_dispatcher */ inline future<> depends(future<> precondition, future<> out) { return precondition.parent()->depends(precondition, out); } //! Utility routines often useful when using AFIO namespace utils { /*! \brief Returns the page sizes of this architecture which is useful for calculating direct i/o multiples. \param only_actually_available Only return page sizes actually available to the user running this process \return The page sizes of this architecture. \ingroup utils \complexity{Whatever the system API takes (one would hope constant time).} \exceptionmodel{Any error from the operating system or std::bad_alloc.} */ BOOST_AFIO_HEADERS_ONLY_FUNC_SPEC std::vector page_sizes(bool only_actually_available = true) noexcept; /*! \brief Returns a reasonable default size for page_allocator, typically the closest page size from page_sizes() to 1Mb. \return A value of a TLB large page size close to 1Mb. \ingroup utils \complexity{Whatever the system API takes (one would hope constant time).} \exceptionmodel{Any error from the operating system or std::bad_alloc.} */ inline size_t file_buffer_default_size() noexcept { static size_t size; if (!size) { std::vector sizes(page_sizes(true)); for (auto &i : sizes) if (i >= 1024 * 1024) { size = i; break; } if (!size) size = 1024 * 1024; } return size; } /*! \brief Fills the buffer supplied with cryptographically strong randomness. Uses the OS kernel API. \param buffer A buffer to fill \param bytes How many bytes to fill \ingroup utils \complexity{Whatever the system API takes.} \exceptionmodel{Any error from the operating system.} */ BOOST_AFIO_HEADERS_ONLY_FUNC_SPEC void random_fill(char *buffer, size_t bytes); /*! \brief Converts a number to a hex string. Out buffer can be same as in buffer. Note that the character range used is a 16 item table of: 0123456789abcdef This lets one pack one byte of input into two bytes of output. \ingroup utils \complexity{O(N) where N is the length of the number.} \exceptionmodel{Throws exception if output buffer is too small for input.} */ #ifdef _MSC_VER #pragma warning(push) #pragma warning(disable: 6293) // MSVC sanitiser warns that we wrap n in the for loop #endif inline size_t to_hex_string(char *out, size_t outlen, const char *_in, size_t inlen) { unsigned const char *in = (unsigned const char *) _in; static BOOST_CONSTEXPR_OR_CONST char table[] = "0123456789abcdef"; if(outlen> 4) & 0xf]; out[n * 2 + 2] = table[in[n+1] & 0xf]; out[n * 2 + 1] = table[(in[n] >> 4) & 0xf]; out[n * 2 + 0] = table[in[n] & 0xf]; } if(inlen&1) { out[1] = table[(in[0] >> 4) & 0xf]; out[0] = table[in[0] & 0xf]; } return inlen*2; } #ifdef _MSC_VER #pragma warning(pop) #endif //! \overload inline std::string to_hex_string(std::string in) { std::string out(in.size() * 2, ' '); to_hex_string(const_cast(out.data()), out.size(), in.data(), in.size()); return out; } /*! \brief Converts a hex string to a number. Out buffer can be same as in buffer. Note that this routine is about 43% slower than to_hex_string(), half of which is due to input validation. \ingroup utils \complexity{O(N) where N is the length of the string.} \exceptionmodel{Throws exception if output buffer is too small for input or input size is not multiple of two.} */ inline size_t from_hex_string(char *out, size_t outlen, const char *in, size_t inlen) { if (inlen % 2) BOOST_AFIO_THROW(std::invalid_argument("Input buffer not multiple of two.")); if (outlen unsigned char { #if 1 // ASCII starting from 48 is 0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~ // 48 65 97 static BOOST_CONSTEXPR_OR_CONST unsigned char table[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, // +10 = 58 255, 255, 255, 255, 255, 255, 255, // +7 = 65 10, 11, 12, 13, 14, 15, // +6 = 71 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, // +26 = 97 10, 11, 12, 13, 14, 15 }; unsigned char r=255; if(c>=48 && c<=102) r=table[c-48]; if(r==255) is_invalid=true; return r; #else if(c>='0' && c<='9') return c-'0'; if(c>='a' && c<='f') return c-'a'+10; if(c>='A' && c<='F') return c-'A'+10; BOOST_AFIO_THROW(std::invalid_argument("Input is not hexadecimal.")); #endif }; for (size_t n = 0; n(ret.data()), randomlen); to_hex_string(const_cast(ret.data()), outlen, ret.data(), randomlen); return ret; } #ifndef BOOST_AFIO_SECDEC_INTRINSICS # if defined(__GCC__) || defined(__clang__) # define BOOST_AFIO_SECDEC_INTRINSICS 1 # elif defined(_MSC_VER) && (defined(_M_X64) || _M_IX86_FP==1) # define BOOST_AFIO_SECDEC_INTRINSICS 1 # endif #endif #ifndef BOOST_AFIO_SECDEC_INTRINSICS # define BOOST_AFIO_SECDEC_INTRINSICS 0 #endif /*! \class secded_ecc \brief Calculates the single error correcting double error detecting (SECDED) Hamming Error Correcting Code for a \em blocksize block of bytes. For example, a secdec_ecc<8> would be the very common 72,64 Hamming code used in ECC RAM, or secdec_ecc<4096> would be for a 32784,32768 Hamming code. Did you know that some non-ECC RAM systems can see 1e-12 flips/bit/hour, which is 3.3 bits flipped in a 16Gb RAM system per 24 hours). See Schroeder, Pinheiro and Weber (2009) 'DRAM Errors in the Wild: A Large-Scale Field Study'. After construction during which lookup tables are built, no state is modified and therefore this class is safe for static storage (indeed if C++ 14 is available, the constructor is constexpr). The maximum number of bits in a code is a good four billion, I did try limiting it to 65536 for performance but it wasn't worth it, and one might want > 8Kb blocks maybe. As with all SECDED ECC, undefined behaviour occurs when more than two bits of error are present or the ECC supplied is incorrect. You should combine this SECDED with a robust hash which can tell you definitively if a buffer is error free or not rather than relying on this to correctly do so. The main intended use case for this routine is calculating the ECC on data being written to disc, and hence that is where performance has been maximised. It is not expected that this routine will be frequently called on data being read from disc i.e. only when its hash doesn't match its contents which should be very rare, and then a single bit heal using this routine is attempted before trying again with the hash. Care was taken that really enormous SECDEDs are fast, in fact tuning was mostly done for the 32784,32768 code which can heal one bad bit per 4Kb page as the main thing we have in mind is achieving reliable filing system code on computers without ECC RAM and in which sustained large quantities of random disc i/o produce a worrying number of flipped bits in a 24 hour period (anywhere between 0 and 3 on my hardware here, average is about 0.8). Performance of the fixed block size routine where you supply whole chunks of \em blocksize is therefore \b particularly excellent as I spent a lot of time tuning it for Ivy Bridge and later out of order architectures: an amazing 22 cycles per byte for the 32784,32768 code, which is a testament to modern out of order CPUs (remember SECDED inherently must work a bit at a time, so that's just 2.75 amortised CPU cycles per bit which includes a table load, a bit test, and a conditional XOR) i.e. it's pushing about 1.5 ops per clock cycle. On my 3.9Ghz i7-3770K here, I see about 170Mb/sec per CPU core. The variable length routine is necessarily much slower as it must work in single bytes, and achieves 72 cycles per byte, or 9 cycles per bit (64Mb/sec per CPU core). \ingroup utils \complexity{O(N) where N is the blocksize} \exceptionmodel{Throws constexpr exceptions in constructor only, otherwise entirely noexcept.} */ template class secded_ecc { public: typedef unsigned int result_type; //!< The largest ECC which can be calculated private: static BOOST_CONSTEXPR_OR_CONST size_t bits_per_byte=8; typedef unsigned char unit_type; // The batch unit of processing result_type bitsvalid; // Many CPUs (x86) are slow doing variable bit shifts, so keep a table result_type ecc_twospowers[sizeof(result_type)*bits_per_byte]; unsigned short ecc_table[blocksize*bits_per_byte]; static bool _is_single_bit_set(result_type x) { #ifndef _MSC_VER #if defined(__i386__) || defined(__x86_64__) #ifndef __SSE4_2__ // Do a once off runtime check static int have_popcnt=[]{ size_t cx, dx; #if defined(__x86_64__) asm("cpuid": "=c" (cx), "=d" (dx) : "a" (1), "b" (0), "c" (0), "d" (0)); #else asm("pushl %%ebx\n\tcpuid\n\tpopl %%ebx\n\t": "=c" (cx), "=d" (dx) : "a" (1), "c" (0), "d" (0)); #endif return (dx&(1<<26))!=0/*SSE2*/ && (cx&(1<<23))!=0/*POPCNT*/; }(); if(have_popcnt) #endif { unsigned count; asm("popcnt %1,%0" : "=r"(count) : "rm"(x) : "cc"); return count==1; } #endif return __builtin_popcount(x)==1; #else x -= (x >> 1) & 0x55555555; x = (x & 0x33333333) + ((x >> 2) & 0x33333333); x = (x + (x >> 4)) & 0x0f0f0f0f; unsigned int count=(x * 0x01010101)>>24; return count==1; #if 0 x -= (x >> 1) & 0x5555555555555555ULL; x = (x & 0x3333333333333333ULL) + ((x >> 2) & 0x3333333333333333ULL); x = (x + (x >> 4)) & 0x0f0f0f0f0f0f0f0fULL; unsigned long long count=(x * 0x0101010101010101ULL)>>56; return count==1; #endif #endif } public: //! Constructs an instance, configuring the necessary lookup tables BOOSTLITE_CONSTEXPR secded_ecc() { for(size_t n=0; nsizeof(result_type)) BOOST_AFIO_THROW(std::runtime_error("ECC would exceed the size of result_type!")); for(result_type i=0; i=ecc_twospowers[topbit]) b++; //while(b>ecc_twospowers(_topbit+1)) _topbit++; //b+=_topbit; //if(b>=ecc_twospowers(_topbit)) b++; #else for(size_t p=0; ecc_twospowers[p]<(b+1); p++) b++; #endif ecc_table[i]=(unsigned short) b; if(b>(unsigned short)-1) BOOST_AFIO_THROW(std::runtime_error("Precalculated table has exceeded its bounds")); } } //! The number of bits valid in result_type constexpr result_type result_bits_valid() const noexcept { return bitsvalid; } //! Accumulate ECC from fixed size buffer result_type operator()(result_type ecc, const char *buffer) const noexcept { if(blocksize class page_allocator { public: typedef T value_type; typedef T* pointer; typedef const T* const_pointer; typedef T& reference; typedef const T& const_reference; typedef size_t size_type; typedef ptrdiff_t difference_type; typedef std::true_type propagate_on_container_move_assignment; typedef std::true_type is_always_equal; template struct rebind { typedef page_allocator other; }; page_allocator() noexcept {} template page_allocator(const page_allocator&) noexcept {} size_type max_size() const noexcept { return size_type(~0) / sizeof(T); } pointer address(reference x) const noexcept { return std::addressof(x); } const_pointer address(const_reference x) const noexcept { return std::addressof(x); } pointer allocate(size_type n, const void *hint = 0) { if(n>max_size()) throw std::bad_alloc(); auto mem(detail::allocate_large_pages(n * sizeof(T))); if (mem.p == nullptr) throw std::bad_alloc(); return reinterpret_cast(mem.p); } void deallocate(pointer p, size_type n) { if(n>max_size()) throw std::bad_alloc(); detail::deallocate_large_pages(p, n * sizeof(T)); } template void construct(U* p, Args&&... args) { ::new(reinterpret_cast(p)) U(std::forward(args)...); } template void destroy(U* p) { p->~U(); } }; template <> class page_allocator { public: typedef void value_type; typedef void* pointer; typedef const void* const_pointer; typedef std::true_type propagate_on_container_move_assignment; typedef std::true_type is_always_equal; template struct rebind { typedef page_allocator other; }; }; template inline bool operator==(const page_allocator &, const page_allocator &) noexcept { return true; } } BOOST_AFIO_V2_NAMESPACE_END // Specialise std::hash<> for directory_entry #ifndef BOOST_AFIO_DISABLE_STD_HASH_SPECIALIZATION #include namespace std { template<> struct hash { public: size_t operator()(const BOOST_AFIO_V2_NAMESPACE::path &p) const { return BOOST_AFIO_V2_NAMESPACE::path_hash()(p); } }; template<> struct hash { public: size_t operator()(const BOOST_AFIO_V2_NAMESPACE::directory_entry &p) const { return BOOST_AFIO_V2_NAMESPACE::directory_entry_hash()(p); } }; }//namesapce std #endif #ifdef BOOST_MSVC #pragma warning(pop) #endif #if BOOST_AFIO_HEADERS_ONLY == 1 && !defined(DOXYGEN_SHOULD_SKIP_THIS) #undef BOOST_AFIO_VALIDATE_INPUTS // Let BOOST_AFIO_NEVER_VALIDATE_INPUTS take over #define BOOST_AFIO_HEADER_INCLUDED 1 #include "detail/impl/afio.ipp" #undef BOOST_AFIO_HEADER_INCLUDED #endif #endif #endif