diff options
author | Niall Douglas (s [underscore] sourceforge {at} nedprod [dot] com) <spamtrap@nedprod.com> | 2020-04-13 13:28:56 +0300 |
---|---|---|
committer | Niall Douglas (s [underscore] sourceforge {at} nedprod [dot] com) <spamtrap@nedprod.com> | 2020-04-13 13:28:56 +0300 |
commit | f798bdbccaecf9d1c50f8486a6a34bbd0971ec35 (patch) | |
tree | 9115d35c6bfbff10811289264b5194254c376b75 /include/llfio/v2.0 | |
parent | 45defa9dbf3d3562c37efe48ab98bca55648afba (diff) |
Add the beginnings of porting over the Win IOCP implementation from the resumable i/o branch.
Diffstat (limited to 'include/llfio/v2.0')
-rw-r--r-- | include/llfio/v2.0/detail/impl/io_multiplexer.ipp | 30 | ||||
-rw-r--r-- | include/llfio/v2.0/detail/impl/windows/io_multiplexer.ipp | 111 | ||||
-rw-r--r-- | include/llfio/v2.0/directory_handle.hpp | 1 | ||||
-rw-r--r-- | include/llfio/v2.0/file_handle.hpp | 1 | ||||
-rw-r--r-- | include/llfio/v2.0/io_handle.hpp | 44 | ||||
-rw-r--r-- | include/llfio/v2.0/io_multiplexer.hpp | 26 | ||||
-rw-r--r-- | include/llfio/v2.0/mapped_file_handle.hpp | 1 | ||||
-rw-r--r-- | include/llfio/v2.0/native_handle_type.hpp | 6 | ||||
-rw-r--r-- | include/llfio/v2.0/path_handle.hpp | 4 | ||||
-rw-r--r-- | include/llfio/v2.0/pipe_handle.hpp | 2 | ||||
-rw-r--r-- | include/llfio/v2.0/symlink_handle.hpp | 1 |
11 files changed, 201 insertions, 26 deletions
diff --git a/include/llfio/v2.0/detail/impl/io_multiplexer.ipp b/include/llfio/v2.0/detail/impl/io_multiplexer.ipp index 88d193f7..04f1e888 100644 --- a/include/llfio/v2.0/detail/impl/io_multiplexer.ipp +++ b/include/llfio/v2.0/detail/impl/io_multiplexer.ipp @@ -24,16 +24,38 @@ Distributed under the Boost Software License, Version 1.0. #include "../../io_multiplexer.hpp" +#include <mutex> + LLFIO_V2_NAMESPACE_BEGIN namespace this_thread { static LLFIO_THREAD_LOCAL io_multiplexer *_thread_multiplexer; - LLFIO_HEADERS_ONLY_FUNC_SPEC io_multiplexer *multiplexer() noexcept - { - return _thread_multiplexer; - } + LLFIO_HEADERS_ONLY_FUNC_SPEC io_multiplexer *multiplexer() noexcept { return _thread_multiplexer; } LLFIO_HEADERS_ONLY_FUNC_SPEC void set_multiplexer(io_multiplexer *ctx) noexcept { _thread_multiplexer = ctx; } } // namespace this_thread +template <bool is_threadsafe> struct io_multiplexer_impl : io_multiplexer +{ + struct _lock_impl_type + { + void lock() {} + void unlock() {} + }; + _lock_impl_type _lock; + using _lock_guard = std::unique_lock<_lock_impl_type>; +}; +template <> struct io_multiplexer_impl<true> : io_multiplexer +{ + using _lock_impl_type = std::mutex; + _lock_impl_type _lock; + using _lock_guard = std::unique_lock<_lock_impl_type>; +}; + LLFIO_V2_NAMESPACE_END + +#if defined(_WIN32) +#include "windows/io_multiplexer.ipp" +#else +//#include "posix/io_multiplexer.ipp" +#endif diff --git a/include/llfio/v2.0/detail/impl/windows/io_multiplexer.ipp b/include/llfio/v2.0/detail/impl/windows/io_multiplexer.ipp new file mode 100644 index 00000000..b4b971f2 --- /dev/null +++ b/include/llfio/v2.0/detail/impl/windows/io_multiplexer.ipp @@ -0,0 +1,111 @@ +/* Multiplex file i/o +(C) 2019 Niall Douglas <http://www.nedproductions.biz/> (9 commits) +File Created: Nov 2019 + + +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 "../../../io_multiplexer.hpp" + +#ifndef _WIN32 +#error This implementation file is for Microsoft Windows only +#endif + +#include "import.hpp" + +LLFIO_V2_NAMESPACE_BEGIN + +template <bool is_threadsafe> struct win_iocp_multiplexer final : io_multiplexer_impl<is_threadsafe> +{ + using _base = io_multiplexer_impl<is_threadsafe>; + using _lock_guard = typename _base::_lock_guard; + + explicit win_iocp_multiplexer(size_t threads) { (void) threads; } + virtual ~win_iocp_multiplexer() + { + if(_v) + { + (void) win_iocp_multiplexer::close(); + } + } + // LLFIO_HEADERS_ONLY_VIRTUAL_SPEC result<path_type> current_path() const noexcept override; + LLFIO_HEADERS_ONLY_VIRTUAL_SPEC result<void> close() noexcept override { return _base::close(); } + LLFIO_HEADERS_ONLY_VIRTUAL_SPEC native_handle_type release() noexcept override { return _base::release(); } + + LLFIO_HEADERS_ONLY_VIRTUAL_SPEC result<uint8_t> do_io_handle_register(io_handle *h) noexcept override { + windows_nt_kernel::init(); + using namespace windows_nt_kernel; + LLFIO_LOG_FUNCTION_CALL(this); + IO_STATUS_BLOCK isb = make_iostatus(); + FILE_COMPLETION_INFORMATION fci{}; + memset(&fci, 0, sizeof(fci)); + fci.Port = this->_v.h; + fci.Key = nullptr; + NTSTATUS ntstat = NtSetInformationFile(h->native_handle().h, &isb, &fci, sizeof(fci), FileCompletionInformation); + if(STATUS_PENDING == ntstat) + { + ntstat = ntwait(h->native_handle().h, isb, deadline()); + } + if(ntstat < 0) + { + return ntkernel_error(ntstat); + } + // If this works, we can avoid IOCP entirely for immediately completing i/o + return (uint8_t) !SetFileCompletionNotificationModes(h->native_handle().h, FILE_SKIP_COMPLETION_PORT_ON_SUCCESS | FILE_SKIP_SET_EVENT_ON_HANDLE); + } + LLFIO_HEADERS_ONLY_VIRTUAL_SPEC result<void> do_io_handle_deregister(io_handle *h) noexcept override { + windows_nt_kernel::init(); + using namespace windows_nt_kernel; + LLFIO_LOG_FUNCTION_CALL(this); + IO_STATUS_BLOCK isb = make_iostatus(); + FILE_COMPLETION_INFORMATION fci{}; + memset(&fci, 0, sizeof(fci)); + fci.Port = nullptr; + fci.Key = nullptr; + NTSTATUS ntstat = NtSetInformationFile(h->native_handle().h, &isb, &fci, sizeof(fci), FileReplaceCompletionInformation); + if(STATUS_PENDING == ntstat) + { + ntstat = ntwait(h->native_handle().h, isb, deadline()); + } + if(ntstat < 0) + { + return ntkernel_error(ntstat); + } + return success(); + } + //LLFIO_HEADERS_ONLY_VIRTUAL_SPEC size_t do_io_handle_max_buffers(const io_handle *h) const noexcept override {} + //LLFIO_HEADERS_ONLY_VIRTUAL_SPEC result<registered_buffer_type> do_io_handle_allocate_registered_buffer(io_handle *h, size_t &bytes) noexcept override {} + //LLFIO_HEADERS_ONLY_VIRTUAL_SPEC io_result<buffers_type> do_io_handle_read(io_handle *h, io_request<buffers_type> reqs, deadline d) noexcept override {} + //LLFIO_HEADERS_ONLY_VIRTUAL_SPEC io_result<buffers_type> do_io_handle_read(io_handle *h, registered_buffer_type base, io_request<buffers_type> reqs, deadline d) noexcept override {} + //LLFIO_HEADERS_ONLY_VIRTUAL_SPEC io_result<const_buffers_type> do_io_handle_write(io_handle *h, io_request<const_buffers_type> reqs, deadline d) noexcept override {} + //LLFIO_HEADERS_ONLY_VIRTUAL_SPEC io_result<const_buffers_type> do_io_handle_write(io_handle *h, registered_buffer_type base, io_request<const_buffers_type> reqs, deadline d) noexcept override {} + //LLFIO_HEADERS_ONLY_VIRTUAL_SPEC io_result<const_buffers_type> do_io_handle_barrier(io_handle *h, io_request<const_buffers_type> reqs, barrier_kind kind, deadline d) noexcept override {} +}; + +LLFIO_HEADERS_ONLY_FUNC_SPEC result<io_multiplexer_ptr> multiplexer_win_iocp(size_t threads) noexcept +{ + if(1 == threads) + { + return std::make_unique<win_iocp_multiplexer<false>>(1); + } + return std::make_unique<win_iocp_multiplexer<true>>(threads); +} + +LLFIO_V2_NAMESPACE_END diff --git a/include/llfio/v2.0/directory_handle.hpp b/include/llfio/v2.0/directory_handle.hpp index ebd4ad46..0e5c47e1 100644 --- a/include/llfio/v2.0/directory_handle.hpp +++ b/include/llfio/v2.0/directory_handle.hpp @@ -285,6 +285,7 @@ public: trying to open the path returned. Thus many allocations may occur. */ LLFIO_HEADERS_ONLY_VIRTUAL_SPEC result<directory_handle> reopen(mode mode_ = mode::unchanged, caching caching_ = caching::unchanged, deadline d = std::chrono::seconds(30)) const noexcept; + LLFIO_DEADLINE_TRY_FOR_UNTIL(reopen) /*! Return a copy of this directory handle, but as a path handle. diff --git a/include/llfio/v2.0/file_handle.hpp b/include/llfio/v2.0/file_handle.hpp index dfe4bb14..2c968ee6 100644 --- a/include/llfio/v2.0/file_handle.hpp +++ b/include/llfio/v2.0/file_handle.hpp @@ -254,7 +254,6 @@ public: trying to open the path returned. Thus many allocations may occur. */ LLFIO_HEADERS_ONLY_MEMFUNC_SPEC result<file_handle> reopen(mode mode_ = mode::unchanged, caching caching_ = caching::unchanged, deadline d = std::chrono::seconds(30)) const noexcept; - LLFIO_DEADLINE_TRY_FOR_UNTIL(reopen) /*! Return the current maximum permitted extent of the file. diff --git a/include/llfio/v2.0/io_handle.hpp b/include/llfio/v2.0/io_handle.hpp index 5fef2669..0370d750 100644 --- a/include/llfio/v2.0/io_handle.hpp +++ b/include/llfio/v2.0/io_handle.hpp @@ -41,6 +41,8 @@ LLFIO_V2_NAMESPACE_EXPORT_BEGIN */ class LLFIO_DECL io_handle : public handle { + friend class io_multiplexer; + public: using path_type = handle::path_type; using extent_type = handle::extent_type; @@ -119,13 +121,21 @@ public: } if(_ctx != nullptr) { - OUTCOME_TRY(_ctx->_deregister_io_handle(this)); + OUTCOME_TRY(_ctx->do_io_handle_deregister(this)); _ctx = nullptr; } if(c != nullptr) { - OUTCOME_TRY(type, c->_register_io_handle(this)); - (void) type; + OUTCOME_TRY(state, c->do_io_handle_register(this)); + _v.behaviour = (_v.behaviour & ~(native_handle_type::disposition::_multiplexer_state_bit0 | native_handle_type::disposition::_multiplexer_state_bit1)); + if((state & 1) != 0) + { + _v.behaviour |= native_handle_type::disposition::_multiplexer_state_bit0; + } + if((state & 2) != 0) + { + _v.behaviour |= native_handle_type::disposition::_multiplexer_state_bit1; + } } _ctx = c; return success(); @@ -374,6 +384,34 @@ public: }; static_assert((sizeof(void *) == 4 && sizeof(io_handle) == 20) || (sizeof(void *) == 8 && sizeof(io_handle) == 32), "io_handle is not 20 or 32 bytes in size!"); +inline size_t io_multiplexer::do_io_handle_max_buffers(const io_handle *h) const noexcept +{ + return h->_do_max_buffers(); +} +inline result<io_multiplexer::registered_buffer_type> io_multiplexer::do_io_handle_allocate_registered_buffer(io_handle *h, size_t &bytes) noexcept +{ + return h->_do_allocate_registered_buffer(bytes); +} +inline io_multiplexer::io_result<io_multiplexer::buffers_type> io_multiplexer::do_io_handle_read(io_handle *h, io_multiplexer::io_request<io_multiplexer::buffers_type> reqs, deadline d) noexcept +{ + return h->_do_read(reqs, d); +} +inline io_multiplexer::io_result<io_multiplexer::buffers_type> io_multiplexer::do_io_handle_read(io_handle *h, io_multiplexer::registered_buffer_type base, io_multiplexer::io_request<io_multiplexer::buffers_type> reqs, deadline d) noexcept +{ + return h->_do_read(std::move(base), reqs, d); +} +inline io_multiplexer::io_result<io_multiplexer::const_buffers_type> io_multiplexer::do_io_handle_write(io_handle *h, io_multiplexer::io_request<io_multiplexer::const_buffers_type> reqs, deadline d) noexcept +{ + return h->_do_write(reqs, d); +} +inline io_multiplexer::io_result<io_multiplexer::const_buffers_type> io_multiplexer::do_io_handle_write(io_handle *h, io_multiplexer::registered_buffer_type base, io_multiplexer::io_request<io_multiplexer::const_buffers_type> reqs, deadline d) noexcept +{ + return h->_do_write(std::move(base), reqs, d); +} +inline io_multiplexer::io_result<io_multiplexer::const_buffers_type> io_multiplexer::do_io_handle_barrier(io_handle *h, io_multiplexer::io_request<io_multiplexer::const_buffers_type> reqs, io_multiplexer::barrier_kind kind, deadline d) noexcept +{ + return h->_do_barrier(reqs, kind, d); +} // BEGIN make_free_functions.py /*! \brief Read data from the open handle. diff --git a/include/llfio/v2.0/io_multiplexer.hpp b/include/llfio/v2.0/io_multiplexer.hpp index c130356b..8ae75af8 100644 --- a/include/llfio/v2.0/io_multiplexer.hpp +++ b/include/llfio/v2.0/io_multiplexer.hpp @@ -73,11 +73,6 @@ you really need non-infinite deadline i/o. */ class LLFIO_DECL io_multiplexer : public handle { - friend class io_handle; - - LLFIO_HEADERS_ONLY_VIRTUAL_SPEC result<int> _register_io_handle(io_handle *h) noexcept = 0; - LLFIO_HEADERS_ONLY_VIRTUAL_SPEC result<void> _deregister_io_handle(io_handle *h) noexcept = 0; - public: using path_type = handle::path_type; using extent_type = handle::extent_type; @@ -361,20 +356,24 @@ public: ~io_multiplexer() = default; public: + //! Implements `io_handle` registration. The bottom two bits of the returned value are set into `_v.behaviour`'s `_multiplexer_state_bit0` and `_multiplexer_state_bit` + LLFIO_HEADERS_ONLY_VIRTUAL_SPEC result<uint8_t> do_io_handle_register(io_handle *h) noexcept = 0; + //! Implements `io_handle` deregistration + LLFIO_HEADERS_ONLY_VIRTUAL_SPEC result<void> do_io_handle_deregister(io_handle *h) noexcept = 0; //! Implements `io_handle::max_buffers()` - LLFIO_HEADERS_ONLY_VIRTUAL_SPEC size_t do_io_handle_max_buffers(const io_handle *h) const noexcept = 0; + LLFIO_HEADERS_ONLY_VIRTUAL_SPEC size_t do_io_handle_max_buffers(const io_handle *h) const noexcept; //! Implements `io_handle::allocate_registered_buffer()` - LLFIO_HEADERS_ONLY_VIRTUAL_SPEC result<registered_buffer_type> do_io_handle_allocate_registered_buffer(io_handle *h, size_t &bytes) noexcept = 0; + LLFIO_HEADERS_ONLY_VIRTUAL_SPEC result<registered_buffer_type> do_io_handle_allocate_registered_buffer(io_handle *h, size_t &bytes) noexcept; //! Implements `io_handle::read()` - LLFIO_HEADERS_ONLY_VIRTUAL_SPEC io_result<buffers_type> do_io_handle_read(io_handle *h, io_request<buffers_type> reqs, deadline d) noexcept = 0; + LLFIO_HEADERS_ONLY_VIRTUAL_SPEC io_result<buffers_type> do_io_handle_read(io_handle *h, io_request<buffers_type> reqs, deadline d) noexcept; //! Implements `io_handle::read()` - LLFIO_HEADERS_ONLY_VIRTUAL_SPEC io_result<buffers_type> do_io_handle_read(io_handle *h, registered_buffer_type base, io_request<buffers_type> reqs, deadline d) noexcept = 0; + LLFIO_HEADERS_ONLY_VIRTUAL_SPEC io_result<buffers_type> do_io_handle_read(io_handle *h, registered_buffer_type base, io_request<buffers_type> reqs, deadline d) noexcept; //! Implements `io_handle::write()` - LLFIO_HEADERS_ONLY_VIRTUAL_SPEC io_result<const_buffers_type> do_io_handle_write(io_handle *h, io_request<const_buffers_type> reqs, deadline d) noexcept = 0; + LLFIO_HEADERS_ONLY_VIRTUAL_SPEC io_result<const_buffers_type> do_io_handle_write(io_handle *h, io_request<const_buffers_type> reqs, deadline d) noexcept; //! Implements `io_handle::write()` - LLFIO_HEADERS_ONLY_VIRTUAL_SPEC io_result<const_buffers_type> do_io_handle_write(io_handle *h, registered_buffer_type base, io_request<const_buffers_type> reqs, deadline d) noexcept = 0; + LLFIO_HEADERS_ONLY_VIRTUAL_SPEC io_result<const_buffers_type> do_io_handle_write(io_handle *h, registered_buffer_type base, io_request<const_buffers_type> reqs, deadline d) noexcept; //! Implements `io_handle::barrier()` - LLFIO_HEADERS_ONLY_VIRTUAL_SPEC io_result<const_buffers_type> do_io_handle_barrier(io_handle *h, io_request<const_buffers_type> reqs, barrier_kind kind, deadline d) noexcept = 0; + LLFIO_HEADERS_ONLY_VIRTUAL_SPEC io_result<const_buffers_type> do_io_handle_barrier(io_handle *h, io_request<const_buffers_type> reqs, barrier_kind kind, deadline d) noexcept; }; //! A unique ptr to an i/o multiplexer implementation. using io_multiplexer_ptr = std::unique_ptr<io_multiplexer>; @@ -389,7 +388,8 @@ using io_multiplexer_ptr = std::unique_ptr<io_multiplexer>; // LLFIO_HEADERS_ONLY_FUNC_SPEC result<io_multiplexer_ptr> multiplexer_bsd_kqueue(size_t threads) noexcept; #endif #if defined(_WIN32) || DOXYGEN_IS_IN_THE_HOUSE -// LLFIO_HEADERS_ONLY_FUNC_SPEC result<io_multiplexer_ptr> multiplexer_win_iocp(size_t threads) noexcept; +//! \brief Return an i/o multiplexer implemented using Microsoft Windows IOCPW +LLFIO_HEADERS_ONLY_FUNC_SPEC result<io_multiplexer_ptr> multiplexer_win_iocp(size_t threads) noexcept; #endif //! \brief Thread local settings diff --git a/include/llfio/v2.0/mapped_file_handle.hpp b/include/llfio/v2.0/mapped_file_handle.hpp index c1d9d878..b560af83 100644 --- a/include/llfio/v2.0/mapped_file_handle.hpp +++ b/include/llfio/v2.0/mapped_file_handle.hpp @@ -410,6 +410,7 @@ public: OUTCOME_TRY(fh, file_handle::reopen(mode_, caching_, d)); return mapped_file_handle(std::move(fh), reservation, _sh.section_flags()); } + LLFIO_DEADLINE_TRY_FOR_UNTIL(reopen) LLFIO_HEADERS_ONLY_VIRTUAL_SPEC result<void> set_multiplexer(io_multiplexer *c = this_thread::multiplexer()) noexcept override { OUTCOME_TRY(file_handle::set_multiplexer(c)); diff --git a/include/llfio/v2.0/native_handle_type.hpp b/include/llfio/v2.0/native_handle_type.hpp index 5d9c2cfc..ff375e85 100644 --- a/include/llfio/v2.0/native_handle_type.hpp +++ b/include/llfio/v2.0/native_handle_type.hpp @@ -69,8 +69,10 @@ struct native_handle_type // NOLINT cache_writes = 1U << 23U, //!< Is writing back from kernel cache rather than writing through cache_temporary = 1U << 24U, //!< Writes are not flushed to storage quickly - _is_connected = 1U << 28U, // used by pipe_handle on Windows to store connectedness - _child_close_executed = 1U << 30U // used to trap when vptr has become corrupted + _is_connected = 1U << 28U, // used by pipe_handle on Windows to store connectedness + _multiplexer_state_bit0 = 1U << 29U, // per-handle state bits used by an i/o multiplexer + _multiplexer_state_bit1 = 1U << 30U, // per-handle state bits used by an i/o multiplexer + _child_close_executed = 1U << 31U // used to trap when vptr has become corrupted } QUICKCPPLIB_BITFIELD_END(disposition) union { diff --git a/include/llfio/v2.0/path_handle.hpp b/include/llfio/v2.0/path_handle.hpp index 23c6221c..8e2ee7e2 100644 --- a/include/llfio/v2.0/path_handle.hpp +++ b/include/llfio/v2.0/path_handle.hpp @@ -96,8 +96,8 @@ public: o = std::move(temp); } - //! Replaces `handle::clone()` with a `path_handle returning edition` - result<path_handle> clone() const noexcept + //! A `path_handle` returning edition of `handle::clone()` + result<path_handle> clone_to_path_handle() const noexcept { OUTCOME_TRY(newh, handle::clone()); return path_handle(std::move(newh)); diff --git a/include/llfio/v2.0/pipe_handle.hpp b/include/llfio/v2.0/pipe_handle.hpp index 5d929051..99036e98 100644 --- a/include/llfio/v2.0/pipe_handle.hpp +++ b/include/llfio/v2.0/pipe_handle.hpp @@ -267,7 +267,7 @@ public: LLFIO_HEADERS_ONLY_VIRTUAL_SPEC result<path_handle> parent_path_handle(deadline /*unused*/ = std::chrono::seconds(30)) const noexcept override { // Pipes parent handle is always the NT kernel namespace for pipes - return path_discovery::temporary_named_pipes_directory().clone(); + return path_discovery::temporary_named_pipes_directory().clone_to_path_handle(); } protected: diff --git a/include/llfio/v2.0/symlink_handle.hpp b/include/llfio/v2.0/symlink_handle.hpp index 99b74a29..8f7e7bb8 100644 --- a/include/llfio/v2.0/symlink_handle.hpp +++ b/include/llfio/v2.0/symlink_handle.hpp @@ -386,6 +386,7 @@ public: trying to open the path returned. Thus many allocations may occur. */ LLFIO_HEADERS_ONLY_VIRTUAL_SPEC result<symlink_handle> reopen(mode mode_ = mode::unchanged, deadline d = std::chrono::seconds(30)) const noexcept; + LLFIO_DEADLINE_TRY_FOR_UNTIL(reopen) #if LLFIO_SYMLINK_HANDLE_IS_FAKED LLFIO_HEADERS_ONLY_VIRTUAL_SPEC result<path_type> current_path() const noexcept override; |