Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/windirstat/llfio.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNiall Douglas (s [underscore] sourceforge {at} nedprod [dot] com) <spamtrap@nedprod.com>2017-09-18 03:06:59 +0300
committerNiall Douglas (s [underscore] sourceforge {at} nedprod [dot] com) <spamtrap@nedprod.com>2017-09-18 03:06:59 +0300
commit60965da727d7447b4724e64083e0d939764b1336 (patch)
treed564f89fa73230ddb2b92730c7ea8aab9d43425e
parenta8fd33814a0e858621940d3f91a82457b73e0a53 (diff)
Added a new free function construct<T>() through which handle implementations register their static constructor functions.
Added a new algorithm adapting any handle implementation to cache its parent handle in a process wide registry.
-rw-r--r--Readme.md6
-rw-r--r--cmake/headers.cmake3
-rw-r--r--include/afio/revision.hpp6
-rw-r--r--include/afio/v2.0/afio.hpp1
-rw-r--r--include/afio/v2.0/algorithm/cached_parent_handle_adapter.hpp177
-rw-r--r--include/afio/v2.0/async_file_handle.hpp26
-rw-r--r--include/afio/v2.0/config.hpp6
-rw-r--r--include/afio/v2.0/detail/impl/cached_parent_handle_adapter.ipp128
-rw-r--r--include/afio/v2.0/directory_handle.hpp12
-rw-r--r--include/afio/v2.0/file_handle.hpp12
-rw-r--r--include/afio/v2.0/fs_handle.hpp11
-rw-r--r--include/afio/v2.0/handle.hpp21
-rw-r--r--include/afio/v2.0/map_handle.hpp19
-rw-r--r--include/afio/v2.0/mapped_file_handle.hpp14
-rw-r--r--include/afio/v2.0/path_handle.hpp8
-rw-r--r--test/tests/current_path.cpp149
16 files changed, 517 insertions, 82 deletions
diff --git a/Readme.md b/Readme.md
index bcd47f3d..d125dbb7 100644
--- a/Readme.md
+++ b/Readme.md
@@ -6,12 +6,6 @@ Tarballs of source and prebuilt binaries for Linux x64 and Windows x64: http://m
### Immediate todos in order of priority:
-- [ ] Make some system for registering static constructors
- - Then make the random_X and temp_X implementations common
- - Then add a sibling_X()
-- [ ] Make a templated file handle adapter which keeps a `shared_ptr<directory_handle>`
-for the parent of the inode, thus making race free operations much quicker and
-reliable but at the cost of construction time.
- [ ] `atomic_append` isn't actually being tested in shared_fs_mutex
- [ ] Implement a non-toy ACID key-value BLOB store and send it to Boost for peer review.
- [ ] All time based kernel tests need to use soak test based API and auto adjust to
diff --git a/cmake/headers.cmake b/cmake/headers.cmake
index 45094bec..9679c71f 100644
--- a/cmake/headers.cmake
+++ b/cmake/headers.cmake
@@ -7,6 +7,7 @@ set(afio_HEADERS
"include/afio/ntkernel-error-category/include/ntkernel_category.hpp"
"include/afio/revision.hpp"
"include/afio/v2.0/afio.hpp"
+ "include/afio/v2.0/algorithm/cached_parent_handle_adapter.hpp"
"include/afio/v2.0/algorithm/mapped_view.hpp"
"include/afio/v2.0/algorithm/section_allocator.hpp"
"include/afio/v2.0/algorithm/shared_fs_mutex/atomic_append.hpp"
@@ -38,6 +39,7 @@ set(afio_HEADERS
"include/afio/version.hpp"
"include/afio/ntkernel-error-category/include/detail/ntkernel-table.ipp"
"include/afio/ntkernel-error-category/include/detail/ntkernel_category_impl.ipp"
+ "include/afio/v2.0/detail/impl/cached_parent_handle_adapter.ipp"
"include/afio/v2.0/detail/impl/path_discovery.ipp"
"include/afio/v2.0/detail/impl/posix/async_file_handle.ipp"
"include/afio/v2.0/detail/impl/posix/directory_handle.ipp"
@@ -48,6 +50,7 @@ set(afio_HEADERS
"include/afio/v2.0/detail/impl/posix/io_service.ipp"
"include/afio/v2.0/detail/impl/posix/map_handle.ipp"
"include/afio/v2.0/detail/impl/posix/mapped_file_handle.ipp"
+ "include/afio/v2.0/detail/impl/posix/path_discovery.ipp"
"include/afio/v2.0/detail/impl/posix/path_handle.ipp"
"include/afio/v2.0/detail/impl/posix/stat.ipp"
"include/afio/v2.0/detail/impl/posix/statfs.ipp"
diff --git a/include/afio/revision.hpp b/include/afio/revision.hpp
index df19d14a..ab33f1b5 100644
--- a/include/afio/revision.hpp
+++ b/include/afio/revision.hpp
@@ -1,4 +1,4 @@
// Note the second line of this file must ALWAYS be the git SHA, third line ALWAYS the git SHA update time
-#define AFIO_PREVIOUS_COMMIT_REF f6ce8d2618caedd8bd5e5637ff63dfa83f46180a
-#define AFIO_PREVIOUS_COMMIT_DATE "2017-09-15 02:15:16 +00:00"
-#define AFIO_PREVIOUS_COMMIT_UNIQUE f6ce8d26
+#define AFIO_PREVIOUS_COMMIT_REF a8fd33814a0e858621940d3f91a82457b73e0a53
+#define AFIO_PREVIOUS_COMMIT_DATE "2017-09-17 01:28:52 +00:00"
+#define AFIO_PREVIOUS_COMMIT_UNIQUE a8fd3381
diff --git a/include/afio/v2.0/afio.hpp b/include/afio/v2.0/afio.hpp
index ca96d685..82ede786 100644
--- a/include/afio/v2.0/afio.hpp
+++ b/include/afio/v2.0/afio.hpp
@@ -63,6 +63,7 @@ import AFIO_MODULE_NAME;
#include "statfs.hpp"
#include "storage_profile.hpp"
+#include "algorithm/cached_parent_handle_adapter.hpp"
#include "algorithm/mapped_view.hpp"
#include "algorithm/section_allocator.hpp"
#include "algorithm/shared_fs_mutex/atomic_append.hpp"
diff --git a/include/afio/v2.0/algorithm/cached_parent_handle_adapter.hpp b/include/afio/v2.0/algorithm/cached_parent_handle_adapter.hpp
new file mode 100644
index 00000000..ea9ffb39
--- /dev/null
+++ b/include/afio/v2.0/algorithm/cached_parent_handle_adapter.hpp
@@ -0,0 +1,177 @@
+/* A parent handle caching adapter
+(C) 2017 Niall Douglas <http://www.nedproductions.biz/> (12 commits)
+File Created: Sept 2017
+
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License in the accompanying file
+Licence.txt or at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+
+Distributed under the Boost Software License, Version 1.0.
+ (See accompanying file Licence.txt or copy at
+ http://www.boost.org/LICENSE_1_0.txt)
+*/
+
+#ifndef AFIO_CACHED_PARENT_HANDLE_ADAPTER_HPP
+#define AFIO_CACHED_PARENT_HANDLE_ADAPTER_HPP
+
+#include "../directory_handle.hpp"
+
+//! \file cached_parent_handle_adapter.hpp Adapts any `fs_handle` to cache its parent directory handle
+AFIO_V2_NAMESPACE_BEGIN
+
+namespace algorithm
+{
+ namespace detail
+ {
+ struct AFIO_DECL cached_path_handle : public std::enable_shared_from_this<cached_path_handle>
+ {
+ directory_handle h;
+ filesystem::path _lastpath;
+ cached_path_handle(directory_handle &&_h)
+ : h(std::move(_h))
+ {
+ }
+ AFIO_HEADERS_ONLY_MEMFUNC_SPEC result<filesystem::path> current_path(const filesystem::path &append) noexcept;
+ };
+ using cached_path_handle_ptr = std::shared_ptr<cached_path_handle>;
+ // Passed the base and path of the adapted handle being created, returns a handle to the containing directory and the leafname
+ AFIO_HEADERS_ONLY_FUNC_SPEC std::pair<cached_path_handle_ptr, filesystem::path> get_cached_path_handle(const path_handle &base, path_view path);
+ }
+
+ /*! \brief Adapts any `construct()`-able implementation to cache its parent directory handle in a process wide cache.
+
+ For some use cases where one is calling `parent_path_handle()` or code which calls that function very frequently
+ e.g. calling `relink()` or `unlink()` a lot on many files with the same parent directory, having to constantly
+ fetch the current path, open the parent directory and verify inodes becomes unhelpfully inefficient. This
+ adapter keeps a process-wide hash table of directory handles shared between all instances of this adapter,
+ thus making calling `parent_path_handle()` almost zero cost.
+
+ This adapter is of especial use on platforms which do not reliably implement per-fd path tracking for regular
+ files (Apple MacOS, FreeBSD) as `current_path()` is reimplemented to use the current path of the shared parent
+ directory instead. One loses race freedom within the contained directory, but that is the case on POSIX anyway.
+
+ This adapter is also of use on platforms which do not implement path tracking for open handles at all (e.g.
+ Linux without `/proc` mounted) as the process-wide cache of directory handles retains the path of the directory
+ handle at the time of creation. Third party changes to the part of the filesystem you are working upon will
+ result in the inability to do race free unlinking etc, but if no third party changes are encountered it ought
+ to work well.
+ */
+ template <class T> AFIO_REQUIRES(sizeof(construct<T>) > 0) class AFIO_DECL cached_parent_handle_adapter : public T
+ {
+ static_assert(sizeof(construct<T>) > 0, "Type T must be registered with the construct<T> framework so cached_parent_handle_adapter<T> knows how to construct it");
+
+ public:
+ //! The handle type being adapted
+ using adapted_handle_type = T;
+ using path_type = typename T::path_type;
+
+ protected:
+ detail::cached_path_handle_ptr _sph;
+ filesystem::path _leafname;
+
+ public:
+ cached_parent_handle_adapter() = default;
+ cached_parent_handle_adapter(const cached_parent_handle_adapter &) = default;
+ cached_parent_handle_adapter(cached_parent_handle_adapter &&) = default;
+ cached_parent_handle_adapter &operator=(const cached_parent_handle_adapter &) = default;
+ cached_parent_handle_adapter &operator=(cached_parent_handle_adapter &&) = default;
+ cached_parent_handle_adapter(adapted_handle_type &&o, const path_handle &base, path_view path)
+ : adapted_handle_type(std::move(o))
+ {
+ auto r = detail::get_cached_path_handle(base, path);
+ _sph = std::move(r.first);
+ _leafname = std::move(r.second);
+ }
+ AFIO_HEADERS_ONLY_VIRTUAL_SPEC ~cached_parent_handle_adapter()
+ {
+ if(this->_v)
+ {
+ (void) cached_parent_handle_adapter::close();
+ }
+ }
+ AFIO_HEADERS_ONLY_VIRTUAL_SPEC result<path_type> current_path() const noexcept override
+ {
+ AFIO_LOG_FUNCTION_CALL(this);
+ return _sph->current_path(_leafname);
+ }
+ AFIO_HEADERS_ONLY_VIRTUAL_SPEC result<void> close() noexcept override
+ {
+ AFIO_LOG_FUNCTION_CALL(this);
+ OUTCOME_TRYV(adapted_handle_type::close());
+ _sph.reset();
+ _leafname.clear();
+ return success();
+ }
+ AFIO_HEADERS_ONLY_VIRTUAL_SPEC native_handle_type release() noexcept override
+ {
+ AFIO_LOG_FUNCTION_CALL(this);
+ _sph.reset();
+ _leafname.clear();
+ return adapted_handle_type::release();
+ }
+ AFIO_HEADERS_ONLY_VIRTUAL_SPEC result<path_handle> parent_path_handle(deadline /* unused */ = std::chrono::seconds(30)) const noexcept override
+ {
+ AFIO_LOG_FUNCTION_CALL(this);
+ OUTCOME_TRY(ret, _sph->h.clone());
+ return path_handle(std::move(ret));
+ }
+ };
+ /*! \brief Constructs a `T` adapted into a parent handle caching implementation.
+
+ This function works via the `construct<T>()` free function framework for which your `handle`
+ implementation must have registered its construction details.
+ */
+ template <class T, class... Args> inline result<cached_parent_handle_adapter<T>> cache_parent(Args &&... args) noexcept
+ {
+ construct<T> constructor{std::forward<Args>(args)...};
+ OUTCOME_TRY(h, constructor());
+ try
+ {
+ return cached_parent_handle_adapter<T>(std::move(h), constructor.base, constructor._path);
+ }
+ catch(...)
+ {
+ return error_from_exception();
+ }
+ }
+
+} // namespace
+
+//! \brief Constructor for `algorithm::::cached_parent_handle_adapter<T>`
+template <class T> struct construct<algorithm::cached_parent_handle_adapter<T>>
+{
+ construct<T> args;
+ result<algorithm::cached_parent_handle_adapter<T>> operator()() const noexcept
+ {
+ OUTCOME_TRY(h, args());
+ try
+ {
+ return algorithm::cached_parent_handle_adapter<T>(std::move(h), args.base, args._path);
+ }
+ catch(...)
+ {
+ return error_from_exception();
+ }
+ }
+};
+
+AFIO_V2_NAMESPACE_END
+
+#if AFIO_HEADERS_ONLY == 1 && !defined(DOXYGEN_SHOULD_SKIP_THIS)
+#define AFIO_INCLUDED_BY_HEADER 1
+#include "../detail/impl/cached_parent_handle_adapter.ipp"
+#undef AFIO_INCLUDED_BY_HEADER
+#endif
+
+#endif
diff --git a/include/afio/v2.0/async_file_handle.hpp b/include/afio/v2.0/async_file_handle.hpp
index 24bef6dc..5d5dd56f 100644
--- a/include/afio/v2.0/async_file_handle.hpp
+++ b/include/afio/v2.0/async_file_handle.hpp
@@ -426,6 +426,20 @@ public:
#endif
};
+//! \brief Constructor for `async_file_handle`
+template <> struct construct<async_file_handle>
+{
+ io_service &service;
+ const path_handle &base;
+ async_file_handle::path_view_type _path;
+ async_file_handle::mode _mode = async_file_handle::mode::read;
+ async_file_handle::creation _creation = async_file_handle::creation::open_existing;
+ async_file_handle::caching _caching = async_file_handle::caching::only_metadata;
+ async_file_handle::flag flags = async_file_handle::flag::none;
+ result<async_file_handle> operator()() const noexcept { return async_file_handle::async_file(service, base, _path, _mode, _creation, _caching, flags); }
+};
+
+
// BEGIN make_free_functions.py
//! Swap with another instance
inline void swap(async_file_handle &self, async_file_handle &o) noexcept
@@ -444,9 +458,11 @@ using the given io_service.
\errors Any of the values POSIX open() or CreateFile() can return.
*/
-inline result<async_file_handle> async_file(io_service &service, const path_handle &base, async_file_handle::path_view_type _path, async_file_handle::mode _mode = async_file_handle::mode::read, async_file_handle::creation _creation = async_file_handle::creation::open_existing, async_file_handle::caching _caching = async_file_handle::caching::only_metadata, async_file_handle::flag flags = async_file_handle::flag::none) noexcept
+inline result<async_file_handle> async_file(io_service &service, const path_handle &base, async_file_handle::path_view_type _path, async_file_handle::mode _mode = async_file_handle::mode::read, async_file_handle::creation _creation = async_file_handle::creation::open_existing,
+ async_file_handle::caching _caching = async_file_handle::caching::only_metadata, async_file_handle::flag flags = async_file_handle::flag::none) noexcept
{
- return async_file_handle::async_file(std::forward<decltype(service)>(service), std::forward<decltype(base)>(base), std::forward<decltype(_path)>(_path), std::forward<decltype(_mode)>(_mode), std::forward<decltype(_creation)>(_creation), std::forward<decltype(_caching)>(_caching), std::forward<decltype(flags)>(flags));
+ return async_file_handle::async_file(std::forward<decltype(service)>(service), std::forward<decltype(base)>(base), std::forward<decltype(_path)>(_path), std::forward<decltype(_mode)>(_mode), std::forward<decltype(_creation)>(_creation), std::forward<decltype(_caching)>(_caching),
+ std::forward<decltype(flags)>(flags));
}
/*! Create an async file handle creating a randomly named file on a path.
The file is opened exclusively with `creation::only_if_not_exist` so it
@@ -473,7 +489,8 @@ to use. Use `temp_inode()` instead, it is far more secure.
\errors Any of the values POSIX open() or CreateFile() can return.
*/
-inline result<async_file_handle> async_temp_file(io_service &service, async_file_handle::path_view_type name = async_file_handle::path_view_type(), async_file_handle::mode _mode = async_file_handle::mode::write, async_file_handle::creation _creation = async_file_handle::creation::if_needed, async_file_handle::caching _caching = async_file_handle::caching::only_metadata, async_file_handle::flag flags = async_file_handle::flag::unlink_on_close) noexcept
+inline result<async_file_handle> async_temp_file(io_service &service, async_file_handle::path_view_type name = async_file_handle::path_view_type(), async_file_handle::mode _mode = async_file_handle::mode::write, async_file_handle::creation _creation = async_file_handle::creation::if_needed,
+ async_file_handle::caching _caching = async_file_handle::caching::only_metadata, async_file_handle::flag flags = async_file_handle::flag::unlink_on_close) noexcept
{
return async_file_handle::async_temp_file(std::forward<decltype(service)>(service), std::forward<decltype(name)>(name), std::forward<decltype(_mode)>(_mode), std::forward<decltype(_creation)>(_creation), std::forward<decltype(_caching)>(_caching), std::forward<decltype(flags)>(flags));
}
@@ -491,7 +508,8 @@ inline result<async_file_handle> async_temp_inode(io_service &service, const pat
{
return async_file_handle::async_temp_inode(std::forward<decltype(service)>(service), std::forward<decltype(dir)>(dir), std::forward<decltype(_mode)>(_mode), std::forward<decltype(flags)>(flags));
}
-inline async_file_handle::io_result<async_file_handle::const_buffers_type> barrier(async_file_handle &self, async_file_handle::io_request<async_file_handle::const_buffers_type> reqs = async_file_handle::io_request<async_file_handle::const_buffers_type>(), bool wait_for_device = false, bool and_metadata = false, deadline d = deadline()) noexcept
+inline async_file_handle::io_result<async_file_handle::const_buffers_type> barrier(async_file_handle &self, async_file_handle::io_request<async_file_handle::const_buffers_type> reqs = async_file_handle::io_request<async_file_handle::const_buffers_type>(), bool wait_for_device = false, bool and_metadata = false,
+ deadline d = deadline()) noexcept
{
return self.barrier(std::forward<decltype(reqs)>(reqs), std::forward<decltype(wait_for_device)>(wait_for_device), std::forward<decltype(and_metadata)>(and_metadata), std::forward<decltype(d)>(d));
}
diff --git a/include/afio/v2.0/config.hpp b/include/afio/v2.0/config.hpp
index 617b93c4..b1e7430b 100644
--- a/include/afio/v2.0/config.hpp
+++ b/include/afio/v2.0/config.hpp
@@ -217,6 +217,12 @@ AFIO_V2_NAMESPACE_END
#else
#error No <filesystem> implementation found
#endif
+AFIO_V2_NAMESPACE_BEGIN
+struct path_hasher
+{
+ size_t operator()(const filesystem::path &p) const { return std::hash<filesystem::path::string_type>()(p.native()); }
+};
+AFIO_V2_NAMESPACE_END
// Configure AFIO_DECL
diff --git a/include/afio/v2.0/detail/impl/cached_parent_handle_adapter.ipp b/include/afio/v2.0/detail/impl/cached_parent_handle_adapter.ipp
new file mode 100644
index 00000000..478d7485
--- /dev/null
+++ b/include/afio/v2.0/detail/impl/cached_parent_handle_adapter.ipp
@@ -0,0 +1,128 @@
+/* A parent handle caching adapter
+(C) 2017 Niall Douglas <http://www.nedproductions.biz/> (12 commits)
+File Created: Sept 2017
+
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License in the accompanying file
+Licence.txt or at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+
+Distributed under the Boost Software License, Version 1.0.
+ (See accompanying file Licence.txt or copy at
+ http://www.boost.org/LICENSE_1_0.txt)
+*/
+
+#include "../../algorithm/cached_parent_handle_adapter.hpp"
+
+#include <mutex>
+#include <unordered_map>
+
+AFIO_V2_NAMESPACE_BEGIN
+
+namespace algorithm
+{
+ namespace detail
+ {
+ struct cached_path_handle_map_
+ {
+ std::mutex lock;
+ size_t gc_count{0};
+ std::unordered_map<filesystem::path, std::weak_ptr<cached_path_handle>, path_hasher> by_path;
+ };
+ inline cached_path_handle_map_ &cached_path_handle_map()
+ {
+ static cached_path_handle_map_ map;
+ return map;
+ }
+ AFIO_HEADERS_ONLY_MEMFUNC_SPEC result<filesystem::path> cached_path_handle::current_path(const filesystem::path &append) noexcept
+ {
+ try
+ {
+ auto ret = h.current_path();
+ auto &map = cached_path_handle_map();
+ std::lock_guard<std::mutex> g(map.lock);
+ if(!ret)
+ {
+ std::string msg("cached_path_handle::current_path() failed to retrieve current path of cached handle due to ");
+ msg.append(ret.error().message());
+ AFIO_LOG_WARN(nullptr, msg.c_str());
+ }
+ else if(!ret.value().empty() && ret.value() != _lastpath)
+ {
+ map.by_path.erase(_lastpath);
+ _lastpath = std::move(ret).value();
+ map.by_path.emplace(_lastpath, shared_from_this());
+ }
+ return _lastpath / append;
+ }
+ catch(...)
+ {
+ return error_from_exception();
+ }
+ }
+ AFIO_HEADERS_ONLY_FUNC_SPEC std::pair<cached_path_handle_ptr, filesystem::path> get_cached_path_handle(const path_handle &base, path_view path)
+ {
+ path_view leaf(path.filename());
+ path.remove_filename();
+ filesystem::path dirpath;
+ if(base.is_valid())
+ dirpath = base.current_path().value() / path.path();
+ else
+ {
+ dirpath = path.path();
+ if(dirpath.empty())
+ {
+ dirpath = filesystem::current_path();
+ path = dirpath;
+ }
+ }
+ auto &map = cached_path_handle_map();
+ std::lock_guard<std::mutex> g(map.lock);
+ auto it = map.by_path.find(dirpath);
+ if(it != map.by_path.end())
+ {
+ cached_path_handle_ptr ret = it->second.lock();
+ if(ret)
+ return {ret, leaf.path()};
+ }
+ cached_path_handle_ptr ret = std::make_shared<cached_path_handle>(directory_handle::directory(base, path).value());
+ ret->_lastpath = ret->h.current_path().value();
+ if(it != map.by_path.end())
+ {
+ it->second = ret;
+ }
+ else
+ {
+ map.by_path.emplace(ret->_lastpath, ret);
+ }
+ if(map.gc_count++ >= 1024)
+ {
+ for(auto it = map.by_path.begin(); it != map.by_path.end();)
+ {
+ if(it->second.expired())
+ {
+ it = map.by_path.erase(it);
+ }
+ else
+ {
+ ++it;
+ }
+ }
+ map.gc_count = 0;
+ }
+ return {ret, leaf.path()};
+ }
+ }
+}
+
+AFIO_V2_NAMESPACE_END
diff --git a/include/afio/v2.0/directory_handle.hpp b/include/afio/v2.0/directory_handle.hpp
index 65d4789b..4dadec2f 100644
--- a/include/afio/v2.0/directory_handle.hpp
+++ b/include/afio/v2.0/directory_handle.hpp
@@ -263,6 +263,18 @@ inline std::ostream &operator<<(std::ostream &s, const directory_handle::enumera
return s << "afio::directory_handle::enumerate_info";
}
+//! \brief Constructor for `directory_handle`
+template <> struct construct<directory_handle>
+{
+ const path_handle &base;
+ directory_handle::path_view_type _path;
+ directory_handle::mode _mode = directory_handle::mode::read;
+ directory_handle::creation _creation = directory_handle::creation::open_existing;
+ directory_handle::caching _caching = directory_handle::caching::all;
+ directory_handle::flag flags = directory_handle::flag::none;
+ result<directory_handle> operator()() const noexcept { return directory_handle::directory(base, _path, _mode, _creation, _caching, flags); }
+};
+
// BEGIN make_free_functions.py
//! Swap with another instance
inline void swap(directory_handle &self, directory_handle &o) noexcept
diff --git a/include/afio/v2.0/file_handle.hpp b/include/afio/v2.0/file_handle.hpp
index 9c207c20..75e9d6c7 100644
--- a/include/afio/v2.0/file_handle.hpp
+++ b/include/afio/v2.0/file_handle.hpp
@@ -264,6 +264,18 @@ public:
AFIO_HEADERS_ONLY_VIRTUAL_SPEC result<extent_type> zero(extent_type offset, extent_type bytes, deadline d = deadline()) noexcept;
};
+//! \brief Constructor for `file_handle`
+template <> struct construct<file_handle>
+{
+ const path_handle &base;
+ file_handle::path_view_type _path;
+ file_handle::mode _mode = file_handle::mode::read;
+ file_handle::creation _creation = file_handle::creation::open_existing;
+ file_handle::caching _caching = file_handle::caching::all;
+ file_handle::flag flags = file_handle::flag::none;
+ result<file_handle> operator()() const noexcept { return file_handle::file(base, _path, _mode, _creation, _caching, flags); }
+};
+
// BEGIN make_free_functions.py
//! Swap with another instance
inline void swap(file_handle &self, file_handle &o) noexcept
diff --git a/include/afio/v2.0/fs_handle.hpp b/include/afio/v2.0/fs_handle.hpp
index 986131cf..a0ea94d7 100644
--- a/include/afio/v2.0/fs_handle.hpp
+++ b/include/afio/v2.0/fs_handle.hpp
@@ -41,6 +41,8 @@ AFIO_V2_NAMESPACE_EXPORT_BEGIN
/*! \class fs_handle
\brief A handle to something with a device and inode number.
+
+\sa `algorithm::cached_parent_handle_adapter<T>`
*/
class AFIO_DECL fs_handle
{
@@ -117,8 +119,11 @@ public:
success until the deadline given.
\mallocs Calls `current_path()` and thus is both expensive and calls malloc many times.
+
+ \sa `algorithm::cached_parent_handle_adapter<T>` which overrides this with a zero cost
+ implementation, thus making unlinking and relinking very considerably quicker.
*/
- result<path_handle> parent_path_handle(deadline d = std::chrono::seconds(30)) const noexcept;
+ AFIO_HEADERS_ONLY_VIRTUAL_SPEC result<path_handle> parent_path_handle(deadline d = std::chrono::seconds(30)) const noexcept;
/*! Relinks the current path of this open handle to the new path specified. If `atomic_replace` is
true, the relink \b atomically and silently replaces any item at the new path specified. This operation
@@ -141,9 +146,10 @@ public:
\param d The deadline by which the matching of the containing directory to the open handle's inode
must succeed, else `std::errc::timed_out` will be returned.
\mallocs Except on platforms with race free syscalls for renaming open handles (Windows), calls
- `current_path()` and thus is both expensive and calls malloc many times.
+ `current_path()` via `parent_path_handle()` and thus is both expensive and calls malloc many times.
*/
AFIO_MAKE_FREE_FUNCTION
+ AFIO_HEADERS_ONLY_VIRTUAL_SPEC
result<void> relink(const path_handle &base, path_view_type newpath, bool atomic_replace = true, deadline d = std::chrono::seconds(30)) noexcept;
/*! Unlinks the current path of this open handle, causing its entry to immediately disappear from the filing system.
@@ -169,6 +175,7 @@ public:
`current_path()` if `flag::disable_safety_unlinks` is not set.
*/
AFIO_MAKE_FREE_FUNCTION
+ AFIO_HEADERS_ONLY_VIRTUAL_SPEC
result<void> unlink(deadline d = std::chrono::seconds(30)) noexcept;
};
diff --git a/include/afio/v2.0/handle.hpp b/include/afio/v2.0/handle.hpp
index 62e93456..6418f186 100644
--- a/include/afio/v2.0/handle.hpp
+++ b/include/afio/v2.0/handle.hpp
@@ -237,6 +237,12 @@ public:
instead!
\mallocs At least one malloc for the `path_type`, likely several more.
+ \sa `algorithm::cached_parent_handle_adapter<T>` which overrides this with an
+ implementation based on retrieving the current path of a cached handle to the parent
+ directory. On platforms with instability or failure to retrieve the correct current path
+ for regular files, the cached parent handle adapter works around the problem by
+ taking advantage of directory inodes not having the same instability problems on any
+ platform.
*/
AFIO_HEADERS_ONLY_VIRTUAL_SPEC result<path_type> current_path() const noexcept;
//! Immediately close the native handle type managed by this handle
@@ -379,6 +385,21 @@ inline std::ostream &operator<<(std::ostream &s, const handle::flag &v)
return s << "afio::handle::flag::" << temp;
}
+/*! \brief Metaprogramming shim for constructing any `handle` subclass.
+
+Each `handle` implementation provides one or more static member functions used to construct it.
+Each of these has a descriptive, unique name so it can be used as a free function which is
+convenient and intuitive for human programmers.
+
+This design pattern is however inconvenient for generic code which needs a single way
+of constructing some arbitrary unknown `handle` implementation. This shim function
+provides that.
+*/
+template <class T> struct construct
+{
+ result<T> operator()() const noexcept { static_assert(!std::is_same<T, T>::value, "construct<T>() was not specialised for the type T supplied"); }
+};
+
// Intercept when Outcome creates an errored result and log it to our log
template <class T, class R> inline void hook_result_construction(OUTCOME_V2_NAMESPACE::in_place_type_t<T>, result<R> *res) noexcept
{
diff --git a/include/afio/v2.0/map_handle.hpp b/include/afio/v2.0/map_handle.hpp
index d0016278..0cc10916 100644
--- a/include/afio/v2.0/map_handle.hpp
+++ b/include/afio/v2.0/map_handle.hpp
@@ -196,6 +196,16 @@ inline std::ostream &operator<<(std::ostream &s, const section_handle::flag &v)
return s << "afio::section_handle::flag::" << temp;
}
+//! \brief Constructor for `section_handle`
+template <> struct construct<section_handle>
+{
+ file_handle &backing;
+ section_handle::extent_type maximum_size = 0;
+ section_handle::flag _flag = section_handle::flag::read | section_handle::flag::write;
+ result<section_handle> operator()() const noexcept { return section_handle::section(backing, maximum_size, _flag); }
+};
+
+
/*! \class map_handle
\brief A handle to a memory mapped region of memory.
@@ -386,6 +396,15 @@ public:
using io_handle::write;
};
+//! \brief Constructor for `map_handle`
+template <> struct construct<map_handle>
+{
+ section_handle &section;
+ map_handle::size_type bytes = 0;
+ map_handle::extent_type offset = 0;
+ section_handle::flag _flag = section_handle::flag::readwrite;
+ result<map_handle> operator()() const noexcept { return map_handle::map(section, bytes, offset, _flag); }
+};
// BEGIN make_free_functions.py
//! Swap with another instance
diff --git a/include/afio/v2.0/mapped_file_handle.hpp b/include/afio/v2.0/mapped_file_handle.hpp
index b7f96e56..49fb304a 100644
--- a/include/afio/v2.0/mapped_file_handle.hpp
+++ b/include/afio/v2.0/mapped_file_handle.hpp
@@ -433,6 +433,20 @@ public:
AFIO_HEADERS_ONLY_VIRTUAL_SPEC io_result<const_buffers_type> write(io_request<const_buffers_type> reqs, deadline d = deadline()) noexcept override { return _mh.write(std::move(reqs), std::move(d)); }
};
+//! \brief Constructor for `mapped_file_handle`
+template <> struct construct<mapped_file_handle>
+{
+ mapped_file_handle::size_type reservation;
+ const path_handle &base;
+ mapped_file_handle::path_view_type _path;
+ mapped_file_handle::mode _mode = mapped_file_handle::mode::read;
+ mapped_file_handle::creation _creation = mapped_file_handle::creation::open_existing;
+ mapped_file_handle::caching _caching = mapped_file_handle::caching::all;
+ mapped_file_handle::flag flags = mapped_file_handle::flag::none;
+ result<mapped_file_handle> operator()() const noexcept { return mapped_file_handle::mapped_file(reservation, base, _path, _mode, _creation, _caching, flags); }
+};
+
+
// BEGIN make_free_functions.py
//! Swap with another instance
inline void swap(mapped_file_handle &self, mapped_file_handle &o) noexcept
diff --git a/include/afio/v2.0/path_handle.hpp b/include/afio/v2.0/path_handle.hpp
index 5c7b1375..c1b85e33 100644
--- a/include/afio/v2.0/path_handle.hpp
+++ b/include/afio/v2.0/path_handle.hpp
@@ -86,6 +86,14 @@ public:
static AFIO_HEADERS_ONLY_MEMFUNC_SPEC result<path_handle> path(path_view_type _path) noexcept { return path(path_handle(), _path); }
};
+//! \brief Constructor for `path_handle`
+template <> struct construct<path_handle>
+{
+ const path_handle &base;
+ path_handle::path_view_type _path;
+ result<path_handle> operator()() const noexcept { return path_handle::path(base, _path); }
+};
+
// BEGIN make_free_functions.py
/*! Create a path handle opening access to some location on the filing system.
Some operating systems provide a particularly lightweight method of doing this
diff --git a/test/tests/current_path.cpp b/test/tests/current_path.cpp
index 29c98c16..c5aa9ec7 100644
--- a/test/tests/current_path.cpp
+++ b/test/tests/current_path.cpp
@@ -25,83 +25,98 @@ Distributed under the Boost Software License, Version 1.0.
#include "../../include/afio/afio.hpp"
#include "kerneltest/include/kerneltest.hpp"
-static inline void TestHandleCurrentPath()
+template <class FileHandleType, class DirectoryHandleType> static inline void TestHandleCurrentPath()
{
namespace afio = AFIO_V2_NAMESPACE;
- afio::file_handle h1 = afio::file_handle::file({}, "tempfile", afio::file_handle::mode::write, afio::file_handle::creation::if_needed, afio::file_handle::caching::temporary, afio::file_handle::flag::unlink_on_close).value();
- afio::directory_handle h2 = afio::directory_handle::directory({}, "tempdir", afio::file_handle::mode::write, afio::file_handle::creation::if_needed, afio::file_handle::caching::temporary, afio::file_handle::flag::unlink_on_close).value();
-
- {
- auto h1path=h1.current_path();
- BOOST_CHECK(h1path);
- if(!h1path)
- {
- std::cerr << "Getting the current path of a file FAILED due to " << h1path.error().message() << std::endl;
- }
- else if(h1path.value().empty())
- {
- BOOST_CHECK(!h1path.value().empty());
- std::cerr << "Getting the current path of a file FAILED due to the returned path being empty" << std::endl;
- }
- else
- {
- std::cout << "The path of the file is " << h1path.value() << std::endl;
- }
-
- auto h2path=h2.current_path();
- BOOST_CHECK(h2path);
- if(!h2path)
{
- std::cerr << "Getting the current path of a directory FAILED due to " << h2path.error().message() << std::endl;
+ std::error_code ec;
+ afio::filesystem::remove_all("tempfile", ec);
+ afio::filesystem::remove_all("tempfile2", ec);
+ afio::filesystem::remove_all("tempdir", ec);
+ afio::filesystem::remove_all("tempdir2", ec);
}
- else if(h2path.value().empty())
- {
- BOOST_CHECK(!h2path.value().empty());
- std::cerr << "Getting the current path of a directory FAILED due to the returned path being empty" << std::endl;
- }
- else
+#ifdef __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wmissing-braces"
+#endif
+ FileHandleType h1 = afio::construct<FileHandleType>{afio::path_handle(), "tempfile", afio::file_handle::mode::write, afio::file_handle::creation::if_needed, afio::file_handle::caching::temporary, afio::file_handle::flag::unlink_on_close}().value(); // NOLINT
+ DirectoryHandleType h2 = afio::construct<DirectoryHandleType>{afio::path_handle(), "tempdir", afio::file_handle::mode::write, afio::file_handle::creation::if_needed, afio::file_handle::caching::all, afio::file_handle::flag::unlink_on_close}().value(); // NOLINT
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif
+
{
- std::cout << "The path of the directory is " << h2path.value() << std::endl;
- }
+ auto h1path = h1.current_path();
+ BOOST_CHECK(h1path);
+ if(!h1path)
+ {
+ std::cerr << "Getting the current path of a file FAILED due to " << h1path.error().message() << std::endl;
+ }
+ else if(h1path.value().empty())
+ {
+ BOOST_CHECK(!h1path.value().empty());
+ std::cerr << "Getting the current path of a file FAILED due to the returned path being empty" << std::endl;
+ }
+ else
+ {
+ std::cout << "The path of the file is " << h1path.value() << std::endl;
+ }
+
+ auto h2path = h2.current_path();
+ BOOST_CHECK(h2path);
+ if(!h2path)
+ {
+ std::cerr << "Getting the current path of a directory FAILED due to " << h2path.error().message() << std::endl;
+ }
+ else if(h2path.value().empty())
+ {
+ BOOST_CHECK(!h2path.value().empty());
+ std::cerr << "Getting the current path of a directory FAILED due to the returned path being empty" << std::endl;
+ }
+ else
+ {
+ std::cout << "The path of the directory is " << h2path.value() << std::endl;
+ }
}
-
+
h1.relink({}, "tempfile2").value();
h2.relink({}, "tempdir2").value();
- {
- auto h1path=h1.current_path();
- BOOST_CHECK(h1path);
- if(!h1path)
{
- std::cerr << "Getting the current path of a file FAILED due to " << h1path.error().message() << std::endl;
- }
- else if(h1path.value().empty())
- {
- BOOST_CHECK(!h1path.value().empty());
- std::cerr << "Getting the current path of a file FAILED due to the returned path being empty" << std::endl;
- }
- else
- {
- std::cout << "The path of the file is " << h1path.value() << std::endl;
- }
-
- auto h2path=h2.current_path();
- BOOST_CHECK(h2path);
- if(!h2path)
- {
- std::cerr << "Getting the current path of a directory FAILED due to " << h2path.error().message() << std::endl;
- }
- else if(h2path.value().empty())
- {
- BOOST_CHECK(!h2path.value().empty());
- std::cerr << "Getting the current path of a directory FAILED due to the returned path being empty" << std::endl;
- }
- else
- {
- std::cout << "The path of the directory is " << h2path.value() << std::endl;
- }
+ auto h1path = h1.current_path();
+ BOOST_CHECK(h1path);
+ if(!h1path)
+ {
+ std::cerr << "Getting the current path of a file FAILED due to " << h1path.error().message() << std::endl;
+ }
+ else if(h1path.value().empty())
+ {
+ BOOST_CHECK(!h1path.value().empty());
+ std::cerr << "Getting the current path of a file FAILED due to the returned path being empty" << std::endl;
+ }
+ else
+ {
+ std::cout << "The path of the file is " << h1path.value() << std::endl;
+ }
+
+ auto h2path = h2.current_path();
+ BOOST_CHECK(h2path);
+ if(!h2path)
+ {
+ std::cerr << "Getting the current path of a directory FAILED due to " << h2path.error().message() << std::endl;
+ }
+ else if(h2path.value().empty())
+ {
+ BOOST_CHECK(!h2path.value().empty());
+ std::cerr << "Getting the current path of a directory FAILED due to the returned path being empty" << std::endl;
+ }
+ else
+ {
+ std::cout << "The path of the directory is " << h2path.value() << std::endl;
+ }
}
-
}
-KERNELTEST_TEST_KERNEL(integration, afio, current_path, handle, "Tests that afio::handle::current_path() works as expected", TestHandleCurrentPath())
+KERNELTEST_TEST_KERNEL(integration, afio, current_path, handle, "Tests that afio::handle::current_path() works as expected", TestHandleCurrentPath<AFIO_V2_NAMESPACE::file_handle, AFIO_V2_NAMESPACE::directory_handle>())
+KERNELTEST_TEST_KERNEL(integration, afio, current_path, cached_parent_handle_adapter, "Tests that afio::cached_parent_handle_adapter::current_path() works as expected",
+ TestHandleCurrentPath<AFIO_V2_NAMESPACE::algorithm::cached_parent_handle_adapter<AFIO_V2_NAMESPACE::file_handle>, AFIO_V2_NAMESPACE::algorithm::cached_parent_handle_adapter<AFIO_V2_NAMESPACE::directory_handle>>())