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

github.com/ned14/llfio.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNiall Douglas <s_github@nedprod.com>2021-12-24 00:39:17 +0300
committerGitHub <noreply@github.com>2021-12-24 00:39:17 +0300
commit47b0e2fc1647eca3dd468ad9282faf8cfc4fb4b4 (patch)
treee010bfde8226999787197e35385095f242b667b3
parentbfa67375086cd63db1eb1bef64ccc9708b021eaa (diff)
parentd5717dc2c53d1e340095da85d8bbfa9a1d83d808 (diff)
Merge pull request #84 from ned14/networkingall_tests_passed_47b0e2fc1647eca3dd468ad9282faf8cfc4fb4b4
Networking
-rw-r--r--CMakeLists.txt55
-rw-r--r--cmake/headers.cmake21
-rw-r--r--cmake/tests.cmake1
-rw-r--r--example/ts_examples.cpp20
-rw-r--r--include/llfio/revision.hpp6
-rw-r--r--include/llfio/v2.0/algorithm/handle_adapter/combining.hpp94
-rw-r--r--include/llfio/v2.0/byte_io_handle.hpp (renamed from include/llfio/v2.0/io_handle.hpp)173
-rw-r--r--include/llfio/v2.0/byte_io_multiplexer.hpp (renamed from include/llfio/v2.0/io_multiplexer.hpp)248
-rw-r--r--include/llfio/v2.0/byte_socket_handle.hpp971
-rw-r--r--include/llfio/v2.0/config.hpp1
-rw-r--r--include/llfio/v2.0/detail/impl/byte_io_multiplexer.ipp (renamed from include/llfio/v2.0/detail/impl/io_multiplexer.ipp)12
-rw-r--r--include/llfio/v2.0/detail/impl/byte_socket_handle.ipp304
-rw-r--r--include/llfio/v2.0/detail/impl/dynamic_thread_pool_group.ipp4
-rw-r--r--include/llfio/v2.0/detail/impl/posix/byte_io_handle.ipp (renamed from include/llfio/v2.0/detail/impl/posix/io_handle.ipp)33
-rw-r--r--include/llfio/v2.0/detail/impl/posix/byte_socket_handle.ipp402
-rw-r--r--include/llfio/v2.0/detail/impl/posix/directory_handle.ipp2
-rw-r--r--include/llfio/v2.0/detail/impl/posix/file_handle.ipp6
-rw-r--r--include/llfio/v2.0/detail/impl/posix/lockable_byte_io_handle.ipp (renamed from include/llfio/v2.0/detail/impl/posix/lockable_io_handle.ipp)20
-rw-r--r--include/llfio/v2.0/detail/impl/posix/map_handle.ipp2
-rw-r--r--include/llfio/v2.0/detail/impl/posix/path_handle.ipp2
-rw-r--r--include/llfio/v2.0/detail/impl/posix/pipe_handle.ipp6
-rw-r--r--include/llfio/v2.0/detail/impl/posix/symlink_handle.ipp1
-rw-r--r--include/llfio/v2.0/detail/impl/posix/test/io_uring_multiplexer.ipp52
-rw-r--r--include/llfio/v2.0/detail/impl/storage_profile.ipp14
-rw-r--r--include/llfio/v2.0/detail/impl/test/null_multiplexer.ipp81
-rw-r--r--include/llfio/v2.0/detail/impl/windows/byte_io_handle.ipp450
-rw-r--r--include/llfio/v2.0/detail/impl/windows/byte_io_multiplexer.ipp55
-rw-r--r--include/llfio/v2.0/detail/impl/windows/byte_socket_handle.ipp433
-rw-r--r--include/llfio/v2.0/detail/impl/windows/directory_handle.ipp2
-rw-r--r--include/llfio/v2.0/detail/impl/windows/file_handle.ipp6
-rw-r--r--include/llfio/v2.0/detail/impl/windows/handle.ipp8
-rw-r--r--include/llfio/v2.0/detail/impl/windows/import.hpp4
-rw-r--r--include/llfio/v2.0/detail/impl/windows/io_handle.ipp270
-rw-r--r--include/llfio/v2.0/detail/impl/windows/lockable_byte_io_handle.ipp (renamed from include/llfio/v2.0/detail/impl/windows/lockable_io_handle.ipp)18
-rw-r--r--include/llfio/v2.0/detail/impl/windows/map_handle.ipp6
-rw-r--r--include/llfio/v2.0/detail/impl/windows/path_handle.ipp2
-rw-r--r--include/llfio/v2.0/detail/impl/windows/pipe_handle.ipp8
-rw-r--r--include/llfio/v2.0/detail/impl/windows/process_handle.ipp2
-rw-r--r--include/llfio/v2.0/detail/impl/windows/symlink_handle.ipp2
-rw-r--r--include/llfio/v2.0/detail/impl/windows/test/iocp_multiplexer.ipp49
-rw-r--r--include/llfio/v2.0/dynamic_thread_pool_group.hpp14
-rw-r--r--include/llfio/v2.0/fast_random_file_handle.hpp26
-rw-r--r--include/llfio/v2.0/file_handle.hpp66
-rw-r--r--include/llfio/v2.0/handle.hpp8
-rw-r--r--include/llfio/v2.0/llfio.hpp3
-rw-r--r--include/llfio/v2.0/lockable_byte_io_handle.hpp (renamed from include/llfio/v2.0/lockable_io_handle.hpp)90
-rw-r--r--include/llfio/v2.0/map_handle.hpp54
-rw-r--r--include/llfio/v2.0/mapped_file_handle.hpp28
-rw-r--r--include/llfio/v2.0/multiplex.hpp12
-rw-r--r--include/llfio/v2.0/native_handle_type.hpp11
-rw-r--r--include/llfio/v2.0/pipe_handle.hpp89
-rw-r--r--include/llfio/v2.0/storage_profile.hpp18
-rw-r--r--programs/benchmark-async/main.cpp48
-rw-r--r--programs/benchmark-io-congestion/main.cpp8
-rw-r--r--test/tests/byte_socket_handle.cpp729
-rw-r--r--test/tests/dynamic_thread_pool_group.cpp2
-rw-r--r--test/tests/pipe_handle.cpp18
57 files changed, 4233 insertions, 837 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index ddfe3e7f..1110be4c 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -44,6 +44,9 @@ option(LLFIO_ENABLE_DEPENDENCY_SMOKE_TEST "Whether to build executables which ar
option(LLFIO_ASSUME_CROSS_COMPILING "Whether to assume we are cross compiling. Normally automatically detected, but if automatic detection doesn't work, a working <filesystem> will not be found during cmake configure." OFF)
option(LLFIO_FORCE_CONCEPTS_OFF "Whether to not auto detect and enable concepts for the LLFIO cmake targets" OFF)
option(LLFIO_FORCE_COROUTINES_OFF "Whether to not auto detect and enable coroutines for the LLFIO cmake targets" OFF)
+option(LLFIO_FORCE_DYNAMIC_THREAD_POOL_GROUP_OFF "Whether to disable dynamic thread pool support in LLFIO" OFF)
+option(LLFIO_FORCE_NETWORKING_OFF "Whether to disable networking support in LLFIO" OFF)
+option(LLFIO_FORCE_MAPPED_FILES_OFF "Whether to disable memory mapped files support in LLFIO" OFF)
option(UNIT_TESTS_BUILD_ALL "Whether to run all of the unit test suite." OFF)
set(UNIT_TESTS_CXX_VERSION "latest" CACHE STRING "The version of C++ to use in the header-only unit tests")
if(CMAKE_SYSTEM_NAME MATCHES "FreeBSD" OR APPLE)
@@ -317,29 +320,43 @@ int main() {
all_compile_definitions(PUBLIC LLFIO_FORCE_EXPERIMENTAL_FILESYSTEM=1 KERNELTEST_FORCE_EXPERIMENTAL_FILESYSTEM=1)
endif()
endif()
-# Do we have Grand Central Dispatch on this platform?
-if(LLFIO_USE_LIBDISPATCH)
- function(check_have_libdispatch postfix)
- set(CMAKE_REQUIRED_LIBRARIES ${ARGN})
- check_cxx_source_compiles("
-#include <dispatch/dispatch.h>
-int main() {
- return dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0) != nullptr;
-}
-" LLFIO_HAS_LIBDISPATCH_${postfix})
- endfunction()
- check_have_libdispatch(BUILTIN)
- if(NOT LLFIO_HAS_LIBDISPATCH_BUILTIN)
- check_have_libdispatch(WITH_LIBDISPATCH dispatch)
- if(LLFIO_HAS_LIBDISPATCH_WITH_LIBDISPATCH)
- all_link_libraries(PUBLIC dispatch)
+if(LLFIO_FORCE_DYNAMIC_THREAD_POOL_GROUP_OFF)
+ all_compile_definitions(PUBLIC LLFIO_EXCLUDE_DYNAMIC_THREAD_POOL_GROUP=1)
+else()
+ # Do we have Grand Central Dispatch on this platform?
+ if(LLFIO_USE_LIBDISPATCH)
+ function(check_have_libdispatch postfix)
+ set(CMAKE_REQUIRED_LIBRARIES ${ARGN})
+ check_cxx_source_compiles("
+ #include <dispatch/dispatch.h>
+ int main() {
+ return dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0) != nullptr;
+ }
+ " LLFIO_HAS_LIBDISPATCH_${postfix})
+ endfunction()
+ check_have_libdispatch(BUILTIN)
+ if(NOT LLFIO_HAS_LIBDISPATCH_BUILTIN)
+ check_have_libdispatch(WITH_LIBDISPATCH dispatch)
+ if(LLFIO_HAS_LIBDISPATCH_WITH_LIBDISPATCH)
+ all_link_libraries(PUBLIC dispatch)
+ endif()
endif()
+ else()
+ all_compile_definitions(PUBLIC LLFIO_DYNAMIC_THREAD_POOL_GROUP_USING_GCD=0)
+ endif()
+ if(NOT LLFIO_HAS_LIBDISPATCH_BUILTIN AND NOT LLFIO_HAS_LIBDISPATCH_WITH_LIBDISPATCH AND (CMAKE_SYSTEM_NAME MATCHES "FreeBSD" OR APPLE))
+ indented_message(FATAL_ERROR "FATAL: Grand Central Dispatch as libdispatch was not found on this FreeBSD or Mac OS system. libdispatch is required for LLFIO to build on those systems.")
endif()
+endif()
+if(LLFIO_FORCE_NETWORKING_OFF)
+ all_compile_definitions(PUBLIC LLFIO_EXCLUDE_NETWORKING=1)
else()
- all_compile_definitions(PUBLIC LLFIO_DYNAMIC_THREAD_POOL_GROUP_USING_GCD=0)
+ if(WIN32)
+ all_link_libraries(PUBLIC ws2_32)
+ endif()
endif()
-if(NOT LLFIO_HAS_LIBDISPATCH_BUILTIN AND NOT LLFIO_HAS_LIBDISPATCH_WITH_LIBDISPATCH AND (CMAKE_SYSTEM_NAME MATCHES "FreeBSD" OR APPLE))
- indented_message(FATAL_ERROR "FATAL: Grand Central Dispatch as libdispatch was not found on this FreeBSD or Mac OS system. libdispatch is required for LLFIO to build on those systems.")
+if(LLFIO_FORCE_MAPPED_FILES_OFF)
+ all_compile_definitions(PUBLIC LLFIO_EXCLUDE_MAPPED_FILE_HANDLE=1)
endif()
# Set any macros this library requires
diff --git a/cmake/headers.cmake b/cmake/headers.cmake
index 3ecb486f..3fb2a589 100644
--- a/cmake/headers.cmake
+++ b/cmake/headers.cmake
@@ -24,24 +24,29 @@ set(llfio_HEADERS
"include/llfio/v2.0/algorithm/summarize.hpp"
"include/llfio/v2.0/algorithm/traverse.hpp"
"include/llfio/v2.0/algorithm/trivial_vector.hpp"
+ "include/llfio/v2.0/byte_io_handle.hpp"
+ "include/llfio/v2.0/byte_io_multiplexer.hpp"
+ "include/llfio/v2.0/byte_socket_handle.hpp"
"include/llfio/v2.0/config.hpp"
"include/llfio/v2.0/deadline.h"
+ "include/llfio/v2.0/detail/impl/byte_io_multiplexer.ipp"
+ "include/llfio/v2.0/detail/impl/byte_socket_handle.ipp"
"include/llfio/v2.0/detail/impl/cached_parent_handle_adapter.ipp"
"include/llfio/v2.0/detail/impl/clone.ipp"
"include/llfio/v2.0/detail/impl/config.ipp"
"include/llfio/v2.0/detail/impl/dynamic_thread_pool_group.ipp"
"include/llfio/v2.0/detail/impl/fast_random_file_handle.ipp"
- "include/llfio/v2.0/detail/impl/io_multiplexer.ipp"
"include/llfio/v2.0/detail/impl/map_handle.ipp"
"include/llfio/v2.0/detail/impl/path_discovery.ipp"
"include/llfio/v2.0/detail/impl/path_view.ipp"
+ "include/llfio/v2.0/detail/impl/posix/byte_io_handle.ipp"
+ "include/llfio/v2.0/detail/impl/posix/byte_socket_handle.ipp"
"include/llfio/v2.0/detail/impl/posix/directory_handle.ipp"
"include/llfio/v2.0/detail/impl/posix/file_handle.ipp"
"include/llfio/v2.0/detail/impl/posix/fs_handle.ipp"
"include/llfio/v2.0/detail/impl/posix/handle.ipp"
"include/llfio/v2.0/detail/impl/posix/import.hpp"
- "include/llfio/v2.0/detail/impl/posix/io_handle.ipp"
- "include/llfio/v2.0/detail/impl/posix/lockable_io_handle.ipp"
+ "include/llfio/v2.0/detail/impl/posix/lockable_byte_io_handle.ipp"
"include/llfio/v2.0/detail/impl/posix/map_handle.ipp"
"include/llfio/v2.0/detail/impl/posix/mapped_file_handle.ipp"
"include/llfio/v2.0/detail/impl/posix/path_discovery.ipp"
@@ -59,13 +64,15 @@ set(llfio_HEADERS
"include/llfio/v2.0/detail/impl/storage_profile.ipp"
"include/llfio/v2.0/detail/impl/test/null_multiplexer.ipp"
"include/llfio/v2.0/detail/impl/traverse.ipp"
+ "include/llfio/v2.0/detail/impl/windows/byte_io_handle.ipp"
+ "include/llfio/v2.0/detail/impl/windows/byte_io_multiplexer.ipp"
+ "include/llfio/v2.0/detail/impl/windows/byte_socket_handle.ipp"
"include/llfio/v2.0/detail/impl/windows/directory_handle.ipp"
"include/llfio/v2.0/detail/impl/windows/file_handle.ipp"
"include/llfio/v2.0/detail/impl/windows/fs_handle.ipp"
"include/llfio/v2.0/detail/impl/windows/handle.ipp"
"include/llfio/v2.0/detail/impl/windows/import.hpp"
- "include/llfio/v2.0/detail/impl/windows/io_handle.ipp"
- "include/llfio/v2.0/detail/impl/windows/lockable_io_handle.ipp"
+ "include/llfio/v2.0/detail/impl/windows/lockable_byte_io_handle.ipp"
"include/llfio/v2.0/detail/impl/windows/map_handle.ipp"
"include/llfio/v2.0/detail/impl/windows/mapped_file_handle.ipp"
"include/llfio/v2.0/detail/impl/windows/path_discovery.ipp"
@@ -84,10 +91,8 @@ set(llfio_HEADERS
"include/llfio/v2.0/file_handle.hpp"
"include/llfio/v2.0/fs_handle.hpp"
"include/llfio/v2.0/handle.hpp"
- "include/llfio/v2.0/io_handle.hpp"
- "include/llfio/v2.0/io_multiplexer.hpp"
"include/llfio/v2.0/llfio.hpp"
- "include/llfio/v2.0/lockable_io_handle.hpp"
+ "include/llfio/v2.0/lockable_byte_io_handle.hpp"
"include/llfio/v2.0/logging.hpp"
"include/llfio/v2.0/map_handle.hpp"
"include/llfio/v2.0/mapped.hpp"
diff --git a/cmake/tests.cmake b/cmake/tests.cmake
index 9201bd4f..8e64425f 100644
--- a/cmake/tests.cmake
+++ b/cmake/tests.cmake
@@ -1,6 +1,7 @@
# DO NOT EDIT, GENERATED BY SCRIPT
set(llfio_TESTS
"test/test_kernel_decl.hpp"
+ "test/tests/byte_socket_handle.cpp"
"test/tests/clone_extents.cpp"
"test/tests/current_path.cpp"
"test/tests/directory_handle_create_close/kernel_directory_handle.cpp.hpp"
diff --git a/example/ts_examples.cpp b/example/ts_examples.cpp
index b0f81cf5..fed56b79 100644
--- a/example/ts_examples.cpp
+++ b/example/ts_examples.cpp
@@ -30,7 +30,7 @@ using namespace std;
#define throws(x)
#define VALUE .value()
-inline io_handle::buffers_type read_all(io_handle &h, io_handle::io_request<io_handle::buffers_type> reqs, deadline d = deadline()) throws(file_io_error)
+inline byte_io_handle::buffers_type read_all(byte_io_handle &h, byte_io_handle::io_request<byte_io_handle::buffers_type> reqs, deadline d = deadline()) throws(file_io_error)
{
// Record beginning if deadline is specified
chrono::steady_clock::time_point began_steady;
@@ -38,17 +38,17 @@ inline io_handle::buffers_type read_all(io_handle &h, io_handle::io_request<io_h
began_steady = chrono::steady_clock::now();
// Take copy of input buffers onto stack, and set output buffers to buffers supplied
- auto *input_buffers_mem = reinterpret_cast<io_handle::buffer_type *>(alloca(reqs.buffers.size() * sizeof(io_handle::buffer_type)));
- auto *input_buffers_sizes = reinterpret_cast<io_handle::size_type *>(alloca(reqs.buffers.size() * sizeof(io_handle::size_type)));
- io_handle::buffers_type output_buffers(reqs.buffers);
- io_handle::io_request<io_handle::buffers_type> creq({input_buffers_mem, reqs.buffers.size()}, 0);
+ auto *input_buffers_mem = reinterpret_cast<byte_io_handle::buffer_type *>(alloca(reqs.buffers.size() * sizeof(byte_io_handle::buffer_type)));
+ auto *input_buffers_sizes = reinterpret_cast<byte_io_handle::size_type *>(alloca(reqs.buffers.size() * sizeof(byte_io_handle::size_type)));
+ byte_io_handle::buffers_type output_buffers(reqs.buffers);
+ byte_io_handle::io_request<byte_io_handle::buffers_type> creq({input_buffers_mem, reqs.buffers.size()}, 0);
for(size_t n = 0; n < reqs.buffers.size(); n++)
{
// Copy input buffer to stack and retain original size
creq.buffers[n] = reqs.buffers[n];
input_buffers_sizes[n] = reqs.buffers[n].size();
// Set output buffer length to zero
- output_buffers[n] = io_handle::buffer_type{output_buffers[n].data(), 0};
+ output_buffers[n] = byte_io_handle::buffer_type{output_buffers[n].data(), 0};
}
// Track which output buffer we are currently filling
@@ -71,7 +71,7 @@ inline io_handle::buffers_type read_all(io_handle &h, io_handle::io_request<io_h
nd = d;
}
// Partial fill buffers with current request
- io_handle::buffers_type filled = h.read(creq, nd) VALUE;
+ byte_io_handle::buffers_type filled = h.read(creq, nd) VALUE;
(void) filled;
// Adjust output buffers by what was filled, and prepare input
@@ -82,15 +82,15 @@ inline io_handle::buffers_type read_all(io_handle &h, io_handle::io_request<io_h
auto &input_buffer = creq.buffers[n];
auto &output_buffer = output_buffers[idx + n];
creq.offset += input_buffer.size();
- output_buffer = io_handle::buffer_type{output_buffer.data(), output_buffer.size() + input_buffer.size()};
+ output_buffer = byte_io_handle::buffer_type{output_buffer.data(), output_buffer.size() + input_buffer.size()};
// Adjust input buffer to amount remaining
- input_buffer = io_handle::buffer_type{input_buffer.data() + input_buffer.size(), input_buffers_sizes[idx + n] - output_buffer.size()};
+ input_buffer = byte_io_handle::buffer_type{input_buffer.data() + input_buffer.size(), input_buffers_sizes[idx + n] - output_buffer.size()};
}
// Remove completely filled input buffers
while(!creq.buffers.empty() && creq.buffers[0].size() == 0)
{
- creq.buffers = io_handle::buffers_type(creq.buffers.data() + 1, creq.buffers.size() - 1);
+ creq.buffers = byte_io_handle::buffers_type(creq.buffers.data() + 1, creq.buffers.size() - 1);
++idx;
}
} while(!creq.buffers.empty());
diff --git a/include/llfio/revision.hpp b/include/llfio/revision.hpp
index 870d6a06..a1f1ca75 100644
--- a/include/llfio/revision.hpp
+++ b/include/llfio/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 LLFIO_PREVIOUS_COMMIT_REF fba45d6bacbdc7710e257a0b3d229fe3ef3a1f5d
-#define LLFIO_PREVIOUS_COMMIT_DATE "2021-12-14 16:26:38 +00:00"
-#define LLFIO_PREVIOUS_COMMIT_UNIQUE fba45d6b
+#define LLFIO_PREVIOUS_COMMIT_REF 0906bce16f8767db4ecdbe3992eaf0f62c6dc0fe
+#define LLFIO_PREVIOUS_COMMIT_DATE "2021-12-21 21:17:38 +00:00"
+#define LLFIO_PREVIOUS_COMMIT_UNIQUE 0906bce1
diff --git a/include/llfio/v2.0/algorithm/handle_adapter/combining.hpp b/include/llfio/v2.0/algorithm/handle_adapter/combining.hpp
index 1cc60973..ea49ee69 100644
--- a/include/llfio/v2.0/algorithm/handle_adapter/combining.hpp
+++ b/include/llfio/v2.0/algorithm/handle_adapter/combining.hpp
@@ -40,7 +40,7 @@ namespace algorithm
{
file_handle_wrapper() = default;
using file_handle::file_handle;
- file_handle_wrapper(native_handle_type nativeh, io_handle::caching _caching, io_handle::flag flags, io_multiplexer *ctx)
+ file_handle_wrapper(native_handle_type nativeh, byte_io_handle::caching _caching, byte_io_handle::flag flags, byte_io_multiplexer *ctx)
: file_handle(nativeh, 0, 0, _caching, flags, ctx)
{
}
@@ -49,7 +49,7 @@ namespace algorithm
using combining_handle_adapter_choose_base = std::conditional_t< //
std::is_base_of<file_handle, Target>::value && (std::is_void<Source>::value || std::is_base_of<file_handle, Source>::value), //
file_handle_wrapper, //
- io_handle //
+ byte_io_handle //
>;
template <class A, class B> struct is_void_or_io_request_compatible
{
@@ -64,26 +64,26 @@ namespace algorithm
static_assert(is_void_or_io_request_compatible<Target, Source>::value, "Combined handle types do not share io_request<buffers_type>");
public:
- using path_type = io_handle::path_type;
- using extent_type = io_handle::extent_type;
- using size_type = io_handle::size_type;
- using mode = io_handle::mode;
- using creation = io_handle::creation;
- using caching = io_handle::caching;
- using flag = io_handle::flag;
- using buffer_type = io_handle::buffer_type;
- using const_buffer_type = io_handle::const_buffer_type;
- using buffers_type = io_handle::buffers_type;
- using const_buffers_type = io_handle::const_buffers_type;
- template <class T> using io_request = io_handle::io_request<T>;
- template <class T> using io_result = io_handle::io_result<T>;
+ using path_type = byte_io_handle::path_type;
+ using extent_type = byte_io_handle::extent_type;
+ using size_type = byte_io_handle::size_type;
+ using mode = byte_io_handle::mode;
+ using creation = byte_io_handle::creation;
+ using caching = byte_io_handle::caching;
+ using flag = byte_io_handle::flag;
+ using buffer_type = byte_io_handle::buffer_type;
+ using const_buffer_type = byte_io_handle::const_buffer_type;
+ using buffers_type = byte_io_handle::buffers_type;
+ using const_buffers_type = byte_io_handle::const_buffers_type;
+ template <class T> using io_request = byte_io_handle::io_request<T>;
+ template <class T> using io_result = byte_io_handle::io_result<T>;
using target_handle_type = Target;
using source_handle_type = Source;
protected:
static constexpr bool _have_source = !std::is_void<source_handle_type>::value;
- using _source_handle_type = std::conditional_t<!_have_source, io_handle, source_handle_type>;
+ using _source_handle_type = std::conditional_t<!_have_source, byte_io_handle, source_handle_type>;
target_handle_type *_target{nullptr};
_source_handle_type *_source{nullptr};
@@ -115,13 +115,13 @@ namespace algorithm
protected:
combining_handle_adapter_base() = default;
- constexpr combining_handle_adapter_base(target_handle_type *a, _source_handle_type *b, mode _mode, flag flags, io_multiplexer *ctx)
+ constexpr combining_handle_adapter_base(target_handle_type *a, _source_handle_type *b, mode _mode, flag flags, byte_io_multiplexer *ctx)
: Base(_native_handle(_mode), _combine_caching(a, b), flags, ctx)
, _target(a)
, _source(b)
{
}
- combining_handle_adapter_base(target_handle_type *a, void *b, mode _mode, flag flags, io_multiplexer *ctx)
+ combining_handle_adapter_base(target_handle_type *a, void *b, mode _mode, flag flags, byte_io_multiplexer *ctx)
: Base(_native_handle(_mode), a->caching(), flags, ctx)
, _target(a)
, _source(reinterpret_cast<_source_handle_type *>(b))
@@ -315,19 +315,19 @@ namespace algorithm
static constexpr bool _have_source = _base::_have_source;
public:
- using path_type = io_handle::path_type;
- using extent_type = io_handle::extent_type;
- using size_type = io_handle::size_type;
- using mode = io_handle::mode;
- using creation = io_handle::creation;
- using caching = io_handle::caching;
- using flag = io_handle::flag;
- using buffer_type = io_handle::buffer_type;
- using const_buffer_type = io_handle::const_buffer_type;
- using buffers_type = io_handle::buffers_type;
- using const_buffers_type = io_handle::const_buffers_type;
- template <class T> using io_request = io_handle::io_request<T>;
- template <class T> using io_result = io_handle::io_result<T>;
+ using path_type = byte_io_handle::path_type;
+ using extent_type = byte_io_handle::extent_type;
+ using size_type = byte_io_handle::size_type;
+ using mode = byte_io_handle::mode;
+ using creation = byte_io_handle::creation;
+ using caching = byte_io_handle::caching;
+ using flag = byte_io_handle::flag;
+ using buffer_type = byte_io_handle::buffer_type;
+ using const_buffer_type = byte_io_handle::const_buffer_type;
+ using buffers_type = byte_io_handle::buffers_type;
+ using const_buffers_type = byte_io_handle::const_buffers_type;
+ template <class T> using io_request = byte_io_handle::io_request<T>;
+ template <class T> using io_result = byte_io_handle::io_result<T>;
combining_handle_adapter_base() = default;
using _base::_base;
@@ -505,7 +505,7 @@ namespace algorithm
If both input handle types have a base of `file_handle`, `combining_handle_adapter`
inherits from `file_handle` and provides the extra member functions which `file_handle`
- provides over `io_handle`. If not, it inherits from `io_handle`, and provides that
+ provides over `byte_io_handle`. If not, it inherits from `byte_io_handle`, and provides that
class' reduced functionality instead.
The default implementation of `read()` and `write()` allocate temporary buffers, and
@@ -533,7 +533,7 @@ namespace algorithm
Destroying the adapter does not destroy the attached handles. Closing the adapter
does close the attached handles.
- \todo I have been lazy and used public inheritance from `io_handle` and `file_handle`.
+ \todo I have been lazy and used public inheritance from `byte_io_handle` and `file_handle`.
I should use protected inheritance to prevent slicing, and expose all the public functions by hand.
*/
template <template <class, class> class Op, class Target, class Source> class combining_handle_adapter : public Op<Target, Source>::template override_<detail::combining_handle_adapter_base<Op, Target, Source, detail::combining_handle_adapter_choose_base<Target, Source>>>
@@ -541,19 +541,19 @@ namespace algorithm
using _base = typename Op<Target, Source>::template override_<detail::combining_handle_adapter_base<Op, Target, Source, detail::combining_handle_adapter_choose_base<Target, Source>>>;
public:
- using path_type = io_handle::path_type;
- using extent_type = io_handle::extent_type;
- using size_type = io_handle::size_type;
- using mode = io_handle::mode;
- using creation = io_handle::creation;
- using caching = io_handle::caching;
- using flag = io_handle::flag;
- using buffer_type = io_handle::buffer_type;
- using const_buffer_type = io_handle::const_buffer_type;
- using buffers_type = io_handle::buffers_type;
- using const_buffers_type = io_handle::const_buffers_type;
- template <class T> using io_request = io_handle::io_request<T>;
- template <class T> using io_result = io_handle::io_result<T>;
+ using path_type = byte_io_handle::path_type;
+ using extent_type = byte_io_handle::extent_type;
+ using size_type = byte_io_handle::size_type;
+ using mode = byte_io_handle::mode;
+ using creation = byte_io_handle::creation;
+ using caching = byte_io_handle::caching;
+ using flag = byte_io_handle::flag;
+ using buffer_type = byte_io_handle::buffer_type;
+ using const_buffer_type = byte_io_handle::const_buffer_type;
+ using buffers_type = byte_io_handle::buffers_type;
+ using const_buffers_type = byte_io_handle::const_buffers_type;
+ template <class T> using io_request = byte_io_handle::io_request<T>;
+ template <class T> using io_result = byte_io_handle::io_result<T>;
using target_handle_type = Target;
using source_handle_type = Source;
@@ -563,7 +563,7 @@ namespace algorithm
combining_handle_adapter() = default;
//! Constructor, passing any extra arguments to `Op::override`.
template <class... Args>
- combining_handle_adapter(target_handle_type *a, source_handle_type *b, mode _mode = mode::write, flag flags = flag::none, io_multiplexer *ctx = nullptr, Args &&... args)
+ combining_handle_adapter(target_handle_type *a, source_handle_type *b, mode _mode = mode::write, flag flags = flag::none, byte_io_multiplexer *ctx = nullptr, Args &&... args)
: _base(a, b, _mode, flags, ctx, std::forward<Args>(args)...)
{
}
diff --git a/include/llfio/v2.0/io_handle.hpp b/include/llfio/v2.0/byte_io_handle.hpp
index bd333681..0a71f013 100644
--- a/include/llfio/v2.0/io_handle.hpp
+++ b/include/llfio/v2.0/byte_io_handle.hpp
@@ -25,9 +25,9 @@ Distributed under the Boost Software License, Version 1.0.
#ifndef LLFIO_IO_HANDLE_H
#define LLFIO_IO_HANDLE_H
-#include "io_multiplexer.hpp"
+#include "byte_io_multiplexer.hpp"
-//! \file io_handle.hpp Provides a byte-orientated i/o handle
+//! \file byte_io_handle.hpp Provides a byte-orientated i/o handle
#ifdef _MSC_VER
#pragma warning(push)
@@ -36,12 +36,12 @@ Distributed under the Boost Software License, Version 1.0.
LLFIO_V2_NAMESPACE_EXPORT_BEGIN
-/*! \class io_handle
+/*! \class byte_io_handle
\brief A handle to something capable of scatter-gather byte i/o.
*/
-class LLFIO_DECL io_handle : public handle
+class LLFIO_DECL byte_io_handle : public handle
{
- friend class io_multiplexer;
+ friend class byte_io_multiplexer;
public:
using path_type = handle::path_type;
@@ -51,43 +51,43 @@ public:
using creation = handle::creation;
using caching = handle::caching;
using flag = handle::flag;
- using barrier_kind = io_multiplexer::barrier_kind;
- using buffer_type = io_multiplexer::buffer_type;
- using const_buffer_type = io_multiplexer::const_buffer_type;
- using buffers_type = io_multiplexer::buffers_type;
- using const_buffers_type = io_multiplexer::const_buffers_type;
- using registered_buffer_type = io_multiplexer::registered_buffer_type;
- template <class T> using io_request = io_multiplexer::io_request<T>;
- template <class T> using io_result = io_multiplexer::io_result<T>;
- template <class T> using awaitable = io_multiplexer::awaitable<T>;
+ using barrier_kind = byte_io_multiplexer::barrier_kind;
+ using buffer_type = byte_io_multiplexer::buffer_type;
+ using const_buffer_type = byte_io_multiplexer::const_buffer_type;
+ using buffers_type = byte_io_multiplexer::buffers_type;
+ using const_buffers_type = byte_io_multiplexer::const_buffers_type;
+ using registered_buffer_type = byte_io_multiplexer::registered_buffer_type;
+ template <class T> using io_request = byte_io_multiplexer::io_request<T>;
+ template <class T> using io_result = byte_io_multiplexer::io_result<T>;
+ template <class T> using awaitable = byte_io_multiplexer::awaitable<T>;
protected:
- io_multiplexer *_ctx{nullptr}; // +4 or +8 bytes
+ byte_io_multiplexer *_ctx{nullptr}; // +4 or +8 bytes
public:
//! Default constructor
- constexpr io_handle() {} // NOLINT
- ~io_handle() = default;
+ constexpr byte_io_handle() {} // NOLINT
+ ~byte_io_handle() = default;
//! Construct a handle from a supplied native handle
- constexpr explicit io_handle(native_handle_type h, caching caching, flag flags, io_multiplexer *ctx)
+ constexpr explicit byte_io_handle(native_handle_type h, caching caching, flag flags, byte_io_multiplexer *ctx)
: handle(h, caching, flags)
, _ctx(ctx)
{
}
//! Explicit conversion from handle permitted
- explicit constexpr io_handle(handle &&o, io_multiplexer *ctx) noexcept
+ explicit constexpr byte_io_handle(handle &&o, byte_io_multiplexer *ctx) noexcept
: handle(std::move(o))
, _ctx(ctx)
{
}
//! Move construction permitted
- io_handle(io_handle &&) = default;
+ byte_io_handle(byte_io_handle &&) = default;
//! No copy construction (use `clone()`)
- io_handle(const io_handle &) = delete;
+ byte_io_handle(const byte_io_handle &) = delete;
//! Move assignment permitted
- io_handle &operator=(io_handle &&) = default;
+ byte_io_handle &operator=(byte_io_handle &&) = default;
//! No copy assignment
- io_handle &operator=(const io_handle &) = delete;
+ byte_io_handle &operator=(const byte_io_handle &) = delete;
LLFIO_MAKE_FREE_FUNCTION
LLFIO_HEADERS_ONLY_VIRTUAL_SPEC result<void> close() noexcept override
@@ -102,7 +102,7 @@ public:
/*! \brief The i/o multiplexer this handle will use to multiplex i/o. If this returns null,
then this handle has not been registered with an i/o multiplexer yet.
*/
- io_multiplexer *multiplexer() const noexcept { return _ctx; }
+ byte_io_multiplexer *multiplexer() const noexcept { return _ctx; }
/*! \brief Sets the i/o multiplexer this handle will use to implement `read()`, `write()` and `barrier()`.
@@ -116,13 +116,14 @@ public:
\mallocs Multiple dynamic memory allocations and deallocations.
*/
- virtual result<void> set_multiplexer(io_multiplexer *c = this_thread::multiplexer()) noexcept; // implementation is below
+ virtual result<void> set_multiplexer(byte_io_multiplexer *c = this_thread::multiplexer()) noexcept; // implementation is below
protected:
//! The virtualised implementation of `max_buffers()` used if no multiplexer has been set.
LLFIO_HEADERS_ONLY_VIRTUAL_SPEC size_t _do_max_buffers() const noexcept;
//! The virtualised implementation of `allocate_registered_buffer()` used if no multiplexer has been set.
- LLFIO_HEADERS_ONLY_VIRTUAL_SPEC result<registered_buffer_type> _do_allocate_registered_buffer(size_t &bytes) noexcept; // default implementation is in map_handle.hpp
+ LLFIO_HEADERS_ONLY_VIRTUAL_SPEC result<registered_buffer_type>
+ _do_allocate_registered_buffer(size_t &bytes) noexcept; // default implementation is in map_handle.hpp
//! The virtualised implementation of `read()` used if no multiplexer has been set.
LLFIO_HEADERS_ONLY_VIRTUAL_SPEC io_result<buffers_type> _do_read(io_request<buffers_type> reqs, deadline d) noexcept;
//! The virtualised implementation of `read()` used if no multiplexer has been set.
@@ -150,6 +151,10 @@ protected:
const auto diff = (uintptr_t) storage & (state_reqs.second - 1);
storage += state_reqs.second - diff;
auto *state = _ctx->construct_and_init_io_operation({storage, state_reqs.first}, this, nullptr, std::move(base), d, std::move(reqs));
+ if(state == nullptr)
+ {
+ return errc::resource_unavailable_try_again;
+ }
OUTCOME_TRY(_ctx->flush_inited_io_operations());
while(!is_finished(_ctx->check_io_operation(state)))
{
@@ -169,6 +174,10 @@ protected:
const auto diff = (uintptr_t) storage & (state_reqs.second - 1);
storage += state_reqs.second - diff;
auto *state = _ctx->construct_and_init_io_operation({storage, state_reqs.first}, this, nullptr, std::move(base), d, std::move(reqs));
+ if(state == nullptr)
+ {
+ return errc::resource_unavailable_try_again;
+ }
OUTCOME_TRY(_ctx->flush_inited_io_operations());
while(!is_finished(_ctx->check_io_operation(state)))
{
@@ -180,7 +189,8 @@ protected:
state->~io_operation_state();
return ret;
}
- io_result<const_buffers_type> _do_multiplexer_barrier(registered_buffer_type &&base, io_request<const_buffers_type> reqs, barrier_kind kind, deadline d) noexcept
+ io_result<const_buffers_type> _do_multiplexer_barrier(registered_buffer_type &&base, io_request<const_buffers_type> reqs, barrier_kind kind,
+ deadline d) noexcept
{
LLFIO_DEADLINE_TO_SLEEP_INIT(d);
const auto state_reqs = _ctx->io_state_requirements();
@@ -188,6 +198,10 @@ protected:
const auto diff = (uintptr_t) storage & (state_reqs.second - 1);
storage += state_reqs.second - diff;
auto *state = _ctx->construct_and_init_io_operation({storage, state_reqs.first}, this, nullptr, std::move(base), d, std::move(reqs), kind);
+ if(state == nullptr)
+ {
+ return errc::resource_unavailable_try_again;
+ }
OUTCOME_TRY(_ctx->flush_inited_io_operations());
while(!is_finished(_ctx->check_io_operation(state)))
{
@@ -228,7 +242,7 @@ public:
{
return _do_max_buffers();
}
- return _ctx->do_io_handle_max_buffers(this);
+ return _ctx->do_byte_io_handle_max_buffers(this);
}
/*! \brief Request the allocation of a new registered i/o buffer with the system suitable
@@ -257,7 +271,7 @@ public:
{
return _do_allocate_registered_buffer(bytes);
}
- return _ctx->do_io_handle_allocate_registered_buffer(this, bytes);
+ return _ctx->do_byte_io_handle_allocate_registered_buffer(this, bytes);
}
/*! \brief Read data from the open handle, preferentially using any i/o multiplexer set over the
@@ -280,10 +294,16 @@ public:
\mallocs The default synchronous implementation in file_handle performs no memory allocation.
*/
LLFIO_MAKE_FREE_FUNCTION
- io_result<buffers_type> read(io_request<buffers_type> reqs, deadline d = deadline()) noexcept { return (_ctx == nullptr) ? _do_read(reqs, d) : _do_multiplexer_read({}, reqs, d); }
+ io_result<buffers_type> read(io_request<buffers_type> reqs, deadline d = deadline()) noexcept
+ {
+ return (_ctx == nullptr) ? _do_read(reqs, d) : _do_multiplexer_read({}, reqs, d);
+ }
//! \overload Registered buffer overload, scatter list **must** be wholly within the registered buffer
LLFIO_MAKE_FREE_FUNCTION
- io_result<buffers_type> read(registered_buffer_type base, io_request<buffers_type> reqs, deadline d = deadline()) noexcept { return (_ctx == nullptr) ? _do_read(std::move(base), reqs, d) : _do_multiplexer_read(std::move(base), reqs, d); }
+ io_result<buffers_type> read(registered_buffer_type base, io_request<buffers_type> reqs, deadline d = deadline()) noexcept
+ {
+ return (_ctx == nullptr) ? _do_read(std::move(base), reqs, d) : _do_multiplexer_read(std::move(base), reqs, d);
+ }
//! \overload Convenience initialiser list based overload for `read()`
LLFIO_MAKE_FREE_FUNCTION
io_result<size_type> read(extent_type offset, std::initializer_list<buffer_type> lst, deadline d = deadline()) noexcept
@@ -322,10 +342,16 @@ public:
\mallocs The default synchronous implementation in file_handle performs no memory allocation.
*/
LLFIO_MAKE_FREE_FUNCTION
- io_result<const_buffers_type> write(io_request<const_buffers_type> reqs, deadline d = deadline()) noexcept { return (_ctx == nullptr) ? _do_write(reqs, d) : _do_multiplexer_write({}, std::move(reqs), d); }
+ io_result<const_buffers_type> write(io_request<const_buffers_type> reqs, deadline d = deadline()) noexcept
+ {
+ return (_ctx == nullptr) ? _do_write(reqs, d) : _do_multiplexer_write({}, std::move(reqs), d);
+ }
//! \overload Registered buffer overload, gather list **must** be wholly within the registered buffer
LLFIO_MAKE_FREE_FUNCTION
- io_result<const_buffers_type> write(registered_buffer_type base, io_request<const_buffers_type> reqs, deadline d = deadline()) noexcept { return (_ctx == nullptr) ? _do_write(std::move(base), reqs, d) : _do_multiplexer_write(std::move(base), std::move(reqs), d); }
+ io_result<const_buffers_type> write(registered_buffer_type base, io_request<const_buffers_type> reqs, deadline d = deadline()) noexcept
+ {
+ return (_ctx == nullptr) ? _do_write(std::move(base), reqs, d) : _do_multiplexer_write(std::move(base), std::move(reqs), d);
+ }
//! \overload Convenience initialiser list based overload for `write()`
LLFIO_MAKE_FREE_FUNCTION
io_result<size_type> write(extent_type offset, std::initializer_list<const_buffer_type> lst, deadline d = deadline()) noexcept
@@ -373,7 +399,8 @@ public:
\mallocs None.
*/
LLFIO_MAKE_FREE_FUNCTION
- LLFIO_HEADERS_ONLY_VIRTUAL_SPEC io_result<const_buffers_type> barrier(io_request<const_buffers_type> reqs = io_request<const_buffers_type>(), barrier_kind kind = barrier_kind::nowait_data_only, deadline d = deadline()) noexcept
+ LLFIO_HEADERS_ONLY_VIRTUAL_SPEC io_result<const_buffers_type> barrier(io_request<const_buffers_type> reqs = io_request<const_buffers_type>(),
+ barrier_kind kind = barrier_kind::nowait_data_only, deadline d = deadline()) noexcept
{
return (_ctx == nullptr) ? _do_barrier(reqs, kind, d) : _do_multiplexer_barrier({}, std::move(reqs), kind, d);
}
@@ -454,7 +481,8 @@ public:
and finishes immediately, no coroutine suspension occurs.
*/
LLFIO_MAKE_FREE_FUNCTION
- awaitable<io_result<const_buffers_type>> co_barrier(io_request<const_buffers_type> reqs = io_request<const_buffers_type>(), barrier_kind kind = barrier_kind::nowait_data_only, deadline d = deadline()) noexcept
+ awaitable<io_result<const_buffers_type>> co_barrier(io_request<const_buffers_type> reqs = io_request<const_buffers_type>(),
+ barrier_kind kind = barrier_kind::nowait_data_only, deadline d = deadline()) noexcept
{
if(_ctx == nullptr)
{
@@ -465,11 +493,12 @@ public:
return ret;
}
};
-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!");
+static_assert((sizeof(void *) == 4 && sizeof(byte_io_handle) == 20) || (sizeof(void *) == 8 && sizeof(byte_io_handle) == 32),
+ "byte_io_handle is not 20 or 32 bytes in size!");
// Out of line definition purely to work around a bug in GCC where if marked inline,
// its visibility is hidden and links fail
-inline result<void> io_handle::set_multiplexer(io_multiplexer *c) noexcept
+inline result<void> byte_io_handle::set_multiplexer(byte_io_multiplexer *c) noexcept
{
if(!is_multiplexable())
{
@@ -481,12 +510,12 @@ inline result<void> io_handle::set_multiplexer(io_multiplexer *c) noexcept
}
if(_ctx != nullptr)
{
- OUTCOME_TRY(_ctx->do_io_handle_deregister(this));
+ OUTCOME_TRY(_ctx->do_byte_io_handle_deregister(this));
_ctx = nullptr;
}
if(c != nullptr)
{
- OUTCOME_TRY(auto &&state, c->do_io_handle_register(this));
+ OUTCOME_TRY(auto &&state, c->do_byte_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)
{
@@ -501,15 +530,16 @@ inline result<void> io_handle::set_multiplexer(io_multiplexer *c) noexcept
return success();
}
-inline size_t io_multiplexer::do_io_handle_max_buffers(const io_handle *h) const noexcept
+inline size_t byte_io_multiplexer::do_byte_io_handle_max_buffers(const byte_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
+inline result<byte_io_multiplexer::registered_buffer_type> byte_io_multiplexer::do_byte_io_handle_allocate_registered_buffer(byte_io_handle *h,
+ size_t &bytes) noexcept
{
return h->_do_allocate_registered_buffer(bytes);
}
-template <class T> inline bool io_multiplexer::awaitable<T>::await_ready() noexcept
+template <class T> inline bool byte_io_multiplexer::awaitable<T>::await_ready() noexcept
{
auto state = _state->current_state();
if(is_initialised(state))
@@ -520,7 +550,7 @@ template <class T> inline bool io_multiplexer::awaitable<T>::await_ready() noexc
}
return is_finished(state);
}
-template <class T> inline io_multiplexer::awaitable<T>::~awaitable()
+template <class T> inline byte_io_multiplexer::awaitable<T>::~awaitable()
{
if(_state != nullptr)
{
@@ -548,6 +578,50 @@ template <class T> inline io_multiplexer::awaitable<T>::~awaitable()
}
}
+/*! \class pollable_handle
+\brief A handle type which can be supplied to `poll()`.
+*/
+class pollable_handle
+{
+ virtual const handle &_get_handle() const noexcept = 0;
+
+public:
+ virtual ~pollable_handle() {}
+};
+
+//! What to poll
+QUICKCPPLIB_BITFIELD_BEGIN_T(poll_what, uint8_t) //
+{
+none = 0U, //!< Query nothing for this handle.
+
+is_readable = (1U << 0U), //!< If this handle is readable.
+is_writable = (1U << 1U), //!< If this handle is writable.
+is_errored = (1U << 2U), //!< If this handle is errored.
+is_closed = (1U << 3U), //!< If this handle is closed/hung up.
+
+not_pollable = (1U << 7U) //!< This handle is not pollable.
+} //
+QUICKCPPLIB_BITFIELD_END(poll_what)
+
+/*! \brief Polls a list of pollable handles awaiting a change in state.
+\return The number of handles with changed state. Handles not `is_kernel_handle()`
+receive `poll_what::not_pollable`.
+\param out An array of `poll_what` set with the results of the poll.
+\param handles An array of pointers to `handle`. Individual pointers can be null if you want to skip them.
+\param query An array of `poll_what` to check.
+\param d An optional timeout.
+\errors Whatever POSIX `poll()` or Windows `select()` can return.
+
+Note that the maximum number of handles which can be passed to this function is 1024
+(the platform syscall may refuse even that many). Note that this function is `O(N)` to
+handle count, so more than a few hundred is a bad idea in any case.
+If you need to wait on more handles than this, you need to implement a `byte_io_multiplexer`
+for your platform.
+
+The sizes of `out`, `handles` and `query` must be the same, or an error is returned.
+*/
+LLFIO_HEADERS_ONLY_FUNC_SPEC result<size_t> poll(span<poll_what> out, span<pollable_handle *> handles, span<const poll_what> query, deadline d = {}) noexcept;
+
// BEGIN make_free_functions.py
/*! \brief Read data from the open handle.
@@ -568,7 +642,8 @@ returned if deadline i/o is not possible with this particular handle configurati
reading from regular files on POSIX or reading from a non-overlapped HANDLE on Windows).
\mallocs The default synchronous implementation in file_handle performs no memory allocation.
*/
-inline io_handle::io_result<io_handle::buffers_type> read(io_handle &self, io_handle::io_request<io_handle::buffers_type> reqs, deadline d = deadline()) noexcept
+inline byte_io_handle::io_result<byte_io_handle::buffers_type> read(byte_io_handle &self, byte_io_handle::io_request<byte_io_handle::buffers_type> reqs,
+ deadline d = deadline()) noexcept
{
return self.read(std::forward<decltype(reqs)>(reqs), std::forward<decltype(d)>(d));
}
@@ -593,12 +668,14 @@ returned if deadline i/o is not possible with this particular handle configurati
writing to regular files on POSIX or writing to a non-overlapped HANDLE on Windows).
\mallocs The default synchronous implementation in file_handle performs no memory allocation.
*/
-inline io_handle::io_result<io_handle::const_buffers_type> write(io_handle &self, io_handle::io_request<io_handle::const_buffers_type> reqs, deadline d = deadline()) noexcept
+inline byte_io_handle::io_result<byte_io_handle::const_buffers_type>
+write(byte_io_handle &self, byte_io_handle::io_request<byte_io_handle::const_buffers_type> reqs, deadline d = deadline()) noexcept
{
return self.write(std::forward<decltype(reqs)>(reqs), std::forward<decltype(d)>(d));
}
//! \overload
-inline io_handle::io_result<io_handle::size_type> write(io_handle &self, io_handle::extent_type offset, std::initializer_list<io_handle::const_buffer_type> lst, deadline d = deadline()) noexcept
+inline byte_io_handle::io_result<byte_io_handle::size_type>
+write(byte_io_handle &self, byte_io_handle::extent_type offset, std::initializer_list<byte_io_handle::const_buffer_type> lst, deadline d = deadline()) noexcept
{
return self.write(std::forward<decltype(offset)>(offset), std::forward<decltype(lst)>(lst), std::forward<decltype(d)>(d));
}
@@ -616,14 +693,14 @@ LLFIO_V2_NAMESPACE_END
#if LLFIO_ENABLE_TEST_IO_MULTIPLEXERS
#include "detail/impl/windows/test/iocp_multiplexer.ipp"
#else
-#include "detail/impl/windows/io_handle.ipp"
+#include "detail/impl/windows/byte_io_handle.ipp"
#endif
#else
#if LLFIO_ENABLE_TEST_IO_MULTIPLEXERS
//#include "detail/impl/posix/test/io_uring_multiplexer.ipp"
#else
#endif
-#include "detail/impl/posix/io_handle.ipp"
+#include "detail/impl/posix/byte_io_handle.ipp"
#endif
#undef LLFIO_INCLUDED_BY_HEADER
#endif
diff --git a/include/llfio/v2.0/io_multiplexer.hpp b/include/llfio/v2.0/byte_io_multiplexer.hpp
index 070cf0c1..6bb185ee 100644
--- a/include/llfio/v2.0/io_multiplexer.hpp
+++ b/include/llfio/v2.0/byte_io_multiplexer.hpp
@@ -1,5 +1,5 @@
-/* Multiplex file i/o
-(C) 2019-2020 Niall Douglas <http://www.nedproductions.biz/> (9 commits)
+/* Multiplex i/o
+(C) 2019-2021 Niall Douglas <http://www.nedproductions.biz/> (9 commits)
File Created: Nov 2019
@@ -39,16 +39,24 @@ Distributed under the Boost Software License, Version 1.0.
LLFIO_V2_NAMESPACE_EXPORT_BEGIN
-class io_handle;
+class byte_io_handle;
+class byte_socket_handle;
+class listening_socket_handle;
+namespace ip
+{
+ class address;
+}
//! The possible states of the i/o operation
enum class io_operation_state_type
{
unknown,
+
read_initialised,
read_initiated,
read_completed,
read_finished,
+
write_initialised,
write_initiated,
barrier_initialised,
@@ -141,11 +149,11 @@ constexpr inline bool is_finished(io_operation_state_type s) noexcept
return false;
}
-/*! \class io_multiplexer
+/*! \class byte_io_multiplexer
\brief A multiplexer of byte-orientated i/o.
LLFIO does not provide out-of-the-box multiplexing of byte i/o, however it does provide the ability
-to create `io_handle` instances with the `handle::flag::multiplexable` set. With that flag set, the
+to create `byte_io_handle` instances with the `handle::flag::multiplexable` set. With that flag set, the
following LLFIO classes change how they create handles with the kernel:
<table>
@@ -156,24 +164,25 @@ following LLFIO classes change how they create handles with the kernel:
<tr><td><code>mapped_file_handle</code><td>No effect<td>Creates `HANDLE` as `OVERLAPPED`, but i/o is to map not file
<tr><td><code>pipe_handle</code><td>Creates file descriptor as non-blocking<td>Creates `HANDLE` as `OVERLAPPED`
<tr><td><code>section_handle</code><td>No effect<td>Creates `HANDLE` as `OVERLAPPED`
+<tr><td><code>socket_handle</code><td>Creates file descriptor as non-blocking<td>Creates `HANDLE` as `OVERLAPPED` **and** as non-blocking
<tr><td><code>symlink_handle</code><td>No effect<td>Creates `HANDLE` as `OVERLAPPED`
</table>
If the i/o handle's multiplexer pointer is not null, the multiplexer instance is invoked to implement
-`io_handle::read()`, `io_handle::write()` and `io_handle::barrier()` by constructing an i/o
+`byte_io_handle::read()`, `byte_io_handle::write()` and `byte_io_handle::barrier()` by constructing an i/o
operation state on the stack, calling `.init_io_operation()` followed by `.flush_inited_io_operations()`,
and then spinning on `.check_io_operation()` and `.check_for_any_completed_io()` with the deadline
specified to the original blocking operation.
-If the i/o handle's multiplexer pointer is null, `io_handle::read()`, `io_handle::write()` and
-`io_handle::barrier()` all use virtually overridable implementations. The default implementations
+If the i/o handle's multiplexer pointer is null, `byte_io_handle::read()`, `byte_io_handle::write()` and
+`byte_io_handle::barrier()` all use virtually overridable implementations. The default implementations
emulate blocking semantics using the kernel's i/o poll function (literally `poll()` on POSIX,
`NtWaitForSingleObject()` on Windows) to sleep the thread until at least one byte of i/o occurs, or
the deadline specified is exceeded. This, obviously enough, can double the number of kernel syscalls
done per i/o, so using handles with the `handle::flag::multiplexable` flag set is not wise unless
you really need non-infinite deadline i/o.
*/
-class LLFIO_DECL io_multiplexer : public handle
+class LLFIO_DECL byte_io_multiplexer : public handle
{
struct _empty_t
{
@@ -191,8 +200,8 @@ public:
//! The kinds of write reordering barrier which can be performed.
enum class barrier_kind : uint8_t
{
- nowait_view_only, //!< Barrier mapped data only, non-blocking. This is highly optimised on NV-DIMM storage, but consider using `nvram_barrier()` for even better performance.
- wait_view_only, //!< Barrier mapped data only, block until it is done. This is highly optimised on NV-DIMM storage, but consider using `nvram_barrier()` for even better performance.
+ nowait_view_only, //!< Barrier mapped data only, non-blocking. This is highly optimised on NV-DIMM storage, but consider using `nvram_barrier()` for even better performance.
+ wait_view_only, //!< Barrier mapped data only, block until it is done. This is highly optimised on NV-DIMM storage, but consider using `nvram_barrier()` for even better performance.
nowait_data_only, //!< Barrier data only, non-blocking. This is highly optimised on NV-DIMM storage, but consider using `nvram_barrier()` for even better performance.
wait_data_only, //!< Barrier data only, block until it is done. This is highly optimised on NV-DIMM storage, but consider using `nvram_barrier()` for even better performance.
nowait_all, //!< Barrier data and the metadata to retrieve it, non-blocking.
@@ -408,13 +417,13 @@ public:
using Base = LLFIO_V2_NAMESPACE::result<T>;
size_type _bytes_transferred{static_cast<size_type>(-1)};
-#if defined(_MSC_VER) && !defined(__clang__) // workaround MSVC parsing bug
+#if defined(_MSC_VER) && _MSC_VER < 1930 /*VS2022*/ && !defined(__clang__) // workaround MSVC parsing bug
constexpr io_result()
: Base()
{
}
template <class... Args>
- constexpr io_result(Args &&... args)
+ constexpr io_result(Args &&...args)
: Base(std::forward<Args>(args)...)
{
}
@@ -461,57 +470,104 @@ public:
#endif
using handle::handle;
- constexpr io_multiplexer() {}
- io_multiplexer(io_multiplexer &&) = default;
- io_multiplexer(const io_multiplexer &) = delete;
- io_multiplexer &operator=(io_multiplexer &&) = default;
- io_multiplexer &operator=(const io_multiplexer &) = delete;
- ~io_multiplexer() = default;
+ constexpr byte_io_multiplexer() {}
+ byte_io_multiplexer(byte_io_multiplexer &&) = default;
+ byte_io_multiplexer(const byte_io_multiplexer &) = delete;
+ byte_io_multiplexer &operator=(byte_io_multiplexer &&) = default;
+ byte_io_multiplexer &operator=(const byte_io_multiplexer &) = delete;
+ ~byte_io_multiplexer() = default;
+
+ struct implementation_information_t
+ {
+ string_view name; //!< The name of the underlying implementation e.g. "OpenSSL", "IOCP, "io_uring", "Windows RIO" etc.
+ struct
+ {
+ uint16_t major{0}, minor{0}, patch{0};
+ } version; //!< Version of the underlying implementation. Could be a kernel version if appropriate.
+ string_view postfix; //!< The build config or other disambiguator from others with the same name and version.
+ struct multiplexes_t
+ {
+ struct kernel_t
+ {
+ uint16_t file_handle : 1; //!< This i/o multiplexer can register plain kernel `file_handle`.
+ uint16_t pipe_handle : 1; //!< This i/o multiplexer can register plain kernel `pipe_handle`.
+ uint16_t byte_socket_handle : 1; //!< This i/o multiplexer can register plain kernel `byte_socket_handle`.
+ uint16_t listening_socket_handle : 1; //!< This i/o multiplexer can register plain kernel `listening_socket_handle`.
+
+ constexpr kernel_t()
+ : file_handle(false)
+ , pipe_handle(false)
+ , byte_socket_handle(false)
+ , listening_socket_handle(false)
+ {
+ }
+ } kernel;
+ uint16_t registered_io_buffers : 1; //!< This i/o multiplexer implements registered i/o buffers.
+ uint16_t secure_byte_socket_source : 1; //!< This i/o multiplexer can register a `byte_socket_handle` obtained from a `secure_byte_socket_source`.
+ uint16_t http_byte_socket_source : 1; //!< This i/o multiplexer can register a `http_byte_socket_handle` obtained from a `http_byte_socket_source`.
+
+ constexpr multiplexes_t()
+ : registered_io_buffers(false)
+ , secure_byte_socket_source(false)
+ , http_byte_socket_source(false)
+ {
+ }
+ } multiplexes;
+
+ constexpr implementation_information_t() {}
+ };
+ //! Returns implementation information about an i/o multiplexer
+ LLFIO_HEADERS_ONLY_VIRTUAL_SPEC implementation_information_t implementation_information() const noexcept = 0;
public:
- //! Implements `io_handle` registration. The bottom two bits of the returned value are set into `_v.behaviour`'s `_multiplexer_state_bit0` and
+ //! Implements `byte_io_handle` registration. The bottom two bits of the returned value are set into `_v.behaviour`'s `_multiplexer_state_bit0` and
+ //! `_multiplexer_state_bit`
+ virtual result<uint8_t> do_byte_io_handle_register(byte_io_handle * /*unused*/) noexcept { return (uint8_t) 0; }
+ //! Implements `byte_io_handle` deregistration
+ virtual result<void> do_byte_io_handle_deregister(byte_io_handle * /*unused*/) noexcept { return success(); }
+ //! Implements `listening_socket_handle` registration. The bottom two bits of the returned value are set into `_v.behaviour`'s `_multiplexer_state_bit0` and
//! `_multiplexer_state_bit`
- virtual result<uint8_t> do_io_handle_register(io_handle * /*unused*/) noexcept { return (uint8_t) 0; }
- //! Implements `io_handle` deregistration
- virtual result<void> do_io_handle_deregister(io_handle * /*unused*/) noexcept { return success(); }
- //! Implements `io_handle::max_buffers()`
- 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;
+ virtual result<uint8_t> do_byte_io_handle_register(listening_socket_handle * /*unused*/) noexcept { return errc::operation_not_supported; }
+ //! Implements `listening_socket_handle` deregistration
+ virtual result<void> do_byte_io_handle_deregister(listening_socket_handle * /*unused*/) noexcept { return errc::operation_not_supported; }
+ //! Implements `byte_io_handle::max_buffers()`
+ LLFIO_HEADERS_ONLY_VIRTUAL_SPEC size_t do_byte_io_handle_max_buffers(const byte_io_handle *h) const noexcept;
+ //! Implements `byte_io_handle::allocate_registered_buffer()`
+ LLFIO_HEADERS_ONLY_VIRTUAL_SPEC result<registered_buffer_type> do_byte_io_handle_allocate_registered_buffer(byte_io_handle *h, size_t &bytes) noexcept;
struct io_operation_state_visitor;
/*! \brief An interface to a state for an i/o operation scheduled against an i/o multiplexer.
You will need to ask the i/o multiplexer for how much storage, and alignment, is required to
- store one of these using `io_multiplexer::io_state_requirements()`. Be aware that for some
+ store one of these using `byte_io_multiplexer::io_state_requirements()`. Be aware that for some
i/o multiplexers, quite a lot of storage (e.g. > 1Kb for IOCP on Windows) may be required.
You can either preallocate i/o operation states for later use, or use other determinism-maintaining
tricks to avoid dynamic memory allocation for i/o operation states.
- When you construct one of these using `io_multiplexer::init_io_operation()`, you must
+ When you construct one of these using `byte_io_multiplexer::init_io_operation()`, you must
pass in a pointer to a `io_operation_state_visitor`. This visitor will be called whenever
the lifecycle for the i/o state is about to change (it is called just before
`.current_state()` is changed, and with any per-state locks held).
The lifecycle for one of these is as follows:
- 1. i/o initialised. This is after `io_multiplexer::init_io_operation()` has been
+ 1. i/o initialised. This is after `byte_io_multiplexer::init_io_operation()` has been
called to initialise the i/o operation state. You can now no longer relocate the i/o
operation state in memory until the corresponding `*_finished()` visitor function is called.
2. i/o initiated. One is notified of this by the call of the corresponding visitor
- `*_initiated()` function. This may occur in `io_multiplexer::init_io_operation()`,
- in `io_multiplexer::flush_inited_io_operations()`, or **never at all** if the i/o
+ `*_initiated()` function. This may occur in `byte_io_multiplexer::init_io_operation()`,
+ in `byte_io_multiplexer::flush_inited_io_operations()`, or **never at all** if the i/o
completed immediately.
3. When the i/o completes, one is notified of the i/o's result by the call of the
corresponding `*_completed()` visitor function. This can occur *at any time*, and can be called
by **any** kernel thread, if the i/o multiplexer in use is used by multiple kernel threads.
- The completion functions are *usually* invoked by somebody calling `io_multiplexer::check_io_operation()`
- or `io_multiplexer::check_for_any_completed_io()`, but may also be called by an asynchronous system agent.
+ The completion functions are *usually* invoked by somebody calling `byte_io_multiplexer::check_io_operation()`
+ or `byte_io_multiplexer::check_for_any_completed_io()`, but may also be called by an asynchronous system agent.
4. The i/o operation state may still be in use by others. You must not relocate in memory the
- i/o operation state after `io_multiplexer::init_io_operation()` returns until the corresponding
+ i/o operation state after `byte_io_multiplexer::init_io_operation()` returns until the corresponding
`*_finished()` visitor function is called.
*/
struct io_operation_state
@@ -520,7 +576,7 @@ public:
friend struct lock_guard;
//! The i/o handle the i/o operation is upon
- io_handle *h{nullptr};
+ byte_io_handle *h{nullptr};
//! The state visitor supplied when the operation was initialised
io_operation_state_visitor *visitor{nullptr};
//! Used by the visitor to control the state lock
@@ -576,7 +632,7 @@ public:
protected:
constexpr io_operation_state() {}
- constexpr io_operation_state(io_handle *_h, io_operation_state_visitor *_visitor)
+ constexpr io_operation_state(byte_io_handle *_h, io_operation_state_visitor *_visitor)
: h(_h)
, visitor(_visitor)
{
@@ -731,14 +787,15 @@ protected:
//! Construct an unknown state
constexpr _unsynchronised_io_operation_state() {}
//! Construct a read operation state
- _unsynchronised_io_operation_state(io_handle *_h, io_operation_state_visitor *_v, registered_buffer_type &&b, deadline d, io_request<buffers_type> reqs)
+ _unsynchronised_io_operation_state(byte_io_handle *_h, io_operation_state_visitor *_v, registered_buffer_type &&b, deadline d,
+ io_request<buffers_type> reqs)
: io_operation_state(_h, _v)
, state(io_operation_state_type::read_initialised)
, payload(std::move(b), d, std::move(reqs))
{
}
//! Construct a write operation state
- _unsynchronised_io_operation_state(io_handle *_h, io_operation_state_visitor *_v, registered_buffer_type &&b, deadline d,
+ _unsynchronised_io_operation_state(byte_io_handle *_h, io_operation_state_visitor *_v, registered_buffer_type &&b, deadline d,
io_request<const_buffers_type> reqs)
: io_operation_state(_h, _v)
, state(io_operation_state_type::write_initialised)
@@ -746,7 +803,7 @@ protected:
{
}
//! Construct a barrier operation state
- _unsynchronised_io_operation_state(io_handle *_h, io_operation_state_visitor *_v, registered_buffer_type &&b, deadline d,
+ _unsynchronised_io_operation_state(byte_io_handle *_h, io_operation_state_visitor *_v, registered_buffer_type &&b, deadline d,
io_request<const_buffers_type> reqs, barrier_kind kind)
: io_operation_state(_h, _v)
, state(io_operation_state_type::barrier_initialised)
@@ -1117,11 +1174,11 @@ public:
If the i/o does not complete immediately, the coroutine is suspended. To cause resumption
of execution, you will need to pump the associated i/o multiplexer for completions using
- `io_multiplexer::check_for_any_completed_io()`.
+ `byte_io_multiplexer::check_for_any_completed_io()`.
*/
template <class T> struct awaitable final : protected io_operation_state_visitor
{
- friend class io_handle;
+ friend class byte_io_handle;
static constexpr size_t _state_storage_bytes = _awaitable_size - sizeof(void *) - sizeof(io_operation_state *)
#if LLFIO_ENABLE_COROUTINES
- sizeof(coroutine_handle<>)
@@ -1185,10 +1242,10 @@ public:
return *this;
}
//! Destructor, blocks if the i/o is in progress
- inline ~awaitable(); // defined in io_handle.hpp
+ inline ~awaitable(); // defined in byte_io_handle.hpp
//! True if the i/o state is finished. Begins the i/o if it is not initiated yet.
- inline bool await_ready() noexcept; // defined in io_handle.hpp
+ inline bool await_ready() noexcept; // defined in byte_io_handle.hpp
//! Returns the result of the i/o
result_type await_resume()
@@ -1301,23 +1358,51 @@ public:
for a read operation into the storage provided. The i/o is not initiated. The storage must
meet the requirements from `state_requirements()`.
*/
- virtual io_operation_state *construct(span<byte> storage, io_handle *_h, io_operation_state_visitor *_visitor, registered_buffer_type &&b, deadline d,
+ virtual io_operation_state *construct(span<byte> storage, byte_io_handle *_h, io_operation_state_visitor *_visitor, registered_buffer_type &&b, deadline d,
io_request<buffers_type> reqs) noexcept = 0;
/*! \brief Constructs either a `unsynchronised_io_operation_state` or a `synchronised_io_operation_state`
for a write operation into the storage provided. The i/o is not initiated. The storage must
meet the requirements from `state_requirements()`.
*/
- virtual io_operation_state *construct(span<byte> storage, io_handle *_h, io_operation_state_visitor *_visitor, registered_buffer_type &&b, deadline d,
+ virtual io_operation_state *construct(span<byte> storage, byte_io_handle *_h, io_operation_state_visitor *_visitor, registered_buffer_type &&b, deadline d,
io_request<const_buffers_type> reqs) noexcept = 0;
/*! \brief Constructs either a `unsynchronised_io_operation_state` or a `synchronised_io_operation_state`
for a barrier operation into the storage provided. The i/o is not initiated. The storage must
meet the requirements from `state_requirements()`.
*/
- virtual io_operation_state *construct(span<byte> storage, io_handle *_h, io_operation_state_visitor *_visitor, registered_buffer_type &&b, deadline d,
+ virtual io_operation_state *construct(span<byte> storage, byte_io_handle *_h, io_operation_state_visitor *_visitor, registered_buffer_type &&b, deadline d,
io_request<const_buffers_type> reqs, barrier_kind kind) noexcept = 0;
+ /*! \brief Constructs either a `unsynchronised_io_operation_state` or a `synchronised_io_operation_state`
+ for a `byte_socket_handle` read operation into the storage provided. The i/o is not initiated. The storage must
+ meet the requirements from `state_requirements()`.
+ */
+ virtual io_operation_state *construct(span<byte> storage, byte_socket_handle *_h, io_operation_state_visitor *_visitor, deadline d,
+ const ip::address & /*unused*/) noexcept
+ {
+ (void) storage;
+ (void) _h;
+ (void) _visitor;
+ (void) d;
+ return nullptr;
+ }
+
+ /*! \brief Constructs either a `unsynchronised_io_operation_state` or a `synchronised_io_operation_state`
+ for a `listening_socket_handle` read operation into the storage provided. The i/o is not initiated. The storage must
+ meet the requirements from `state_requirements()`.
+ */
+ virtual io_operation_state *construct(span<byte> storage, listening_socket_handle *_h, io_operation_state_visitor *_visitor, deadline d,
+ std::pair<byte_socket_handle, ip::address> & /*unused*/) noexcept
+ {
+ (void) storage;
+ (void) _h;
+ (void) _visitor;
+ (void) d;
+ return nullptr;
+ }
+
/*! \brief Initiates the i/o in a previously constructed state. Note that you should always call
`.flush_inited_io_operations()` after you finished initiating i/o. After this call returns,
you cannot relocate in memory `state` until `is_finished(state->current_state())` returns true.
@@ -1326,32 +1411,67 @@ public:
/*! \brief Combines `.construct()` with `.init_io_operation()` in a single call for improved efficiency.
*/
- virtual io_operation_state *construct_and_init_io_operation(span<byte> storage, io_handle *_h, io_operation_state_visitor *_visitor,
+ virtual io_operation_state *construct_and_init_io_operation(span<byte> storage, byte_io_handle *_h, io_operation_state_visitor *_visitor,
registered_buffer_type &&b, deadline d, io_request<buffers_type> reqs) noexcept
{
io_operation_state *state = construct(storage, _h, _visitor, std::move(b), d, std::move(reqs));
- init_io_operation(state);
+ if(state != nullptr)
+ {
+ init_io_operation(state);
+ }
return state;
}
/*! \brief Combines `.construct()` with `.init_io_operation()` in a single call for improved efficiency.
*/
- virtual io_operation_state *construct_and_init_io_operation(span<byte> storage, io_handle *_h, io_operation_state_visitor *_visitor,
+ virtual io_operation_state *construct_and_init_io_operation(span<byte> storage, byte_io_handle *_h, io_operation_state_visitor *_visitor,
registered_buffer_type &&b, deadline d, io_request<const_buffers_type> reqs) noexcept
{
io_operation_state *state = construct(storage, _h, _visitor, std::move(b), d, std::move(reqs));
- init_io_operation(state);
+ if(state != nullptr)
+ {
+ init_io_operation(state);
+ }
return state;
}
/*! \brief Combines `.construct()` with `.init_io_operation()` in a single call for improved efficiency.
*/
- virtual io_operation_state *construct_and_init_io_operation(span<byte> storage, io_handle *_h, io_operation_state_visitor *_visitor,
+ virtual io_operation_state *construct_and_init_io_operation(span<byte> storage, byte_io_handle *_h, io_operation_state_visitor *_visitor,
registered_buffer_type &&b, deadline d, io_request<const_buffers_type> reqs,
barrier_kind kind) noexcept
{
io_operation_state *state = construct(storage, _h, _visitor, std::move(b), d, std::move(reqs), kind);
- init_io_operation(state);
+ if(state != nullptr)
+ {
+ init_io_operation(state);
+ }
+ return state;
+ }
+
+ /*! \brief Combines `.construct()` with `.init_io_operation()` in a single call for improved efficiency.
+ */
+ virtual io_operation_state *construct_and_init_io_operation(span<byte> storage, byte_socket_handle *_h, io_operation_state_visitor *_visitor, deadline d,
+ const ip::address &addr) noexcept
+ {
+ io_operation_state *state = construct(storage, _h, _visitor, d, addr);
+ if(state != nullptr)
+ {
+ init_io_operation(state);
+ }
+ return state;
+ }
+
+ /*! \brief Combines `.construct()` with `.init_io_operation()` in a single call for improved efficiency.
+ */
+ virtual io_operation_state *construct_and_init_io_operation(span<byte> storage, listening_socket_handle *_h, io_operation_state_visitor *_visitor, deadline d,
+ std::pair<byte_socket_handle, ip::address> &req) noexcept
+ {
+ io_operation_state *state = construct(storage, _h, _visitor, d, req);
+ if(state != nullptr)
+ {
+ init_io_operation(state);
+ }
return state;
}
@@ -1386,11 +1506,11 @@ public:
virtual result<void> wake_check_for_any_completed_io() noexcept = 0;
};
//! A unique ptr to an i/o multiplexer implementation.
-using io_multiplexer_ptr = std::unique_ptr<io_multiplexer>;
+using byte_io_multiplexer_ptr = std::unique_ptr<byte_io_multiplexer>;
#ifndef NDEBUG
-static_assert(OUTCOME_V2_NAMESPACE::concepts::basic_result<io_multiplexer::io_result<int>>,
- "io_multiplexer::io_result<int> does not match the Outcome basic_result concept!");
+static_assert(OUTCOME_V2_NAMESPACE::concepts::basic_result<byte_io_multiplexer::io_result<int>>,
+ "byte_io_multiplexer::io_result<int> does not match the Outcome basic_result concept!");
#endif
#if LLFIO_ENABLE_TEST_IO_MULTIPLEXERS
@@ -1402,14 +1522,14 @@ namespace test
The multiplexer returned by this function is a null implementation
used by the test suite to benchmark performance.
*/
- LLFIO_HEADERS_ONLY_FUNC_SPEC result<io_multiplexer_ptr> multiplexer_null(size_t threads, bool disable_immediate_completions) noexcept;
+ LLFIO_HEADERS_ONLY_FUNC_SPEC result<byte_io_multiplexer_ptr> multiplexer_null(size_t threads, bool disable_immediate_completions) noexcept;
#if defined(__linux__) || DOXYGEN_IS_IN_THE_HOUSE
-// LLFIO_HEADERS_ONLY_FUNC_SPEC result<io_multiplexer_ptr> multiplexer_linux_epoll(size_t threads) noexcept;
- LLFIO_HEADERS_ONLY_FUNC_SPEC result<io_multiplexer_ptr> multiplexer_linux_io_uring(size_t threads, bool is_polling) noexcept;
+ // LLFIO_HEADERS_ONLY_FUNC_SPEC result<byte_io_multiplexer_ptr> multiplexer_linux_epoll(size_t threads) noexcept;
+ LLFIO_HEADERS_ONLY_FUNC_SPEC result<byte_io_multiplexer_ptr> multiplexer_linux_io_uring(size_t threads, bool is_polling) noexcept;
#endif
#if(defined(__FreeBSD__) || defined(__APPLE__)) || DOXYGEN_IS_IN_THE_HOUSE
-// LLFIO_HEADERS_ONLY_FUNC_SPEC result<io_multiplexer_ptr> multiplexer_bsd_kqueue(size_t threads) noexcept;
+// LLFIO_HEADERS_ONLY_FUNC_SPEC result<byte_io_multiplexer_ptr> multiplexer_bsd_kqueue(size_t threads) noexcept;
#endif
#if defined(_WIN32) || DOXYGEN_IS_IN_THE_HOUSE
/*! \brief Return a test i/o multiplexer implemented using Microsoft Windows IOCP.
@@ -1417,7 +1537,7 @@ namespace test
The multiplexer returned by this function is only a partial implementation, used
only by the test suite. In particular it does not fully implement deadlined i/o.
*/
- LLFIO_HEADERS_ONLY_FUNC_SPEC result<io_multiplexer_ptr> multiplexer_win_iocp(size_t threads, bool disable_immediate_completions) noexcept;
+ LLFIO_HEADERS_ONLY_FUNC_SPEC result<byte_io_multiplexer_ptr> multiplexer_win_iocp(size_t threads, bool disable_immediate_completions) noexcept;
#endif
} // namespace test
#endif
@@ -1426,9 +1546,9 @@ namespace test
namespace this_thread
{
//! \brief Return the calling thread's current i/o multiplexer.
- LLFIO_HEADERS_ONLY_FUNC_SPEC io_multiplexer *multiplexer() noexcept;
+ LLFIO_HEADERS_ONLY_FUNC_SPEC byte_io_multiplexer *multiplexer() noexcept;
//! \brief Set the calling thread's current i/o multiplexer.
- LLFIO_HEADERS_ONLY_FUNC_SPEC void set_multiplexer(io_multiplexer *ctx) noexcept;
+ LLFIO_HEADERS_ONLY_FUNC_SPEC void set_multiplexer(byte_io_multiplexer *ctx) noexcept;
} // namespace this_thread
// BEGIN make_free_functions.py
@@ -1442,7 +1562,7 @@ LLFIO_V2_NAMESPACE_END
#if LLFIO_HEADERS_ONLY == 1 && !defined(DOXYGEN_SHOULD_SKIP_THIS)
#define LLFIO_INCLUDED_BY_HEADER 1
-#include "detail/impl/io_multiplexer.ipp"
+#include "detail/impl/byte_io_multiplexer.ipp"
#undef LLFIO_INCLUDED_BY_HEADER
#endif
diff --git a/include/llfio/v2.0/byte_socket_handle.hpp b/include/llfio/v2.0/byte_socket_handle.hpp
new file mode 100644
index 00000000..136341fc
--- /dev/null
+++ b/include/llfio/v2.0/byte_socket_handle.hpp
@@ -0,0 +1,971 @@
+/* A handle to a byte-orientated socket
+(C) 2021-2021 Niall Douglas <http://www.nedproductions.biz/> (20 commits)
+File Created: Dec 2021
+
+
+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 LLFIO_BYTE_SOCKET_HANDLE_H
+#define LLFIO_BYTE_SOCKET_HANDLE_H
+
+#include "byte_io_handle.hpp"
+
+struct sockaddr;
+struct sockaddr_in;
+struct sockaddr_in6;
+
+//! \file byte_socket_handle.hpp Provides `byte_socket_handle`.
+
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning(disable : 4201) // nameless struct/union
+#pragma warning(disable : 4251) // dll interface
+#pragma warning(disable : 4275) // dll interface
+#endif
+
+LLFIO_V2_NAMESPACE_EXPORT_BEGIN
+
+class byte_socket_handle;
+class listening_socket_handle;
+namespace ip
+{
+ class address;
+ LLFIO_HEADERS_ONLY_FUNC_SPEC std::ostream &operator<<(std::ostream &s, const address &v);
+} // namespace ip
+
+namespace detail
+{
+#ifdef _WIN32
+ LLFIO_HEADERS_ONLY_FUNC_SPEC void register_socket_handle_instance(void *i) noexcept;
+ LLFIO_HEADERS_ONLY_FUNC_SPEC void unregister_socket_handle_instance(void *i) noexcept;
+#endif
+} // namespace detail
+
+//! Inspired by ASIO's `ip` namespace
+namespace ip
+{
+ class address_v4;
+ class address_v6;
+
+ //! The family of IP
+ enum class family
+ {
+ unknown,
+ v4, //!< IP version 4
+ v6, //!< IP version 6
+ any //!< Either v4 or v6
+ };
+ /*! \class address
+ \brief A version independent IP address.
+
+ This is inspired by `asio::ip::address`, but it also adds `port()` from `asio::ip::endpoint`
+ and a few other observer member functions i.e. it fuses ASIO's many types into one.
+
+ The reason why is that this type is a simple wrap of `struct sockaddr_in` or `struct sockaddr_in6`,
+ it doesn't split those structures into multiple C++ types.
+ */
+ class LLFIO_DECL address
+ {
+ friend class LLFIO_V2_NAMESPACE::byte_socket_handle;
+ friend class LLFIO_V2_NAMESPACE::listening_socket_handle;
+ friend LLFIO_HEADERS_ONLY_MEMFUNC_SPEC std::ostream &operator<<(std::ostream &s, const address &v);
+
+ protected:
+ union
+ {
+ struct
+ {
+ unsigned short _family; // sa_family_t
+ uint16_t _port; // in_port_t
+ union
+ {
+ struct
+ {
+ uint32_t _flowinfo;
+ byte _addr[16];
+ uint32_t _scope_id;
+ } ipv6;
+ union
+ {
+ byte _addr[4];
+ uint32_t _addr_be;
+ } ipv4;
+ };
+ };
+ byte _storage[32]; // struct sockaddr_?
+ };
+
+ public:
+ constexpr address() noexcept
+ : _storage{to_byte(0), to_byte(0), to_byte(0), to_byte(0), to_byte(0), to_byte(0), to_byte(0), to_byte(0), to_byte(0), to_byte(0), to_byte(0),
+ to_byte(0), to_byte(0), to_byte(0), to_byte(0), to_byte(0), to_byte(0), to_byte(0), to_byte(0), to_byte(0), to_byte(0), to_byte(0),
+ to_byte(0), to_byte(0), to_byte(0), to_byte(0), to_byte(0), to_byte(0), to_byte(0), to_byte(0), to_byte(0), to_byte(0)}
+ {
+ }
+ LLFIO_HEADERS_ONLY_MEMFUNC_SPEC explicit address(const sockaddr_in &storage) noexcept;
+ LLFIO_HEADERS_ONLY_MEMFUNC_SPEC explicit address(const sockaddr_in6 &storage) noexcept;
+ address(const address &) = default;
+ address(address &&) = default;
+ address &operator=(const address &) = default;
+ address &operator=(address &&) = default;
+ ~address() = default;
+
+ //! True if addresses are equal
+ bool operator==(const address &o) const noexcept { return 0 == memcmp(_storage, o._storage, sizeof(_storage)); }
+ //! True if addresses are not equal
+ bool operator!=(const address &o) const noexcept { return 0 != memcmp(_storage, o._storage, sizeof(_storage)); }
+ //! True if address is less than
+ bool operator<(const address &o) const noexcept { return memcmp(_storage, o._storage, sizeof(_storage)) < 0; }
+
+ //! True if address is loopback
+ LLFIO_HEADERS_ONLY_MEMFUNC_SPEC bool is_loopback() const noexcept;
+ //! True if address is multicast
+ LLFIO_HEADERS_ONLY_MEMFUNC_SPEC bool is_multicast() const noexcept;
+ //! True if address is any
+ LLFIO_HEADERS_ONLY_MEMFUNC_SPEC bool is_any() const noexcept;
+ //! True if address is default constructed
+ bool is_default() const noexcept { return _family == 0; }
+ //! True if address is v4
+ LLFIO_HEADERS_ONLY_MEMFUNC_SPEC bool is_v4() const noexcept;
+ //! True if address is v6
+ LLFIO_HEADERS_ONLY_MEMFUNC_SPEC bool is_v6() const noexcept;
+
+ //! Returns the raw family of the address
+ unsigned short raw_family() const noexcept { return _family; }
+ //! Returns the family of the addres
+ LLFIO_HEADERS_ONLY_MEMFUNC_SPEC enum family family() const noexcept;
+ //! Returns the port of the address
+ uint16_t port() const noexcept { return _port; }
+ //! Returns the IPv6 flow info, if address is v6.
+ uint32_t flowinfo() const noexcept { return is_v6() ? ipv6._flowinfo : 0; }
+ //! Returns the IPv6 scope id, if address is v6.
+ uint32_t scope_id() const noexcept { return is_v6() ? ipv6._scope_id : 0; }
+
+ //! Returns the bytes of the address in network order
+ span<const byte> to_bytes() const noexcept { return is_v6() ? span<const byte>(ipv6._addr) : span<const byte>(ipv4._addr); }
+ //! Returns the address as a `sockaddr *`.
+ const sockaddr *to_sockaddr() const noexcept { return (const sockaddr *) _storage; }
+ //! Returns the size of the `sockaddr`
+ LLFIO_HEADERS_ONLY_MEMFUNC_SPEC int sockaddrlen() const noexcept;
+ };
+ //! Write address to stream
+ LLFIO_HEADERS_ONLY_FUNC_SPEC std::ostream &operator<<(std::ostream &s, const address &v);
+
+ class resolver;
+ namespace detail
+ {
+ struct LLFIO_DECL resolver_deleter
+ {
+ void operator()(resolver *p) const;
+ };
+ } // namespace detail
+ //! Returned by `resolve()` as a handle to the asynchronous name resolution operation.
+ class LLFIO_DECL resolver
+ {
+ public:
+ //! Returns true if the deadline expired, and the returned list of addresses is incomplete. Until `get()` is called, always is true.
+ LLFIO_HEADERS_ONLY_MEMFUNC_SPEC bool incomplete() const noexcept;
+ //! Returns the array of addresses, blocking until completion if necessary, returning any error if one occurred.
+ LLFIO_HEADERS_ONLY_MEMFUNC_SPEC result<span<address>> get() const noexcept;
+ //! Wait for up the deadline for the array of addresses to be retrieved.
+ LLFIO_HEADERS_ONLY_MEMFUNC_SPEC result<void> wait(deadline d = {}) const noexcept;
+ //! \overload
+ template <class Rep, class Period> result<bool> wait_for(const std::chrono::duration<Rep, Period> &duration) const noexcept
+ {
+ auto r = wait(duration);
+ if(!r && r.error() == errc::timed_out)
+ {
+ return false;
+ }
+ OUTCOME_TRY(std::move(r));
+ return true;
+ }
+ //! \overload
+ template <class Clock, class Duration> result<bool> wait_until(const std::chrono::time_point<Clock, Duration> &timeout) const noexcept
+ {
+ auto r = wait(timeout);
+ if(!r && r.error() == errc::timed_out)
+ {
+ return false;
+ }
+ OUTCOME_TRY(std::move(r));
+ return true;
+ }
+ };
+ //! A pointer to a resolver
+ using resolver_ptr = std::unique_ptr<resolver, detail::resolver_deleter>;
+
+ /*! \brief Retrieve a list of potential `address` for a given name and service e.g.
+ "www.google.com" and "https" optionally within a bounded deadline.
+
+ The object returned by this function can take many seconds to become ready as multiple network requests may
+ need to be made. The deadline can be used to bound execution times -- like in a few
+ other places in LLFIO, this deadline does not cause timed out errors, rather it aborts
+ any remaining name resolution after the deadline expires and returns whatever addresses
+ have been resolved by the deadline.
+
+ This function has a future-like API as several major platforms provide native asynchronous
+ name resolution (currently: Linux, Windows). On other platforms, `std::async` with
+ `getaddrinfo()` is used as an emulation, and therefore deadline expiry means no partial
+ list of addresses are returned.
+
+ If you become no longer interested in the results, simply reset or delete the pointer
+ and the resolution will be aborted asynchronously.
+
+ \mallocs This is one of those very few APIs in LLFIO where dynamic memory allocation
+ is unbounded and uncontrollable thanks to how the platform APIs are implemented.
+ */
+ LLFIO_HEADERS_ONLY_FUNC_SPEC result<resolver_ptr> resolve(string_view name, string_view service, family _family = family::any, deadline d = {},
+ bool blocking = false) noexcept;
+
+ //! Make an `address_v4`
+ LLFIO_HEADERS_ONLY_FUNC_SPEC result<address_v4> make_address_v4(string_view str) noexcept;
+ /*! \class address_v4
+ \brief A v4 IP address.
+ */
+ class LLFIO_DECL address_v4 : public address
+ {
+ friend result<address_v4> make_address_v4(string_view str) noexcept;
+
+ public:
+#if QUICKCPPLIB_USE_STD_SPAN
+ using bytes_type = span<const byte, 4>;
+#else
+ using bytes_type = span<const byte>;
+#endif
+ using uint_type = uint32_t;
+ constexpr address_v4() noexcept {}
+ LLFIO_HEADERS_ONLY_MEMFUNC_SPEC explicit address_v4(const bytes_type &bytes, uint16_t port = 0) noexcept;
+ LLFIO_HEADERS_ONLY_MEMFUNC_SPEC explicit address_v4(uint_type addr, uint16_t port = 0) noexcept;
+ address_v4(const address_v4 &) = default;
+ address_v4(address_v4 &&) = default;
+ address_v4 &operator=(const address_v4 &) = default;
+ address_v4 &operator=(address_v4 &&) = default;
+ ~address_v4() = default;
+
+ LLFIO_HEADERS_ONLY_MEMFUNC_SPEC bytes_type to_bytes() const noexcept;
+ LLFIO_HEADERS_ONLY_MEMFUNC_SPEC uint_type to_uint() const noexcept;
+
+ static LLFIO_HEADERS_ONLY_MEMFUNC_SPEC address_v4 any() noexcept;
+ static LLFIO_HEADERS_ONLY_MEMFUNC_SPEC address_v4 loopback() noexcept;
+ };
+ static_assert(std::is_trivially_copyable<address>::value, "ip::address is not trivially copyable!");
+ //! Make an `address_v4`
+ inline result<address_v4> make_address_v4(const address_v4::bytes_type &bytes, uint16_t port = 0) noexcept { return address_v4(bytes, port); }
+ //! Make an `address_v4`
+ inline result<address_v4> make_address_v4(const address_v4::uint_type &bytes, uint16_t port = 0) noexcept { return address_v4(bytes, port); }
+
+ //! Make an `address_v6`
+ LLFIO_HEADERS_ONLY_FUNC_SPEC result<address_v6> make_address_v6(string_view str) noexcept;
+ /*! \class address_v6
+ \brief A v6 IP address.
+ */
+ //! Make an `address_v6`. v6 addresses need to have the form `[::]:port`.
+ class LLFIO_DECL address_v6 : public address
+ {
+ friend result<address_v6> make_address_v6(string_view str) noexcept;
+
+ public:
+#if QUICKCPPLIB_USE_STD_SPAN
+ using bytes_type = span<const byte, 16>;
+#else
+ using bytes_type = span<const byte>;
+#endif
+
+ constexpr address_v6() noexcept {}
+ LLFIO_HEADERS_ONLY_MEMFUNC_SPEC explicit address_v6(const bytes_type &bytes, uint16_t port = 0, uint32_t scope_id = 0) noexcept;
+ address_v6(const address_v6 &) = default;
+ address_v6(address_v6 &&) = default;
+ address_v6 &operator=(const address_v6 &) = default;
+ address_v6 &operator=(address_v6 &&) = default;
+ ~address_v6() = default;
+
+ LLFIO_HEADERS_ONLY_MEMFUNC_SPEC bytes_type to_bytes() const noexcept;
+
+ static LLFIO_HEADERS_ONLY_MEMFUNC_SPEC address_v6 any() noexcept;
+ static LLFIO_HEADERS_ONLY_MEMFUNC_SPEC address_v6 loopback() noexcept;
+ };
+ //! Make an `address_v6`
+ inline result<address_v6> make_address_v6(const address_v6::bytes_type &bytes, uint16_t port = 0, uint32_t scope_id = 0) noexcept
+ {
+ return address_v6(bytes, port, scope_id);
+ }
+ //! Make a v4 or v6 `address`. v6 addresses need to have the form `[::]:port`.
+ inline result<address> make_address(string_view str) noexcept
+ {
+ if(str.size() > 0 && str[0] == '[')
+ {
+ OUTCOME_TRY(auto &&addr, make_address_v6(str));
+ return addr;
+ }
+ OUTCOME_TRY(auto &&addr, make_address_v4(str));
+ return addr;
+ }
+} // namespace ip
+
+
+/*! \class byte_socket_handle
+\brief A handle to a byte-orientated socket-like entity.
+
+This handle, or subclasses thereof, may refer to:
+
+- a BSD socket in the kernel configured for TCP.
+- a TLS socket in a userspace library.
+- a userspace socket for certain types of high end network card.
+- or indeed, anything which quacks like a `SOCK_STREAM` socket.
+
+If you construct it directly and assign it a socket that you created,
+then it refers to a kernel BSD socket, as the default implementation
+is for a kernel BSD socket. If you get an instance from elsewhere,
+it may have a *very* different implementation.
+
+The default is blocking sockets, on which timed out i/o is not possible.
+In this use case, `byte_socket()` will block until a successful
+connection is established with the remote address. Thereafter `read()`
+and `write()` block based on i/o from the other side, returning
+immediately if at least one byte is transferred.
+
+If `flag::multiplexable` is specified which causes the handle to
+be created as `native_handle_type::disposition::nonblocking`, `byte_socket()`
+no longer blocks. However it will then block in `read()` or `write()`,
+unless its deadline is zero.
+
+If you want to create a socket which awaits connections, you need
+to instance a `listening_socket_handle`. Reads from that handle yield
+new `byte_socket_handle` instances.
+
+### `caching::safety_barriers`
+
+TCP connections need to be closed by both parties in a specific way
+to not cause the tail of data sent to get truncated:
+
+1. Local side calls `shutdown(SHUT_WR)` to send the FIN packet.
+2. Remote side calls `shutdown(SHUT_WR)` to send the FIN packet.
+3. Local side `read()` returns no bytes read as remote side has closed
+down. Local side can now call `close()`.
+4. Remote side `read()` returns no bytes read as local side has closed
+down. Remote side can now call `close()`.
+
+This is obviously inefficient and prone to issues if the remote side
+is not a good faith actor, so most TCP based protocols such as HTTP
+send the length of the data to be transferred, and one loops reading
+until that length of data is read, whereupon the TCP connection is
+immediately forced closed without the TCP shutdown ceremony.
+
+The default caching is when `close()` is called it immediately
+closes the socket handle, causing an abort in the connection if any
+data remains in buffers. If you wish `close()` to instead issue
+a shutdown and to then block on `read()` until it returns no bytes
+before closing, set `caching::safety_barriers`.
+
+This *should* avoid the need for `SO_LINGER` for remote sides
+acting in good faith. If you don't control remote side code quality,
+you may still need to set `SO_LINGER`, though be aware that that
+socket option is full of gotchas.
+
+If you don't wish to have this operation occur during close, you
+can call `shutdown_and_close()` manually instead.
+*/
+class LLFIO_DECL byte_socket_handle : public byte_io_handle, public pollable_handle
+{
+ LLFIO_HEADERS_ONLY_VIRTUAL_SPEC const handle &_get_handle() const noexcept final { return *this; }
+
+public:
+ using path_type = byte_io_handle::path_type;
+ using extent_type = byte_io_handle::extent_type;
+ using size_type = byte_io_handle::size_type;
+ using mode = byte_io_handle::mode;
+ using creation = byte_io_handle::creation;
+ using caching = byte_io_handle::caching;
+ using flag = byte_io_handle::flag;
+ using buffer_type = byte_io_handle::buffer_type;
+ using const_buffer_type = byte_io_handle::const_buffer_type;
+ using buffers_type = byte_io_handle::buffers_type;
+ using const_buffers_type = byte_io_handle::const_buffers_type;
+ template <class T> using io_request = byte_io_handle::io_request<T>;
+ template <class T> using io_result = byte_io_handle::io_result<T>;
+
+protected:
+ LLFIO_HEADERS_ONLY_VIRTUAL_SPEC result<void> _do_connect(const ip::address &addr, deadline d) noexcept;
+
+ result<void> _do_multiplexer_connect(const ip::address &addr, deadline d) noexcept
+ {
+ LLFIO_DEADLINE_TO_SLEEP_INIT(d);
+ const auto state_reqs = _ctx->io_state_requirements();
+ auto *storage = (byte *) alloca(state_reqs.first + state_reqs.second);
+ const auto diff = (uintptr_t) storage & (state_reqs.second - 1);
+ storage += state_reqs.second - diff;
+ auto *state = _ctx->construct_and_init_io_operation({storage, state_reqs.first}, this, nullptr, d, addr);
+ if(state == nullptr)
+ {
+ return errc::resource_unavailable_try_again;
+ }
+ OUTCOME_TRY(_ctx->flush_inited_io_operations());
+ while(!is_finished(_ctx->check_io_operation(state)))
+ {
+ deadline nd;
+ LLFIO_DEADLINE_TO_PARTIAL_DEADLINE(nd, d);
+ OUTCOME_TRY(_ctx->check_for_any_completed_io(nd));
+ }
+ OUTCOME_TRY(std::move(*state).get_completed_read());
+ state->~io_operation_state();
+ return success();
+ }
+
+public:
+ //! Default constructor
+ constexpr byte_socket_handle() {} // NOLINT
+ //! Construct a handle from a supplied native handle
+ constexpr byte_socket_handle(native_handle_type h, caching caching, flag flags, byte_io_multiplexer *ctx)
+ : byte_io_handle(std::move(h), caching, flags, ctx)
+ {
+#ifdef _WIN32
+ if(_v)
+ {
+ detail::register_socket_handle_instance(this);
+ }
+#endif
+ }
+ //! No copy construction (use clone())
+ byte_socket_handle(const byte_socket_handle &) = delete;
+ //! No copy assignment
+ byte_socket_handle &operator=(const byte_socket_handle &) = delete;
+ //! Implicit move construction of byte_socket_handle permitted
+ constexpr byte_socket_handle(byte_socket_handle &&o) noexcept
+ : byte_io_handle(std::move(o))
+ {
+#ifdef _WIN32
+ if(_v)
+ {
+ detail::register_socket_handle_instance(this);
+ detail::unregister_socket_handle_instance(&o);
+ }
+#endif
+ }
+ //! Explicit conversion from handle permitted
+ explicit constexpr byte_socket_handle(handle &&o, byte_io_multiplexer *ctx) noexcept
+ : byte_io_handle(std::move(o), ctx)
+ {
+#ifdef _WIN32
+ if(_v)
+ {
+ detail::register_socket_handle_instance(this);
+ }
+#endif
+ }
+ //! Explicit conversion from byte_io_handle permitted
+ explicit constexpr byte_socket_handle(byte_io_handle &&o) noexcept
+ : byte_io_handle(std::move(o))
+ {
+#ifdef _WIN32
+ if(_v)
+ {
+ detail::register_socket_handle_instance(this);
+ }
+#endif
+ }
+ //! Move assignment of byte_socket_handle permitted
+ byte_socket_handle &operator=(byte_socket_handle &&o) noexcept
+ {
+ if(this == &o)
+ {
+ return *this;
+ }
+#ifdef _WIN32
+ if(_v)
+ {
+ detail::unregister_socket_handle_instance(this);
+ }
+#endif
+ this->~byte_socket_handle();
+ new(this) byte_socket_handle(std::move(o));
+ return *this;
+ }
+ //! Swap with another instance
+ LLFIO_MAKE_FREE_FUNCTION
+ void swap(byte_socket_handle &o) noexcept
+ {
+ byte_socket_handle temp(std::move(*this));
+ *this = std::move(o);
+ o = std::move(temp);
+ }
+
+ //! Returns the local endpoint of this socket instance
+ LLFIO_HEADERS_ONLY_VIRTUAL_SPEC result<ip::address> local_endpoint() const noexcept;
+ //! Returns the remote endpoint of this socket instance
+ LLFIO_HEADERS_ONLY_VIRTUAL_SPEC result<ip::address> remote_endpoint() const noexcept;
+
+ //! The channels which can be shut down.
+ enum shutdown_kind
+ {
+ shutdown_read, //!< Shutdown further reads.
+ shutdown_write, //!< Shutdown further writes.
+ shutdown_both //!< Shutdown both further reads and writes.
+ };
+ /*! \brief Initiates shutting down further communication on the socket.
+
+ The default is `shutdown_write`, as generally if shutting down you want send
+ a FIN packet to remote and loop polling reads until you receive a FIN from
+ remote.
+ */
+ LLFIO_HEADERS_ONLY_VIRTUAL_SPEC result<void> shutdown(shutdown_kind = shutdown_write) noexcept;
+
+ /*! \brief Connects to an address.
+ \param addr The address to connect to.
+ \param d How long to wait for a connection.
+
+ The connection begins upon first call, if it times out then you can call this function again with a
+ new timeout to poll the socket for when it connects. Eventually this function will either succeed,
+ or fail with an error if the connection failed.
+
+ \errors Any of the values `connect()` can return;
+ */
+ result<void> connect(const ip::address &addr, deadline d = {}) noexcept
+ {
+ return (_ctx == nullptr) ? _do_connect(addr, d) : _do_multiplexer_connect(addr, d);
+ }
+
+ /*! Create a socket handle.
+ \param family Which IP family to create the socket in.
+ \param _mode How to open the socket. If this is `mode::append`, the read side of the socket
+ is shutdown; if this is `mode::read`, the write side of the socket is shutdown.
+ \param _caching How to ask the kernel to cache the socket. If writes are not cached,
+ `SO_SNDBUF` to the minimum possible value and `TCP_NODELAY` is set, this should cause
+ writes to hit the network as quickly as possible.
+ \param flags Any additional custom behaviours.
+
+ \errors Any of the values POSIX `socket()` or `WSASocket()` can return.
+ */
+ LLFIO_MAKE_FREE_FUNCTION
+ static LLFIO_HEADERS_ONLY_MEMFUNC_SPEC result<byte_socket_handle> byte_socket(ip::family family, mode _mode = mode::write, caching _caching = caching::all,
+ flag flags = flag::none) noexcept;
+
+ LLFIO_HEADERS_ONLY_VIRTUAL_SPEC ~byte_socket_handle() override
+ {
+ if(_v)
+ {
+ auto r = byte_socket_handle::close();
+ if(!r)
+ {
+ // std::cout << r.error().message() << std::endl;
+ LLFIO_LOG_FATAL(_v.fd, "byte_socket_handle::~byte_socket_handle() close failed");
+ abort();
+ }
+ }
+ }
+ LLFIO_HEADERS_ONLY_VIRTUAL_SPEC result<void> close() noexcept override;
+
+ /*! \brief Convenience function to shut down the outbound connection and wait for the other side to shut down our
+ inbound connection by throwing away any bytes read, then closing the socket.
+ */
+ result<void> shutdown_and_close(deadline d = {}) noexcept
+ {
+ LLFIO_DEADLINE_TO_SLEEP_INIT(d);
+ OUTCOME_TRY(shutdown());
+ byte buffer[4096];
+ for(;;)
+ {
+ deadline nd;
+ LLFIO_DEADLINE_TO_PARTIAL_DEADLINE(nd, d);
+ OUTCOME_TRY(auto readed, read(0, {{buffer}}, nd));
+ if(readed == 0)
+ {
+ break;
+ }
+ }
+ return close();
+ }
+};
+
+//! \brief Constructor for `byte_socket_handle`
+template <> struct construct<byte_socket_handle>
+{
+ ip::family family;
+ byte_socket_handle::mode _mode = byte_socket_handle::mode::write;
+ byte_socket_handle::caching _caching = byte_socket_handle::caching::all;
+ byte_socket_handle::flag flags = byte_socket_handle::flag::none;
+ result<byte_socket_handle> operator()() const noexcept { return byte_socket_handle::byte_socket(family, _mode, _caching, flags); }
+};
+
+/* \class listening_socket_handle
+\brief A handle to a socket-like entity able to receive incoming connections.
+*/
+class LLFIO_DECL listening_socket_handle : public handle, public pollable_handle
+{
+ LLFIO_HEADERS_ONLY_VIRTUAL_SPEC const handle &_get_handle() const noexcept final { return *this; }
+
+protected:
+ byte_io_multiplexer *_ctx{nullptr}; // +4 or +8 bytes
+public:
+ //! The buffer type used by this handle, which is a pair of `byte_socket_handle` and `ip::address`
+ using buffer_type = std::pair<byte_socket_handle, ip::address>;
+ //! The const buffer type used by this handle, which is a pair of `byte_socket_handle` and `ip::address`
+ using const_buffer_type = std::pair<byte_socket_handle, ip::address>;
+ //! The buffers type used by this handle for reads, which is a single item sequence of `buffer_type`.
+ struct buffers_type
+ {
+ //! Type of the pointer to the buffer.
+ using pointer = std::pair<byte_socket_handle, ip::address> *;
+ //! Type of the iterator to the buffer.
+ using iterator = std::pair<byte_socket_handle, ip::address> *;
+ //! Type of the iterator to the buffer.
+ using const_iterator = const std::pair<byte_socket_handle, ip::address> *;
+ //! Type of the length of the buffers.
+ using size_type = size_t;
+
+ //! Default constructor
+ constexpr buffers_type() {} // NOLINT
+
+ /*! Constructor
+ */
+ constexpr buffers_type(std::pair<byte_socket_handle, ip::address> &sock)
+ : _sock(&sock)
+ {
+ }
+ ~buffers_type() = default;
+ //! Move constructor
+ buffers_type(buffers_type &&o) noexcept
+ : _sock(o._sock)
+ {
+ o._sock = nullptr;
+ }
+ //! No copy construction
+ buffers_type(const buffers_type &) = delete;
+ //! Move assignment
+ buffers_type &operator=(buffers_type &&o) noexcept
+ {
+ if(this == &o)
+ {
+ return *this;
+ }
+ this->~buffers_type();
+ new(this) buffers_type(std::move(o));
+ return *this;
+ }
+ //! No copy assignment
+ buffers_type &operator=(const buffers_type &) = delete;
+
+ //! True if empty
+ LLFIO_NODISCARD constexpr bool empty() const noexcept { return _sock == nullptr; }
+ //! Returns an iterator to the beginning of the buffers
+ constexpr iterator begin() noexcept { return _sock; }
+ //! Returns an iterator to the beginning of the buffers
+ constexpr const_iterator begin() const noexcept { return _sock; }
+ //! Returns an iterator to the beginning of the buffers
+ constexpr const_iterator cbegin() const noexcept { return _sock; }
+ //! Returns an iterator to after the end of the buffers
+ constexpr iterator end() noexcept { return _sock + 1; }
+ //! Returns an iterator to after the end of the buffers
+ constexpr const_iterator end() const noexcept { return _sock + 1; }
+ //! Returns an iterator to after the end of the buffers
+ constexpr const_iterator cend() const noexcept { return _sock + 1; }
+
+ //! The socket referenced by the buffers
+ const buffer_type &connected_socket() const &noexcept
+ {
+ assert(_sock != nullptr);
+ return *_sock;
+ }
+ //! The socket referenced by the buffers
+ buffer_type &connected_socket() &noexcept
+ {
+ assert(_sock != nullptr);
+ return *_sock;
+ }
+ //! The socket and its connected address referenced by the buffers
+ buffer_type connected_socket() &&noexcept
+ {
+ assert(_sock != nullptr);
+ return std::move(*_sock);
+ }
+
+ private:
+ std::pair<byte_socket_handle, ip::address> *_sock{nullptr};
+ };
+ //! The const buffers type used by this handle for reads, which is a single item sequence of `buffer_type`.
+ using const_buffers_type = buffers_type;
+ //! The i/o request type used by this handle.
+ template <class /*unused*/> struct io_request
+ {
+ buffers_type buffers{};
+
+ /*! Construct a request to listen for new socket connections.
+
+ \param _buffers The buffers to fill with connected sockets.
+ */
+ constexpr io_request(buffers_type _buffers)
+ : buffers(std::move(_buffers))
+ {
+ }
+ };
+ template <class T> using io_result = result<T>;
+
+protected:
+ LLFIO_HEADERS_ONLY_VIRTUAL_SPEC result<buffers_type> _do_read(io_request<buffers_type> req, deadline d) noexcept;
+
+ io_result<buffers_type> _do_multiplexer_read(io_request<buffers_type> reqs, deadline d) noexcept
+ {
+ LLFIO_DEADLINE_TO_SLEEP_INIT(d);
+ const auto state_reqs = _ctx->io_state_requirements();
+ auto *storage = (byte *) alloca(state_reqs.first + state_reqs.second);
+ const auto diff = (uintptr_t) storage & (state_reqs.second - 1);
+ storage += state_reqs.second - diff;
+ auto *state = _ctx->construct_and_init_io_operation({storage, state_reqs.first}, this, nullptr, d, reqs.buffers.connected_socket());
+ if(state == nullptr)
+ {
+ return errc::resource_unavailable_try_again;
+ }
+ OUTCOME_TRY(_ctx->flush_inited_io_operations());
+ while(!is_finished(_ctx->check_io_operation(state)))
+ {
+ deadline nd;
+ LLFIO_DEADLINE_TO_PARTIAL_DEADLINE(nd, d);
+ OUTCOME_TRY(_ctx->check_for_any_completed_io(nd));
+ }
+ OUTCOME_TRY(std::move(*state).get_completed_read());
+ state->~io_operation_state();
+ return {std::move(reqs.buffers)};
+ }
+
+public:
+ //! Default constructor
+ constexpr listening_socket_handle() {} // NOLINT
+ //! Construct a handle from a supplied native handle
+ constexpr listening_socket_handle(native_handle_type h, caching caching, flag flags, byte_io_multiplexer *ctx)
+ : handle(std::move(h), caching, flags)
+ , _ctx(ctx)
+ {
+#ifdef _WIN32
+ if(_v)
+ {
+ detail::register_socket_handle_instance(this);
+ }
+#endif
+ }
+ //! No copy construction (use clone())
+ listening_socket_handle(const listening_socket_handle &) = delete;
+ //! No copy assignment
+ listening_socket_handle &operator=(const listening_socket_handle &) = delete;
+ //! Implicit move construction of byte_socket_handle permitted
+ constexpr listening_socket_handle(listening_socket_handle &&o) noexcept
+ : handle(std::move(o))
+ , _ctx(o._ctx)
+ {
+#ifdef _WIN32
+ if(_v)
+ {
+ detail::register_socket_handle_instance(this);
+ detail::unregister_socket_handle_instance(&o);
+ }
+#endif
+ }
+ //! Explicit conversion from handle permitted
+ explicit constexpr listening_socket_handle(handle &&o, byte_io_multiplexer *ctx) noexcept
+ : handle(std::move(o))
+ , _ctx(ctx)
+ {
+#ifdef _WIN32
+ if(_v)
+ {
+ detail::register_socket_handle_instance(this);
+ }
+#endif
+ }
+ //! Move assignment of listening_socket_handle permitted
+ listening_socket_handle &operator=(listening_socket_handle &&o) noexcept
+ {
+ if(this == &o)
+ {
+ return *this;
+ }
+#ifdef _WIN32
+ if(_v)
+ {
+ detail::unregister_socket_handle_instance(this);
+ }
+#endif
+ this->~listening_socket_handle();
+ new(this) listening_socket_handle(std::move(o));
+ return *this;
+ }
+ //! Swap with another instance
+ LLFIO_MAKE_FREE_FUNCTION
+ void swap(listening_socket_handle &o) noexcept
+ {
+ listening_socket_handle temp(std::move(*this));
+ *this = std::move(o);
+ o = std::move(temp);
+ }
+
+ /*! \brief The i/o multiplexer this handle will use to multiplex i/o. If this returns null,
+ then this handle has not been registered with an i/o multiplexer yet.
+ */
+ byte_io_multiplexer *multiplexer() const noexcept { return _ctx; }
+
+ /*! \brief Sets the i/o multiplexer this handle will use to implement `read()`, `write()` and `barrier()`.
+
+ Note that this call deregisters this handle from any existing i/o multiplexer, and registers
+ it with the new i/o multiplexer. You must therefore not call it if any i/o is currently
+ outstanding on this handle. You should also be aware that multiple dynamic memory
+ allocations and deallocations may occur, as well as multiple syscalls (i.e. this is
+ an expensive call, try to do it from cold code).
+
+ If the handle was not created as multiplexable, this call always fails.
+
+ \mallocs Multiple dynamic memory allocations and deallocations.
+ */
+ virtual result<void> set_multiplexer(byte_io_multiplexer *c = this_thread::multiplexer()) noexcept; // implementation is below
+
+ //! Returns the local endpoint of this socket instance
+ LLFIO_HEADERS_ONLY_VIRTUAL_SPEC result<ip::address> local_endpoint() const noexcept;
+
+ /*! \brief Binds a socket to a local endpoint and sets the socket to listen for new connections.
+ \param addr The local endpoint to which to bind the socket.
+ \param _creation Whether to apply `SO_REUSEADDR` before binding.
+ \param backlog The maximum queue length of pending connections. `-1` chooses `SOMAXCONN`.
+
+ You should set any socket options etc that you need on `native_handle()` before binding
+ the socket to its local endpoint.
+
+ \errors Any of the values `bind()` and `listen()` can return.
+ */
+ LLFIO_HEADERS_ONLY_VIRTUAL_SPEC result<void> bind(const ip::address &addr, creation _creation = creation::only_if_not_exist, int backlog = -1) noexcept;
+
+ /*! Create a listening socket handle.
+ \param family Which IP family to create the socket in.
+ \param _mode How to open the socket. If this is `mode::append`, the read side of the socket
+ is shutdown; if this is `mode::read`, the write side of the socket is shutdown.
+ \param _caching How to ask the kernel to cache the socket. If writes are not cached,
+ `SO_SNDBUF` to the minimum possible value and `TCP_NODELAY` is set, this should cause
+ writes to hit the network as quickly as possible.
+ \param flags Any additional custom behaviours.
+
+ \errors Any of the values POSIX `socket()` or `WSASocket()` can return.
+ */
+ LLFIO_MAKE_FREE_FUNCTION
+ static LLFIO_HEADERS_ONLY_MEMFUNC_SPEC result<listening_socket_handle> listening_socket(ip::family _family, mode _mode = mode::write,
+ caching _caching = caching::all, flag flags = flag::none) noexcept;
+
+ LLFIO_HEADERS_ONLY_VIRTUAL_SPEC ~listening_socket_handle() override
+ {
+ if(_v)
+ {
+ (void) listening_socket_handle::close();
+ }
+ }
+ LLFIO_HEADERS_ONLY_VIRTUAL_SPEC result<void> close() noexcept override
+ {
+ LLFIO_LOG_FUNCTION_CALL(this);
+ if(_ctx != nullptr)
+ {
+ OUTCOME_TRY(set_multiplexer(nullptr));
+ }
+#ifndef NDEBUG
+ if(_v)
+ {
+ // Tell handle::close() that we have correctly executed
+ _v.behaviour |= native_handle_type::disposition::_child_close_executed;
+ }
+#endif
+ auto ret = handle::close();
+#ifdef _WIN32
+ if(_v)
+ {
+ detail::unregister_socket_handle_instance(this);
+ }
+#endif
+ return ret;
+ }
+
+ /*! Read the contents of the listening socket for newly connected byte sockets.
+
+ \return Returns the buffers filled, with its socket handle and address set to the newly connected socket.
+ \param req A buffer to fill with a newly connected socket.
+ \param d An optional deadline by which to time out.
+
+ \errors Any of the errors which `accept()` or `WSAAccept()` might return.
+ */
+ LLFIO_MAKE_FREE_FUNCTION
+ result<buffers_type> read(io_request<buffers_type> req, deadline d = {}) noexcept
+ {
+ return (_ctx == nullptr) ? _do_read(std::move(req), d) : _do_multiplexer_read(std::move(req), d);
+ }
+};
+
+// Out of line definition purely to work around a bug in GCC where if marked inline,
+// its visibility is hidden and links fail
+inline result<void> listening_socket_handle::set_multiplexer(byte_io_multiplexer *c) noexcept
+{
+ if(!is_multiplexable())
+ {
+ return errc::operation_not_supported;
+ }
+ if(c == _ctx)
+ {
+ return success();
+ }
+ if(_ctx != nullptr)
+ {
+ OUTCOME_TRY(_ctx->do_byte_io_handle_deregister(this));
+ _ctx = nullptr;
+ }
+ if(c != nullptr)
+ {
+ OUTCOME_TRY(auto &&state, c->do_byte_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();
+}
+
+//! \brief Constructor for `listening_socket_handle`
+template <> struct construct<listening_socket_handle>
+{
+ ip::family family;
+ byte_socket_handle::mode _mode = byte_socket_handle::mode::write;
+ byte_socket_handle::caching _caching = byte_socket_handle::caching::all;
+ byte_socket_handle::flag flags = byte_socket_handle::flag::none;
+ result<listening_socket_handle> operator()() const noexcept { return listening_socket_handle::listening_socket(family, _mode, _caching, flags); }
+};
+
+// BEGIN make_free_functions.py
+// END make_free_functions.py
+
+LLFIO_V2_NAMESPACE_END
+
+#if LLFIO_HEADERS_ONLY == 1 && !defined(DOXYGEN_SHOULD_SKIP_THIS)
+#define LLFIO_INCLUDED_BY_HEADER 1
+#include "detail/impl/byte_socket_handle.ipp"
+#undef LLFIO_INCLUDED_BY_HEADER
+#endif
+
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+
+#endif
diff --git a/include/llfio/v2.0/config.hpp b/include/llfio/v2.0/config.hpp
index 08dc7e2e..834a4373 100644
--- a/include/llfio/v2.0/config.hpp
+++ b/include/llfio/v2.0/config.hpp
@@ -428,6 +428,7 @@ LLFIO_V2_NAMESPACE_BEGIN
namespace win
{
using handle = void *;
+ using socket = uintptr_t;
using dword = unsigned long;
} // namespace win
diff --git a/include/llfio/v2.0/detail/impl/io_multiplexer.ipp b/include/llfio/v2.0/detail/impl/byte_io_multiplexer.ipp
index 70f5f4f4..740a7f5b 100644
--- a/include/llfio/v2.0/detail/impl/io_multiplexer.ipp
+++ b/include/llfio/v2.0/detail/impl/byte_io_multiplexer.ipp
@@ -22,7 +22,7 @@ Distributed under the Boost Software License, Version 1.0.
http://www.boost.org/LICENSE_1_0.txt)
*/
-#include "../../io_multiplexer.hpp"
+#include "../../byte_io_multiplexer.hpp"
#include <mutex>
@@ -30,12 +30,12 @@ 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 void set_multiplexer(io_multiplexer *ctx) noexcept { _thread_multiplexer = ctx; }
+ static LLFIO_THREAD_LOCAL byte_io_multiplexer *_thread_multiplexer;
+ LLFIO_HEADERS_ONLY_FUNC_SPEC byte_io_multiplexer *multiplexer() noexcept { return _thread_multiplexer; }
+ LLFIO_HEADERS_ONLY_FUNC_SPEC void set_multiplexer(byte_io_multiplexer *ctx) noexcept { _thread_multiplexer = ctx; }
} // namespace this_thread
-template <bool is_threadsafe> struct io_multiplexer_impl : io_multiplexer
+template <bool is_threadsafe> struct byte_io_multiplexer_impl : byte_io_multiplexer
{
struct _lock_impl_type
{
@@ -45,7 +45,7 @@ template <bool is_threadsafe> struct io_multiplexer_impl : io_multiplexer
_lock_impl_type _lock;
using _lock_guard = std::unique_lock<_lock_impl_type>;
};
-template <> struct io_multiplexer_impl<true> : io_multiplexer
+template <> struct byte_io_multiplexer_impl<true> : byte_io_multiplexer
{
using _lock_impl_type = std::mutex;
_lock_impl_type _lock;
diff --git a/include/llfio/v2.0/detail/impl/byte_socket_handle.ipp b/include/llfio/v2.0/detail/impl/byte_socket_handle.ipp
new file mode 100644
index 00000000..b5cf301a
--- /dev/null
+++ b/include/llfio/v2.0/detail/impl/byte_socket_handle.ipp
@@ -0,0 +1,304 @@
+/* A handle to a byte-orientated socket
+(C) 2021-2021 Niall Douglas <http://www.nedproductions.biz/> (20 commits)
+File Created: Dec 2021
+
+
+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 "../../byte_socket_handle.hpp"
+
+#ifdef _WIN32
+#include <WinSock2.h>
+#include <ws2ipdef.h>
+#else
+#include <netinet/ip.h>
+#include <sys/socket.h>
+#endif
+
+LLFIO_V2_NAMESPACE_BEGIN
+
+namespace ip
+{
+ LLFIO_HEADERS_ONLY_MEMFUNC_SPEC address::address(const ::sockaddr_in &storage) noexcept
+ {
+ static_assert(sizeof(_storage) >= sizeof(::sockaddr_in), "address is not bigger than sockaddr_in");
+ memcpy(_storage, &storage, sizeof(storage));
+ }
+ LLFIO_HEADERS_ONLY_MEMFUNC_SPEC address::address(const ::sockaddr_in6 &storage) noexcept
+ {
+ static_assert(sizeof(_storage) >= sizeof(sockaddr_in6), "address is not bigger than sockaddr_in6");
+ memcpy(_storage, &storage, sizeof(storage));
+ }
+ LLFIO_HEADERS_ONLY_MEMFUNC_SPEC bool address::is_loopback() const noexcept
+ {
+ union
+ {
+ uint32_t val4 = htonl(INADDR_LOOPBACK);
+ byte loopback4[4];
+ };
+ return (is_v4() && 0 == memcmp(ipv4._addr, loopback4, sizeof(ipv4._addr))) || (is_v6() && 0 == memcmp(ipv6._addr, &in6addr_loopback, sizeof(ipv6._addr)));
+ }
+ LLFIO_HEADERS_ONLY_MEMFUNC_SPEC bool address::is_multicast() const noexcept
+ {
+ return (is_v4() && IN_MULTICAST(ntohl(ipv4._addr_be))) || (is_v6() && ipv6._addr[0] == to_byte(0xff) && ipv6._addr[1] == to_byte(0x00));
+ }
+ LLFIO_HEADERS_ONLY_MEMFUNC_SPEC bool address::is_any() const noexcept
+ {
+ return (_family == AF_INET && 0 == ipv4._addr_be && 0 == _port) ||
+ (_family == AF_INET6 && ipv6._addr[0] == to_byte(0) && ipv6._addr[1] == to_byte(0) && ipv6._addr[2] == to_byte(0) && ipv6._addr[3] == to_byte(0) &&
+ ipv6._addr[4] == to_byte(0) && ipv6._addr[5] == to_byte(0) && ipv6._addr[6] == to_byte(0) && ipv6._addr[7] == to_byte(0) &&
+ ipv6._addr[8] == to_byte(0) && ipv6._addr[9] == to_byte(0) && ipv6._addr[10] == to_byte(0) && ipv6._addr[11] == to_byte(0) &&
+ ipv6._addr[12] == to_byte(0) && ipv6._addr[13] == to_byte(0) && ipv6._addr[14] == to_byte(0) && ipv6._addr[15] == to_byte(0) && _port == 0);
+ }
+ LLFIO_HEADERS_ONLY_MEMFUNC_SPEC bool address::is_v4() const noexcept { return _family == AF_INET; }
+ LLFIO_HEADERS_ONLY_MEMFUNC_SPEC bool address::is_v6() const noexcept { return _family == AF_INET6; }
+ LLFIO_HEADERS_ONLY_MEMFUNC_SPEC enum family address::family() const noexcept
+ {
+ switch(_family)
+ {
+ case AF_INET:
+ return family::v4;
+ case AF_INET6:
+ return family::v6;
+ default:
+ return family::unknown;
+ }
+ }
+ LLFIO_HEADERS_ONLY_MEMFUNC_SPEC int address::sockaddrlen() const noexcept
+ {
+ switch(_family)
+ {
+ case AF_INET:
+ return sizeof(sockaddr_in);
+ case AF_INET6:
+ return sizeof(sockaddr_in6);
+ default:
+ return 0;
+ }
+ }
+
+ LLFIO_HEADERS_ONLY_FUNC_SPEC std::ostream &operator<<(std::ostream &s, const address &v)
+ {
+ auto print_byte = [](const byte &b) -> uint16_t
+ {
+ auto *v = (const uint8_t *) &b;
+ return *v;
+ };
+ switch(v._family)
+ {
+ case AF_INET:
+ return s << print_byte(v.ipv4._addr[0]) << "." << print_byte(v.ipv4._addr[1]) << "." << print_byte(v.ipv4._addr[2]) << "." << print_byte(v.ipv4._addr[3])
+ << ":" << v._port;
+ case AF_INET6:
+ {
+ std::stringstream ss;
+ ss << std::hex << ":";
+ for(size_t n = 0; n < 8; n++)
+ {
+#ifdef __BIG_ENDIAN__
+ ss << (print_byte(v.ipv6._addr[n * 2 + 0]) | (print_byte(v.ipv6._addr[n * 2 + 1]) << 8)) << ":";
+#else
+ ss << (print_byte(v.ipv6._addr[n * 2 + 1]) | (print_byte(v.ipv6._addr[n * 2 + 0]) << 8)) << ":";
+#endif
+ }
+ ss << std::dec << "]" << v._port;
+ std::string str(std::move(ss).str());
+ string_view zeros(":0:0:0:0:0:0:0:0:");
+ if(0 == string_view(str).find(zeros))
+ {
+ str.replace(0, zeros.size(), "::::");
+ }
+ else
+ {
+ for(size_t len = zeros.size(); len > 3; len -= 2)
+ {
+ auto idx = string_view(str).find(zeros.substr(0, len));
+ if(idx != str.npos)
+ {
+ str.replace(idx, len, (idx == 0 || (str[idx + len] == ']')) ? ":::" : "::");
+ break;
+ }
+ }
+ }
+ auto idx = str.rfind(":]");
+ str[0] = '[';
+ str[idx] = ']';
+ str[idx + 1] = ':';
+ return s << str;
+ }
+ default:
+ return s << "unknown address";
+ }
+ }
+
+ LLFIO_HEADERS_ONLY_MEMFUNC_SPEC address_v4::address_v4(const bytes_type &bytes, uint16_t port) noexcept
+ {
+ static_assert(sizeof(_storage) >= sizeof(sockaddr_in), "address is not bigger than sockaddr_in");
+ static_assert(offsetof(address_v4, ipv4._addr) == offsetof(sockaddr_in, sin_addr), "offset of ipv4._addr is not that of sockaddr_in.sin_addr");
+ _family = AF_INET;
+ _port = port;
+ memcpy(ipv4._addr, bytes.data(), sizeof(ipv4._addr));
+ }
+ LLFIO_HEADERS_ONLY_MEMFUNC_SPEC address_v4::address_v4(uint_type addr, uint16_t port) noexcept
+ {
+ _family = AF_INET;
+ _port = port;
+ ipv4._addr_be = htonl(addr);
+ }
+
+ LLFIO_HEADERS_ONLY_MEMFUNC_SPEC address_v4::bytes_type address_v4::to_bytes() const noexcept { return ipv4._addr; }
+ LLFIO_HEADERS_ONLY_MEMFUNC_SPEC address_v4::uint_type address_v4::to_uint() const noexcept { return ntohl(ipv4._addr_be); }
+
+ LLFIO_HEADERS_ONLY_MEMFUNC_SPEC address_v4 address_v4::any() noexcept { return address_v4(0, 0); }
+ LLFIO_HEADERS_ONLY_MEMFUNC_SPEC address_v4 address_v4::loopback() noexcept { return address_v4(INADDR_LOOPBACK, 0); }
+
+ LLFIO_HEADERS_ONLY_FUNC_SPEC result<address_v4> make_address_v4(string_view str) noexcept
+ {
+ address_v4 ret(0);
+ auto parse = [&](auto &out, size_t &idx, char sep) -> result<void>
+ {
+ if(idx >= str.size())
+ {
+ return errc::invalid_argument;
+ }
+ auto idx2 = (sep != 0) ? str.find(sep, idx) : str.npos;
+ if(idx2 == str.npos)
+ {
+ idx2 = str.size();
+ }
+ const char *end = str.data() + idx2;
+ auto res = strtoul(str.data() + idx, (char **) &end, 10);
+ if(res == ULONG_MAX || end != str.data() + idx2)
+ {
+ return errc::invalid_argument;
+ }
+ idx = idx2 + 1;
+ out = static_cast<std::decay_t<decltype(out)>>(res);
+ return success();
+ };
+ size_t idx = 0;
+ OUTCOME_TRY(parse(*(uint8_t *) &ret.ipv4._addr[0], idx, '.'));
+ OUTCOME_TRY(parse(*(uint8_t *) &ret.ipv4._addr[1], idx, '.'));
+ OUTCOME_TRY(parse(*(uint8_t *) &ret.ipv4._addr[2], idx, '.'));
+ OUTCOME_TRY(parse(*(uint8_t *) &ret.ipv4._addr[3], idx, ':'));
+ OUTCOME_TRY(parse(ret._port, idx, 0));
+ return ret;
+ }
+
+ LLFIO_HEADERS_ONLY_MEMFUNC_SPEC address_v6::address_v6(const bytes_type &bytes, uint16_t port, uint32_t scope_id) noexcept
+ {
+ static_assert(sizeof(_storage) >= sizeof(sockaddr_in6), "address is not bigger than sockaddr_in6");
+ static_assert(offsetof(address_v6, ipv6._addr) == offsetof(sockaddr_in6, sin6_addr), "offset of ipv6._addr is not that of sockaddr_in6.sin_addr");
+ _family = AF_INET6;
+ _port = port;
+ ipv6._scope_id = scope_id;
+ memcpy(ipv6._addr, bytes.data(), sizeof(ipv6._addr));
+ }
+ LLFIO_HEADERS_ONLY_MEMFUNC_SPEC address_v6::bytes_type address_v6::to_bytes() const noexcept { return ipv6._addr; }
+
+ LLFIO_HEADERS_ONLY_MEMFUNC_SPEC address_v6 address_v6::any() noexcept { return address_v6(bytes_type{(const byte *) &in6addr_any, 16}); }
+ LLFIO_HEADERS_ONLY_MEMFUNC_SPEC address_v6 address_v6::loopback() noexcept { return address_v6(bytes_type{(const byte *) &in6addr_loopback, 16}); }
+
+ LLFIO_HEADERS_ONLY_FUNC_SPEC result<address_v6> make_address_v6(string_view str) noexcept
+ {
+ address_v6 ret(address_v6::any());
+ if(str.empty() || str.front() != '[')
+ {
+ return errc::invalid_argument;
+ }
+ const auto compactidx = str.find("::"), portidx = str.rfind("]:");
+ if(portidx == str.npos)
+ {
+ return errc::invalid_argument;
+ }
+ auto parse = [&](size_t &idx, char sep) -> result<uint16_t>
+ {
+ if(idx >= str.size())
+ {
+ return errc::invalid_argument;
+ }
+ auto idx2 = str.find(sep, idx);
+ if(idx2 == str.npos)
+ {
+ idx2 = str.size();
+ }
+ const char *end = str.data() + idx2;
+ auto res = strtoul(str.data() + idx, (char **) &end, 16);
+ if(res == ULONG_MAX || end != str.data() + idx2)
+ {
+ return errc::invalid_argument;
+ }
+ idx = idx2 + 1;
+ return (uint16_t) res;
+ };
+ size_t idx = 1, n = 0;
+ do
+ {
+ OUTCOME_TRY(auto res, parse(idx, (n == 14) ? ']' : ':'));
+ ret.ipv6._addr[n++] = to_byte(res >> 8);
+ ret.ipv6._addr[n++] = to_byte(res & 0xff);
+ } while(idx < compactidx && idx < portidx);
+ if(compactidx != str.npos)
+ {
+ auto parse2 = [&](size_t &idx) -> result<uint16_t>
+ {
+ auto idx2 = str.rfind(':', idx - 1);
+ if(idx2 == str.npos)
+ {
+ return errc::invalid_argument;
+ }
+ const char *end = str.data() + idx;
+ auto res = strtoul(str.data() + idx2 + 1, (char **) &end, 16);
+ if(res == ULONG_MAX || end != str.data() + idx)
+ {
+ return errc::invalid_argument;
+ }
+ idx = idx2;
+ return (uint16_t) res;
+ };
+ idx = portidx;
+ n = 15;
+ do
+ {
+ OUTCOME_TRY(auto res, parse2(idx));
+ ret.ipv6._addr[n--] = to_byte(res & 0xff);
+ ret.ipv6._addr[n--] = to_byte(res >> 8);
+ } while(idx > compactidx + 1);
+ }
+ const char *end = str.data() + str.size();
+ auto res = strtoul(str.data() + portidx + 2, (char **) &end, 10);
+ if(res == ULONG_MAX || end != str.data() + str.size())
+ {
+ return errc::invalid_argument;
+ }
+ ret._port = (uint16_t) res;
+ return ret;
+ }
+} // namespace ip
+
+LLFIO_V2_NAMESPACE_END
+
+#if LLFIO_HEADERS_ONLY == 1 && !defined(DOXYGEN_SHOULD_SKIP_THIS)
+#ifdef _WIN32
+#include "windows/byte_socket_handle.ipp"
+#else
+#include "posix/byte_socket_handle.ipp"
+#endif
+#endif
diff --git a/include/llfio/v2.0/detail/impl/dynamic_thread_pool_group.ipp b/include/llfio/v2.0/detail/impl/dynamic_thread_pool_group.ipp
index b59214a0..240d5947 100644
--- a/include/llfio/v2.0/detail/impl/dynamic_thread_pool_group.ipp
+++ b/include/llfio/v2.0/detail/impl/dynamic_thread_pool_group.ipp
@@ -2375,8 +2375,8 @@ namespace detail
/****************************************** io_aware_work_item *********************************************/
-LLFIO_HEADERS_ONLY_MEMFUNC_SPEC dynamic_thread_pool_group::io_aware_work_item::io_aware_work_item(span<io_handle_awareness> hs)
- : _handles([](span<io_handle_awareness> hs) -> span<io_handle_awareness> {
+LLFIO_HEADERS_ONLY_MEMFUNC_SPEC dynamic_thread_pool_group::io_aware_work_item::io_aware_work_item(span<byte_io_handle_awareness> hs)
+ : _handles([](span<byte_io_handle_awareness> hs) -> span<byte_io_handle_awareness> {
float all = 0;
for(auto &i : hs)
{
diff --git a/include/llfio/v2.0/detail/impl/posix/io_handle.ipp b/include/llfio/v2.0/detail/impl/posix/byte_io_handle.ipp
index b1cc1388..f0c1a844 100644
--- a/include/llfio/v2.0/detail/impl/posix/io_handle.ipp
+++ b/include/llfio/v2.0/detail/impl/posix/byte_io_handle.ipp
@@ -22,7 +22,7 @@ Distributed under the Boost Software License, Version 1.0.
http://www.boost.org/LICENSE_1_0.txt)
*/
-#include "../../../io_handle.hpp"
+#include "../../../byte_io_handle.hpp"
#include "import.hpp"
@@ -38,12 +38,13 @@ LLFIO_V2_NAMESPACE_BEGIN
constexpr inline void _check_iovec_match()
{
- static_assert(sizeof(io_handle::buffer_type) == sizeof(iovec), "buffer_type and struct iovec do not match in size");
- static_assert(offsetof(io_handle::buffer_type, _data) == offsetof(iovec, iov_base), "buffer_type and struct iovec do not have same offset of data member");
- static_assert(offsetof(io_handle::buffer_type, _len) == offsetof(iovec, iov_len), "buffer_type and struct iovec do not have same offset of len member");
+ static_assert(sizeof(byte_io_handle::buffer_type) == sizeof(iovec), "buffer_type and struct iovec do not match in size");
+ static_assert(offsetof(byte_io_handle::buffer_type, _data) == offsetof(iovec, iov_base),
+ "buffer_type and struct iovec do not have same offset of data member");
+ static_assert(offsetof(byte_io_handle::buffer_type, _len) == offsetof(iovec, iov_len), "buffer_type and struct iovec do not have same offset of len member");
}
-size_t io_handle::_do_max_buffers() const noexcept
+size_t byte_io_handle::_do_max_buffers() const noexcept
{
static size_t v;
if(v == 0u)
@@ -66,7 +67,8 @@ size_t io_handle::_do_max_buffers() const noexcept
return v;
}
-io_handle::io_result<io_handle::buffers_type> io_handle::_do_read(io_handle::io_request<io_handle::buffers_type> reqs, deadline d) noexcept
+byte_io_handle::io_result<byte_io_handle::buffers_type> byte_io_handle::_do_read(byte_io_handle::io_request<byte_io_handle::buffers_type> reqs,
+ deadline d) noexcept
{
LLFIO_LOG_FUNCTION_CALL(this);
if(d && !_v.is_nonblocking())
@@ -125,6 +127,11 @@ io_handle::io_result<io_handle::buffers_type> io_handle::_do_read(io_handle::io_
bytesread = ::readv(_v.fd, iov, reqs.buffers.size());
if(bytesread <= 0)
{
+ if(bytesread == 0 && is_socket())
+ {
+ // Sockets read zero if the remote has shutdown
+ break;
+ }
if(bytesread < 0 && EWOULDBLOCK != errno && EAGAIN != errno)
{
return posix_error();
@@ -163,7 +170,8 @@ io_handle::io_result<io_handle::buffers_type> io_handle::_do_read(io_handle::io_
return {reqs.buffers};
}
-io_handle::io_result<io_handle::const_buffers_type> io_handle::_do_write(io_handle::io_request<io_handle::const_buffers_type> reqs, deadline d) noexcept
+byte_io_handle::io_result<byte_io_handle::const_buffers_type> byte_io_handle::_do_write(byte_io_handle::io_request<byte_io_handle::const_buffers_type> reqs,
+ deadline d) noexcept
{
LLFIO_LOG_FUNCTION_CALL(this);
if(d && !_v.is_nonblocking())
@@ -221,12 +229,18 @@ io_handle::io_result<io_handle::const_buffers_type> io_handle::_do_write(io_hand
// Can't guarantee that user code hasn't enabled SIGPIPE
byteswritten = QUICKCPPLIB_NAMESPACE::signal_guard::signal_guard(
QUICKCPPLIB_NAMESPACE::signal_guard::signalc_set::broken_pipe, [&] { return ::writev(_v.fd, iov, reqs.buffers.size()); },
- [&](const QUICKCPPLIB_NAMESPACE::signal_guard::raised_signal_info * /*unused*/) {
+ [&](const QUICKCPPLIB_NAMESPACE::signal_guard::raised_signal_info * /*unused*/)
+ {
errno = EPIPE;
return -1;
});
if(byteswritten <= 0)
{
+ if(byteswritten == 0 && is_socket())
+ {
+ // Sockets write zero if write has been shutdown
+ break;
+ }
if(byteswritten < 0 && EWOULDBLOCK != errno && EAGAIN != errno)
{
return posix_error();
@@ -265,7 +279,8 @@ io_handle::io_result<io_handle::const_buffers_type> io_handle::_do_write(io_hand
return {reqs.buffers};
}
-io_handle::io_result<io_handle::const_buffers_type> io_handle::_do_barrier(io_handle::io_request<io_handle::const_buffers_type> reqs, barrier_kind kind, deadline d) noexcept
+byte_io_handle::io_result<byte_io_handle::const_buffers_type> byte_io_handle::_do_barrier(byte_io_handle::io_request<byte_io_handle::const_buffers_type> reqs,
+ barrier_kind kind, deadline d) noexcept
{
(void) kind;
LLFIO_LOG_FUNCTION_CALL(this);
diff --git a/include/llfio/v2.0/detail/impl/posix/byte_socket_handle.ipp b/include/llfio/v2.0/detail/impl/posix/byte_socket_handle.ipp
new file mode 100644
index 00000000..33005596
--- /dev/null
+++ b/include/llfio/v2.0/detail/impl/posix/byte_socket_handle.ipp
@@ -0,0 +1,402 @@
+/* A handle to a byte-orientated socket
+(C) 2021-2021 Niall Douglas <http://www.nedproductions.biz/> (20 commits)
+File Created: Dec 2021
+
+
+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 "../../../byte_socket_handle.hpp"
+#include "import.hpp"
+
+#include <netinet/ip.h>
+#include <netinet/tcp.h>
+#include <poll.h>
+#include <sys/socket.h>
+
+#if !defined(SOCK_CLOEXEC) || !defined(SOCK_NONBLOCK)
+#include <fcntl.h>
+#endif
+
+LLFIO_V2_NAMESPACE_BEGIN
+
+namespace detail
+{
+ inline result<void> create_socket(native_handle_type &nativeh, ip::family _family, handle::mode _mode, handle::caching _caching, handle::flag flags) noexcept
+ {
+ flags &= ~handle::flag::unlink_on_first_close;
+ nativeh.behaviour |= native_handle_type::disposition::socket | native_handle_type::disposition::kernel_handle;
+ OUTCOME_TRY(attribs_from_handle_mode_caching_and_flags(nativeh, _mode, handle::creation::if_needed, _caching, flags));
+ nativeh.behaviour &= ~native_handle_type::disposition::seekable; // not seekable
+
+ const unsigned short family = (_family == ip::family::v6) ? AF_INET6 : ((_family == ip::family::v4) ? AF_INET : 0);
+ nativeh.fd = ::socket(family,
+ SOCK_STREAM
+#ifdef SOCK_CLOEXEC
+ | SOCK_CLOEXEC
+#endif
+#ifdef SOCK_NONBLOCK
+ | ((flags & handle::flag::multiplexable) ? SOCK_NONBLOCK : 0)
+#endif
+ ,
+ IPPROTO_TCP);
+ if(nativeh.fd == -1)
+ {
+ return posix_error();
+ }
+#ifndef SOCK_CLOEXEC
+ // Not FD_CLOEXEC as it's only MacOS that doesn't implement SOCK_CLOEXEC, and its F_SETFD requires bit 0.
+ if(-1 == ::fcntl(nativeh.fd, F_SETFD, 1))
+ {
+ return posix_error();
+ }
+#endif
+#ifndef SOCK_NONBLOCK
+ if(flags & handle::flag::multiplexable)
+ {
+ if(-1 == ::fcntl(nativeh.fd, F_SETFL, O_NONBLOCK))
+ {
+ return posix_error();
+ }
+ }
+#endif
+ if(_caching < handle::caching::all)
+ {
+ {
+ int val = 1;
+ if(-1 == ::setsockopt(nativeh.fd, SOL_SOCKET, SO_SNDBUF, (char *) &val, sizeof(val)))
+ {
+ return posix_error();
+ }
+ }
+ {
+ int val = 1;
+ if(-1 == ::setsockopt(nativeh.fd, IPPROTO_TCP, TCP_NODELAY, (char *) &val, sizeof(val)))
+ {
+ return posix_error();
+ }
+ }
+ }
+ return success();
+ }
+} // namespace detail
+
+LLFIO_HEADERS_ONLY_MEMFUNC_SPEC result<ip::address> byte_socket_handle::local_endpoint() const noexcept
+{
+ LLFIO_LOG_FUNCTION_CALL(this);
+ ip::address ret;
+ socklen_t len = (socklen_t) sizeof(ret._storage);
+ if(-1 == getsockname(_v.fd, (::sockaddr *) ret._storage, &len))
+ {
+ return posix_error();
+ }
+ return ret;
+}
+LLFIO_HEADERS_ONLY_MEMFUNC_SPEC result<ip::address> byte_socket_handle::remote_endpoint() const noexcept
+{
+ LLFIO_LOG_FUNCTION_CALL(this);
+ ip::address ret;
+ socklen_t len = (socklen_t) sizeof(ret._storage);
+ if(-1 == getpeername(_v.fd, (::sockaddr *) ret._storage, &len))
+ {
+ return posix_error();
+ }
+ return ret;
+}
+
+LLFIO_HEADERS_ONLY_MEMFUNC_SPEC result<void> byte_socket_handle::shutdown(shutdown_kind kind) noexcept
+{
+ LLFIO_LOG_FUNCTION_CALL(this);
+ const int how = (kind == shutdown_write) ? SHUT_WR : ((kind == shutdown_both) ? SHUT_RDWR : SHUT_RD);
+ if(-1 == ::shutdown(_v.fd, how))
+ {
+ return posix_error();
+ }
+ return success();
+}
+
+LLFIO_HEADERS_ONLY_MEMFUNC_SPEC result<void> byte_socket_handle::_do_connect(const ip::address &addr, deadline d) noexcept
+{
+ LLFIO_LOG_FUNCTION_CALL(this);
+ if(d && !_v.is_nonblocking())
+ {
+ return errc::not_supported;
+ }
+ LLFIO_DEADLINE_TO_SLEEP_INIT(d);
+ if(!(_v.behaviour & native_handle_type::disposition::_is_connected))
+ {
+ if(-1 == ::connect(_v.fd, addr.to_sockaddr(), addr.sockaddrlen()))
+ {
+ auto retcode = errno;
+ if(retcode != EINPROGRESS && retcode != EAGAIN)
+ {
+ return posix_error(retcode);
+ }
+ }
+ _v.behaviour |= native_handle_type::disposition::_is_connected;
+ }
+ if(_v.is_nonblocking())
+ {
+ for(;;)
+ {
+ pollfd writefds;
+ writefds.fd = _v.fd;
+ writefds.events = POLLOUT|POLLERR|POLLHUP;
+ writefds.revents = 0;
+ int timeout = -1;
+ if(d)
+ {
+ std::chrono::milliseconds ms;
+ if(d.steady)
+ {
+ ms = std::chrono::duration_cast<std::chrono::milliseconds>((began_steady + std::chrono::nanoseconds((d).nsecs)) - std::chrono::steady_clock::now());
+ }
+ else
+ {
+ ms = std::chrono::duration_cast<std::chrono::milliseconds>(d.to_time_point() - std::chrono::system_clock::now());
+ }
+ if(ms.count() < 0)
+ {
+ timeout = 0;
+ }
+ else
+ {
+ timeout = (int) ms.count();
+ }
+ }
+ if(-1 == ::poll(&writefds, 1, timeout))
+ {
+ return posix_error();
+ }
+ if(writefds.revents & POLLOUT)
+ {
+ break;
+ }
+ if(writefds.revents & (POLLERR | POLLHUP))
+ {
+ return errc::connection_refused;
+ }
+ LLFIO_DEADLINE_TO_TIMEOUT_LOOP(d);
+ }
+ }
+ if(!is_writable())
+ {
+ OUTCOME_TRY(shutdown(shutdown_write));
+ }
+ else if(!is_readable())
+ {
+ OUTCOME_TRY(shutdown(shutdown_read));
+ }
+ return success();
+}
+
+LLFIO_HEADERS_ONLY_MEMFUNC_SPEC result<byte_socket_handle> byte_socket_handle::byte_socket(ip::family family, mode _mode, caching _caching, flag flags) noexcept
+{
+ result<byte_socket_handle> ret(byte_socket_handle(native_handle_type(), _caching, flags, nullptr));
+ native_handle_type &nativeh = ret.value()._v;
+ LLFIO_LOG_FUNCTION_CALL(&ret);
+ OUTCOME_TRY(detail::create_socket(nativeh, family, _mode, _caching, flags));
+ return ret;
+}
+
+LLFIO_HEADERS_ONLY_MEMFUNC_SPEC result<void> byte_socket_handle::close() noexcept
+{
+ LLFIO_LOG_FUNCTION_CALL(this);
+ if(_v)
+ {
+ if(are_safety_barriers_issued() && is_writable())
+ {
+ auto r = shutdown();
+ if(r)
+ {
+ byte buffer[4096];
+ for(;;)
+ {
+ OUTCOME_TRY(auto readed, read(0, {{buffer}}));
+ if(readed == 0)
+ {
+ break;
+ }
+ }
+ }
+ else if(r.error() != errc::not_connected)
+ {
+ OUTCOME_TRY(std::move(r));
+ }
+ }
+ if(-1 == ::close(_v.fd))
+ {
+ return posix_error();
+ }
+ _v = {};
+ }
+ return success();
+}
+
+
+/*******************************************************************************************************************/
+
+LLFIO_HEADERS_ONLY_MEMFUNC_SPEC result<ip::address> listening_socket_handle::local_endpoint() const noexcept
+{
+ LLFIO_LOG_FUNCTION_CALL(this);
+ ip::address ret;
+ socklen_t len = (socklen_t) sizeof(ret._storage);
+ if(-1 == getsockname(_v.fd, (::sockaddr *) ret._storage, &len))
+ {
+ return posix_error();
+ }
+ return ret;
+}
+
+LLFIO_HEADERS_ONLY_MEMFUNC_SPEC result<void> listening_socket_handle::bind(const ip::address &addr, creation _creation, int backlog) noexcept
+{
+ LLFIO_LOG_FUNCTION_CALL(this);
+ if(_creation != creation::only_if_not_exist)
+ {
+ int val = 1;
+ if(-1 == ::setsockopt(_v.fd, SOL_SOCKET, SO_REUSEADDR, (char *) &val, sizeof(val)))
+ {
+ return posix_error();
+ }
+ }
+ if(-1 == ::bind(_v.fd, addr.to_sockaddr(), addr.sockaddrlen()))
+ {
+ return posix_error();
+ }
+ if(-1 == ::listen(_v.fd, (backlog == -1) ? SOMAXCONN : backlog))
+ {
+ return posix_error();
+ }
+ return success();
+}
+
+LLFIO_HEADERS_ONLY_MEMFUNC_SPEC result<listening_socket_handle> listening_socket_handle::listening_socket(ip::family family, mode _mode, caching _caching,
+ flag flags) noexcept
+{
+ result<listening_socket_handle> ret(listening_socket_handle(native_handle_type(), _caching, flags, nullptr));
+ native_handle_type &nativeh = ret.value()._v;
+ OUTCOME_TRY(detail::create_socket(nativeh, family, _mode, _caching, flags));
+ return ret;
+}
+
+LLFIO_HEADERS_ONLY_MEMFUNC_SPEC result<listening_socket_handle::buffers_type> listening_socket_handle::_do_read(io_request<buffers_type> req,
+ deadline d) noexcept
+{
+ LLFIO_LOG_FUNCTION_CALL(this);
+ if(req.buffers.empty())
+ {
+ return std::move(req.buffers);
+ }
+ LLFIO_DEADLINE_TO_SLEEP_INIT(d);
+ mode _mode = this->is_append_only() ? mode::append : (this->is_writable() ? mode::write : mode::read);
+ caching _caching = this->kernel_caching();
+ auto &b = *req.buffers.begin();
+ native_handle_type nativeh;
+ nativeh.behaviour |= native_handle_type::disposition::socket | native_handle_type::disposition::kernel_handle;
+ OUTCOME_TRY(attribs_from_handle_mode_caching_and_flags(nativeh, _mode, handle::creation::if_needed, _caching, _flags));
+ nativeh.behaviour &= ~native_handle_type::disposition::seekable; // not seekable
+ for(;;)
+ {
+ bool ready_to_accept = true;
+ if(d)
+ {
+ ready_to_accept = false;
+ pollfd readfds;
+ readfds.fd = _v.fd;
+ readfds.events = POLLIN;
+ readfds.revents = 0;
+ int timeout = -1;
+ std::chrono::milliseconds ms;
+ if(d.steady)
+ {
+ ms = std::chrono::duration_cast<std::chrono::milliseconds>((began_steady + std::chrono::nanoseconds((d).nsecs)) - std::chrono::steady_clock::now());
+ }
+ else
+ {
+ ms = std::chrono::duration_cast<std::chrono::milliseconds>(d.to_time_point() - std::chrono::system_clock::now());
+ }
+ if(ms.count() < 0)
+ {
+ timeout = 0;
+ }
+ else
+ {
+ timeout = (int) ms.count();
+ }
+ if(-1 == ::poll(&readfds, 1, timeout))
+ {
+ return posix_error();
+ }
+ if(readfds.revents & POLLIN)
+ {
+ ready_to_accept = true;
+ }
+ }
+ if(ready_to_accept)
+ {
+ socklen_t len = (socklen_t) sizeof(b.second._storage);
+#ifdef __linux__
+ // Linux's accept() doesn't inherit flags for some odd reason
+ nativeh.fd = ::accept4(_v.fd, (sockaddr *) b.second._storage, &len, SOCK_CLOEXEC | ((_flags & handle::flag::multiplexable) ? SOCK_NONBLOCK : 0));
+#else
+ nativeh.fd = ::accept(_v.fd, (sockaddr *) b.second._storage, &len);
+#endif
+ if(-1 != nativeh.fd)
+ {
+ break;
+ }
+ auto retcode = errno;
+ if(EAGAIN != retcode && EWOULDBLOCK != retcode)
+ {
+ return posix_error(retcode);
+ }
+ }
+ LLFIO_DEADLINE_TO_TIMEOUT_LOOP(d);
+ }
+ nativeh.behaviour |= native_handle_type::disposition::_is_connected;
+ if(_caching < caching::all)
+ {
+ {
+ int val = 1;
+ if(-1 == ::setsockopt(nativeh.fd, SOL_SOCKET, SO_SNDBUF, (char *) &val, sizeof(val)))
+ {
+ return posix_error();
+ }
+ }
+ {
+ int val = 1;
+ if(-1 == ::setsockopt(nativeh.fd, IPPROTO_TCP, TCP_NODELAY, (char *) &val, sizeof(val)))
+ {
+ return posix_error();
+ }
+ }
+ }
+ b.first = byte_socket_handle(nativeh, _caching, _flags, _ctx);
+ if(_mode == mode::read)
+ {
+ OUTCOME_TRY(b.first.shutdown(byte_socket_handle::shutdown_write));
+ }
+ else if(_mode == mode::append)
+ {
+ OUTCOME_TRY(b.first.shutdown(byte_socket_handle::shutdown_read));
+ }
+ return std::move(req.buffers);
+}
+
+LLFIO_V2_NAMESPACE_END
diff --git a/include/llfio/v2.0/detail/impl/posix/directory_handle.ipp b/include/llfio/v2.0/detail/impl/posix/directory_handle.ipp
index 81165413..746f4882 100644
--- a/include/llfio/v2.0/detail/impl/posix/directory_handle.ipp
+++ b/include/llfio/v2.0/detail/impl/posix/directory_handle.ipp
@@ -49,7 +49,7 @@ result<directory_handle> directory_handle::directory(const path_handle &base, pa
result<directory_handle> ret(directory_handle(native_handle_type(), 0, 0, _caching, flags));
native_handle_type &nativeh = ret.value()._v;
LLFIO_LOG_FUNCTION_CALL(&ret);
- nativeh.behaviour |= native_handle_type::disposition::directory | native_handle_type::disposition::path;
+ nativeh.behaviour |= native_handle_type::disposition::directory | native_handle_type::disposition::path | native_handle_type::disposition::kernel_handle;
// POSIX does not permit directory opens with O_RDWR like Windows, so silently convert to read
if(_mode == mode::attr_write)
{
diff --git a/include/llfio/v2.0/detail/impl/posix/file_handle.ipp b/include/llfio/v2.0/detail/impl/posix/file_handle.ipp
index 3dab95e3..b77ce9f7 100644
--- a/include/llfio/v2.0/detail/impl/posix/file_handle.ipp
+++ b/include/llfio/v2.0/detail/impl/posix/file_handle.ipp
@@ -47,7 +47,7 @@ result<file_handle> file_handle::file(const path_handle &base, file_handle::path
result<file_handle> ret(file_handle(native_handle_type(), 0, 0, _caching, flags, nullptr));
native_handle_type &nativeh = ret.value()._v;
LLFIO_LOG_FUNCTION_CALL(&ret);
- nativeh.behaviour |= native_handle_type::disposition::file;
+ nativeh.behaviour |= native_handle_type::disposition::file | native_handle_type::disposition::kernel_handle;
OUTCOME_TRY(auto &&attribs, attribs_from_handle_mode_caching_and_flags(nativeh, _mode, _creation, _caching, flags));
attribs &= ~O_NONBLOCK;
nativeh.behaviour &= ~native_handle_type::disposition::nonblocking;
@@ -121,7 +121,7 @@ result<file_handle> file_handle::temp_inode(const path_handle &dirh, mode _mode,
result<file_handle> ret(file_handle(native_handle_type(), 0, 0, _caching, flags, nullptr));
native_handle_type &nativeh = ret.value()._v;
LLFIO_LOG_FUNCTION_CALL(&ret);
- nativeh.behaviour |= native_handle_type::disposition::file;
+ nativeh.behaviour |= native_handle_type::disposition::file | native_handle_type::disposition::kernel_handle;
// Open file exclusively to prevent collision
OUTCOME_TRY(auto &&attribs, attribs_from_handle_mode_caching_and_flags(nativeh, _mode, creation::only_if_not_exist, _caching, flags));
attribs &= ~O_NONBLOCK;
@@ -433,7 +433,7 @@ result<std::vector<file_handle::extent_pair>> file_handle::extents() const noexc
}
}
-result<file_handle::extent_pair> file_handle::clone_extents_to(file_handle::extent_pair extent, io_handle &dest_, io_handle::extent_type destoffset, deadline d,
+result<file_handle::extent_pair> file_handle::clone_extents_to(file_handle::extent_pair extent, byte_io_handle &dest_, byte_io_handle::extent_type destoffset, deadline d,
bool force_copy_now, bool emulate_if_unsupported) noexcept
{
try
diff --git a/include/llfio/v2.0/detail/impl/posix/lockable_io_handle.ipp b/include/llfio/v2.0/detail/impl/posix/lockable_byte_io_handle.ipp
index 9074b4c5..82b86796 100644
--- a/include/llfio/v2.0/detail/impl/posix/lockable_io_handle.ipp
+++ b/include/llfio/v2.0/detail/impl/posix/lockable_byte_io_handle.ipp
@@ -22,7 +22,7 @@ Distributed under the Boost Software License, Version 1.0.
http://www.boost.org/LICENSE_1_0.txt)
*/
-#include "../../../lockable_io_handle.hpp"
+#include "../../../lockable_byte_io_handle.hpp"
#include "import.hpp"
#include <sys/file.h>
@@ -52,7 +52,7 @@ LLFIO_V2_NAMESPACE_BEGIN
#endif
-result<void> lockable_io_handle::lock_file() noexcept
+result<void> lockable_byte_io_handle::lock_file() noexcept
{
LLFIO_LOG_FUNCTION_CALL(this);
if(-1 == flock(_v.fd, LOCK_EX))
@@ -61,7 +61,7 @@ result<void> lockable_io_handle::lock_file() noexcept
}
return success();
}
-bool lockable_io_handle::try_lock_file() noexcept
+bool lockable_byte_io_handle::try_lock_file() noexcept
{
LLFIO_LOG_FUNCTION_CALL(this);
if(-1 == flock(_v.fd, LOCK_EX | LOCK_NB))
@@ -70,13 +70,13 @@ bool lockable_io_handle::try_lock_file() noexcept
}
return true;
}
-void lockable_io_handle::unlock_file() noexcept
+void lockable_byte_io_handle::unlock_file() noexcept
{
LLFIO_LOG_FUNCTION_CALL(this);
(void) flock(_v.fd, LOCK_UN);
}
-result<void> lockable_io_handle::lock_file_shared() noexcept
+result<void> lockable_byte_io_handle::lock_file_shared() noexcept
{
LLFIO_LOG_FUNCTION_CALL(this);
if(-1 == flock(_v.fd, LOCK_SH))
@@ -85,7 +85,7 @@ result<void> lockable_io_handle::lock_file_shared() noexcept
}
return success();
}
-bool lockable_io_handle::try_lock_file_shared() noexcept
+bool lockable_byte_io_handle::try_lock_file_shared() noexcept
{
LLFIO_LOG_FUNCTION_CALL(this);
if(-1 == flock(_v.fd, LOCK_SH | LOCK_NB))
@@ -94,14 +94,14 @@ bool lockable_io_handle::try_lock_file_shared() noexcept
}
return true;
}
-void lockable_io_handle::unlock_file_shared() noexcept
+void lockable_byte_io_handle::unlock_file_shared() noexcept
{
LLFIO_LOG_FUNCTION_CALL(this);
(void) flock(_v.fd, LOCK_UN);
}
-result<lockable_io_handle::extent_guard> lockable_io_handle::lock_file_range(io_handle::extent_type offset, io_handle::extent_type bytes, lock_kind kind, deadline d) noexcept
+result<lockable_byte_io_handle::extent_guard> lockable_byte_io_handle::lock_file_range(byte_io_handle::extent_type offset, byte_io_handle::extent_type bytes, lock_kind kind, deadline d) noexcept
{
LLFIO_LOG_FUNCTION_CALL(this);
if(d && d.nsecs > 0)
@@ -162,7 +162,7 @@ result<lockable_io_handle::extent_guard> lockable_io_handle::lock_file_range(io_
return extent_guard(this, offset, bytes, kind);
}
-void lockable_io_handle::unlock_file_range(io_handle::extent_type offset, io_handle::extent_type bytes) noexcept
+void lockable_byte_io_handle::unlock_file_range(byte_io_handle::extent_type offset, byte_io_handle::extent_type bytes) noexcept
{
LLFIO_LOG_FUNCTION_CALL(this);
bool failed = false;
@@ -198,7 +198,7 @@ void lockable_io_handle::unlock_file_range(io_handle::extent_type offset, io_han
{
auto ret(posix_error());
(void) ret;
- LLFIO_LOG_FATAL(_v.fd, "io_handle::unlock() failed");
+ LLFIO_LOG_FATAL(_v.fd, "byte_io_handle::unlock() failed");
std::terminate();
}
}
diff --git a/include/llfio/v2.0/detail/impl/posix/map_handle.ipp b/include/llfio/v2.0/detail/impl/posix/map_handle.ipp
index 845b8894..98d1e4cc 100644
--- a/include/llfio/v2.0/detail/impl/posix/map_handle.ipp
+++ b/include/llfio/v2.0/detail/impl/posix/map_handle.ipp
@@ -505,7 +505,7 @@ result<map_handle> map_handle::map(section_handle &section, size_type bytes, ext
ret.value()._pagesize = pagesize;
// Make my handle borrow the native handle of my backing storage
ret.value()._v.fd = section.native_handle().fd;
- nativeh.behaviour |= native_handle_type::disposition::allocation;
+ nativeh.behaviour |= native_handle_type::disposition::allocation | native_handle_type::disposition::kernel_handle;
LLFIO_LOG_FUNCTION_CALL(&ret);
return ret;
}
diff --git a/include/llfio/v2.0/detail/impl/posix/path_handle.ipp b/include/llfio/v2.0/detail/impl/posix/path_handle.ipp
index 582ef50c..1e2e12aa 100644
--- a/include/llfio/v2.0/detail/impl/posix/path_handle.ipp
+++ b/include/llfio/v2.0/detail/impl/posix/path_handle.ipp
@@ -56,7 +56,7 @@ result<path_handle> path_handle::path(const path_handle &base, path_handle::path
result<path_handle> ret(in_place_type<path_handle>);
native_handle_type &nativeh = ret.value()._v;
LLFIO_LOG_FUNCTION_CALL(&ret);
- nativeh.behaviour |= native_handle_type::disposition::path;
+ nativeh.behaviour |= native_handle_type::disposition::path | native_handle_type::disposition::kernel_handle;
nativeh.behaviour &= ~native_handle_type::disposition::seekable; // not seekable
int attribs = O_CLOEXEC | O_RDONLY;
#ifdef O_DIRECTORY
diff --git a/include/llfio/v2.0/detail/impl/posix/pipe_handle.ipp b/include/llfio/v2.0/detail/impl/posix/pipe_handle.ipp
index ff56e210..3d8c25a6 100644
--- a/include/llfio/v2.0/detail/impl/posix/pipe_handle.ipp
+++ b/include/llfio/v2.0/detail/impl/posix/pipe_handle.ipp
@@ -32,7 +32,7 @@ result<pipe_handle> pipe_handle::pipe(pipe_handle::path_view_type path, pipe_han
result<pipe_handle> ret(pipe_handle(native_handle_type(), 0, 0, _caching, flags, nullptr));
native_handle_type &nativeh = ret.value()._v;
LLFIO_LOG_FUNCTION_CALL(&ret);
- nativeh.behaviour |= native_handle_type::disposition::pipe;
+ nativeh.behaviour |= native_handle_type::disposition::pipe | native_handle_type::disposition::kernel_handle;
OUTCOME_TRY(auto &&attribs, attribs_from_handle_mode_caching_and_flags(nativeh, _mode, _creation, _caching, flags));
attribs &= ~(O_CREAT | O_EXCL); // needs to be emulated for fifos
nativeh.behaviour &= ~native_handle_type::disposition::seekable; // not seekable
@@ -139,9 +139,9 @@ result<std::pair<pipe_handle, pipe_handle>> pipe_handle::anonymous_pipe(caching
#else
readattribs &= O_NONBLOCK;
#endif
- readnativeh.behaviour |= native_handle_type::disposition::pipe;
+ readnativeh.behaviour |= native_handle_type::disposition::pipe | native_handle_type::disposition::kernel_handle;
readnativeh.behaviour &= ~native_handle_type::disposition::seekable; // not seekable
- writenativeh.behaviour |= native_handle_type::disposition::pipe;
+ writenativeh.behaviour |= native_handle_type::disposition::pipe | native_handle_type::disposition::kernel_handle;
writenativeh.behaviour &= ~native_handle_type::disposition::seekable; // not seekable
int pipefds[2];
#if defined(__linux__) || defined(__FreeBSD__)
diff --git a/include/llfio/v2.0/detail/impl/posix/symlink_handle.ipp b/include/llfio/v2.0/detail/impl/posix/symlink_handle.ipp
index 9ae1ff61..489a343e 100644
--- a/include/llfio/v2.0/detail/impl/posix/symlink_handle.ipp
+++ b/include/llfio/v2.0/detail/impl/posix/symlink_handle.ipp
@@ -303,6 +303,7 @@ LLFIO_HEADERS_ONLY_MEMFUNC_SPEC result<symlink_handle> symlink_handle::symlink(c
nativeh.behaviour &= ~native_handle_type::disposition::nonblocking;
nativeh.behaviour &= ~native_handle_type::disposition::seekable; // not seekable
#if !LLFIO_SYMLINK_HANDLE_IS_FAKED
+ nativeh.behaviour |= native_handle_type::disposition::kernel_handle;
path_handle _dirh_, *dirh = &_dirh_;
path_type leafname;
#else
diff --git a/include/llfio/v2.0/detail/impl/posix/test/io_uring_multiplexer.ipp b/include/llfio/v2.0/detail/impl/posix/test/io_uring_multiplexer.ipp
index afb71d90..523f6a9c 100644
--- a/include/llfio/v2.0/detail/impl/posix/test/io_uring_multiplexer.ipp
+++ b/include/llfio/v2.0/detail/impl/posix/test/io_uring_multiplexer.ipp
@@ -22,7 +22,7 @@ Distributed under the Boost Software License, Version 1.0.
http://www.boost.org/LICENSE_1_0.txt)
*/
-#include "../io_handle.ipp"
+#include "../byte_io_handle.ipp"
#ifndef __linux__
#error This implementation file is for Linux only
@@ -95,11 +95,11 @@ namespace test
- Registered i/o buffers
*/
- template <bool is_threadsafe> class linux_io_uring_multiplexer final : public io_multiplexer_impl<is_threadsafe>
+ template <bool is_threadsafe> class linux_io_uring_multiplexer final : public byte_io_multiplexer_impl<is_threadsafe>
{
- friend LLFIO_HEADERS_ONLY_FUNC_SPEC result<io_multiplexer_ptr> multiplexer_linux_io_uring(size_t threads, bool is_polling) noexcept;
+ friend LLFIO_HEADERS_ONLY_FUNC_SPEC result<byte_io_multiplexer_ptr> multiplexer_linux_io_uring(size_t threads, bool is_polling) noexcept;
- using _base = io_multiplexer_impl<is_threadsafe>;
+ using _base = byte_io_multiplexer_impl<is_threadsafe>;
using _multiplexer_lock_guard = typename _base::_lock_guard;
using path_type = typename _base::path_type;
@@ -478,7 +478,7 @@ namespace test
_io_uring_operation_state *write_or_barrier{nullptr};
} inprogress;
- explicit _registered_fd(io_handle &h)
+ explicit _registered_fd(byte_io_handle &h)
: fd(h.native_handle().fd)
{
}
@@ -569,7 +569,7 @@ namespace test
{
_dequeue_from(it->inprogress.reads, state);
auto &reqs = state->payload.noncompleted.params.read.reqs;
- io_handle::io_result<io_handle::buffers_type> ret(reqs.buffers);
+ byte_io_handle::io_result<byte_io_handle::buffers_type> ret(reqs.buffers);
if(cqe->res < 0)
{
ret = posix_error(-cqe->res);
@@ -602,7 +602,7 @@ namespace test
assert(it->inprogress.write_or_barrier == state);
it->inprogress.write_or_barrier = nullptr;
auto &reqs = state->payload.noncompleted.params.write.reqs;
- io_handle::io_result<io_handle::const_buffers_type> ret(reqs.buffers);
+ byte_io_handle::io_result<byte_io_handle::const_buffers_type> ret(reqs.buffers);
if(cqe->res < 0)
{
ret = posix_error(-cqe->res);
@@ -635,7 +635,7 @@ namespace test
assert(it->inprogress.write_or_barrier == state);
it->inprogress.write_or_barrier = nullptr;
auto &reqs = state->payload.noncompleted.params.barrier.reqs;
- io_handle::io_result<io_handle::const_buffers_type> ret(reqs.buffers);
+ byte_io_handle::io_result<byte_io_handle::const_buffers_type> ret(reqs.buffers);
if(cqe->res < 0)
{
ret = posix_error(-cqe->res);
@@ -866,7 +866,7 @@ namespace test
else
{
this->_v.fd = fd;
- this->_v.behaviour |= native_handle_type::disposition::multiplexer;
+ this->_v.behaviour |= native_handle_type::disposition::multiplexer | native_handle_type::disposition::kernel_handle;
}
return success();
}
@@ -943,7 +943,7 @@ namespace test
}
return success();
}
- virtual result<uint8_t> do_io_handle_register(io_handle *h) noexcept override // linear complexity to total handles registered
+ virtual result<uint8_t> do_byte_io_handle_register(byte_io_handle *h) noexcept override // linear complexity to total handles registered
{
_multiplexer_lock_guard g(this->_lock);
if(h->is_seekable() && -1 == _seekable_iouring_fd)
@@ -955,7 +955,7 @@ namespace test
_registered_fds.insert(std::lower_bound(_registered_fds.begin(), _registered_fds.end(), toinsert), toinsert);
return _change_fd_registration(h->is_seekable(), toinsert.fd, true);
}
- virtual result<void> do_io_handle_deregister(io_handle *h) noexcept override
+ virtual result<void> do_byte_io_handle_deregister(byte_io_handle *h) noexcept override
{
_multiplexer_lock_guard g(this->_lock);
int fd = h->native_handle().fd;
@@ -971,9 +971,9 @@ namespace test
return _change_fd_registration(h->is_seekable(), fd, false);
}
- virtual size_t do_io_handle_max_buffers(const io_handle * /*unused*/) const noexcept override { return IOV_MAX; }
+ virtual size_t do_byte_io_handle_max_buffers(const byte_io_handle * /*unused*/) const noexcept override { return IOV_MAX; }
- virtual result<registered_buffer_type> do_io_handle_allocate_registered_buffer(io_handle *h, size_t &bytes) noexcept override
+ virtual result<registered_buffer_type> do_byte_io_handle_allocate_registered_buffer(byte_io_handle *h, size_t &bytes) noexcept override
{
_multiplexer_lock_guard g(this->_lock);
// Try to reuse any previously registered buffers no longer in use, as
@@ -986,7 +986,7 @@ namespace test
}
}
// The default implementation uses mmap, so this is done for us
- OUTCOME_TRY(auto &&ret, _base::do_io_handle_allocate_registered_buffer(h, bytes));
+ OUTCOME_TRY(auto &&ret, _base::do_byte_io_handle_allocate_registered_buffer(h, bytes));
// Register this buffer with io_uring
struct iovec upd;
upd.iov_base = ret->data();
@@ -1002,7 +1002,7 @@ namespace test
// io_uring has very minimal i/o state requirements
virtual std::pair<size_t, size_t> io_state_requirements() noexcept override { return {sizeof(_io_uring_operation_state), alignof(_io_uring_operation_state)}; }
- virtual io_operation_state *construct(span<byte> storage, io_handle *_h, io_operation_state_visitor *_visitor, registered_buffer_type &&b, deadline d, io_request<buffers_type> reqs) noexcept override
+ virtual io_operation_state *construct(span<byte> storage, byte_io_handle *_h, io_operation_state_visitor *_visitor, registered_buffer_type &&b, deadline d, io_request<buffers_type> reqs) noexcept override
{
assert(storage.size() >= sizeof(_io_uring_operation_state));
assert(((uintptr_t) storage.data() % alignof(_io_uring_operation_state)) == 0);
@@ -1012,7 +1012,7 @@ namespace test
}
return new(storage.data()) _io_uring_operation_state(_h, _visitor, std::move(b), d, std::move(reqs));
}
- virtual io_operation_state *construct(span<byte> storage, io_handle *_h, io_operation_state_visitor *_visitor, registered_buffer_type &&b, deadline d, io_request<const_buffers_type> reqs) noexcept override
+ virtual io_operation_state *construct(span<byte> storage, byte_io_handle *_h, io_operation_state_visitor *_visitor, registered_buffer_type &&b, deadline d, io_request<const_buffers_type> reqs) noexcept override
{
assert(storage.size() >= sizeof(_io_uring_operation_state));
assert(((uintptr_t) storage.data() % alignof(_io_uring_operation_state)) == 0);
@@ -1022,7 +1022,7 @@ namespace test
}
return new(storage.data()) _io_uring_operation_state(_h, _visitor, std::move(b), d, std::move(reqs));
}
- virtual io_operation_state *construct(span<byte> storage, io_handle *_h, io_operation_state_visitor *_visitor, registered_buffer_type &&b, deadline d, io_request<const_buffers_type> reqs, barrier_kind kind) noexcept override
+ virtual io_operation_state *construct(span<byte> storage, byte_io_handle *_h, io_operation_state_visitor *_visitor, registered_buffer_type &&b, deadline d, io_request<const_buffers_type> reqs, barrier_kind kind) noexcept override
{
assert(storage.size() >= sizeof(_io_uring_operation_state));
assert(((uintptr_t) storage.data() % alignof(_io_uring_operation_state)) == 0);
@@ -1070,9 +1070,9 @@ namespace test
return state->state;
}
- // virtual io_operation_state *construct_and_init_io_operation(span<byte> storage, io_handle *_h, io_operation_state_visitor *_visitor, registered_buffer_type &&b, deadline d, io_request<buffers_type> reqs) noexcept override
- // virtual io_operation_state *construct_and_init_io_operation(span<byte> storage, io_handle *_h, io_operation_state_visitor *_visitor, registered_buffer_type &&b, deadline d, io_request<const_buffers_type> reqs) noexcept override
- // virtual io_operation_state *construct_and_init_io_operation(span<byte> storage, io_handle *_h, io_operation_state_visitor *_visitor, registered_buffer_type &&b, deadline d, io_request<const_buffers_type> reqs, barrier_kind kind) noexcept override
+ // virtual io_operation_state *construct_and_init_io_operation(span<byte> storage, byte_io_handle *_h, io_operation_state_visitor *_visitor, registered_buffer_type &&b, deadline d, io_request<buffers_type> reqs) noexcept override
+ // virtual io_operation_state *construct_and_init_io_operation(span<byte> storage, byte_io_handle *_h, io_operation_state_visitor *_visitor, registered_buffer_type &&b, deadline d, io_request<const_buffers_type> reqs) noexcept override
+ // virtual io_operation_state *construct_and_init_io_operation(span<byte> storage, byte_io_handle *_h, io_operation_state_visitor *_visitor, registered_buffer_type &&b, deadline d, io_request<const_buffers_type> reqs, barrier_kind kind) noexcept override
virtual result<void> flush_inited_io_operations() noexcept override
{
@@ -1104,21 +1104,21 @@ namespace test
break;
case io_operation_state_type::read_initiated:
{
- io_handle::io_result<io_handle::buffers_type> ret(errc::operation_canceled);
+ byte_io_handle::io_result<byte_io_handle::buffers_type> ret(errc::operation_canceled);
state->_read_completed(g, std::move(ret).value());
state->count = 1;
return io_operation_state_type::read_completed;
}
case io_operation_state_type::write_initiated:
{
- io_handle::io_result<io_handle::const_buffers_type> ret(errc::operation_canceled);
+ byte_io_handle::io_result<byte_io_handle::const_buffers_type> ret(errc::operation_canceled);
state->_write_completed(g, std::move(ret).value());
state->count = 1;
return io_operation_state_type::write_or_barrier_completed;
}
case io_operation_state_type::barrier_initiated:
{
- io_handle::io_result<io_handle::const_buffers_type> ret(errc::operation_canceled);
+ byte_io_handle::io_result<byte_io_handle::const_buffers_type> ret(errc::operation_canceled);
state->_barrier_completed(g, std::move(ret).value());
state->count = 1;
return io_operation_state_type::write_or_barrier_completed;
@@ -1223,7 +1223,7 @@ namespace test
}
};
- LLFIO_HEADERS_ONLY_FUNC_SPEC result<io_multiplexer_ptr> multiplexer_linux_io_uring(size_t threads, bool is_polling) noexcept
+ LLFIO_HEADERS_ONLY_FUNC_SPEC result<byte_io_multiplexer_ptr> multiplexer_linux_io_uring(size_t threads, bool is_polling) noexcept
{
try
{
@@ -1232,11 +1232,11 @@ namespace test
// Make non locking edition
auto ret = std::make_unique<linux_io_uring_multiplexer<false>>(is_polling);
OUTCOME_TRY(ret->init(false, ret->_nonseekable));
- return io_multiplexer_ptr(ret.release());
+ return byte_io_multiplexer_ptr(ret.release());
}
auto ret = std::make_unique<linux_io_uring_multiplexer<true>>(is_polling);
OUTCOME_TRY(ret->init(false, ret->_nonseekable));
- return io_multiplexer_ptr(ret.release());
+ return byte_io_multiplexer_ptr(ret.release());
}
catch(...)
{
diff --git a/include/llfio/v2.0/detail/impl/storage_profile.ipp b/include/llfio/v2.0/detail/impl/storage_profile.ipp
index 61ab55d8..57d54fb9 100644
--- a/include/llfio/v2.0/detail/impl/storage_profile.ipp
+++ b/include/llfio/v2.0/detail/impl/storage_profile.ipp
@@ -391,13 +391,13 @@ namespace storage_profile
{
outcome<void> atomic_rewrite_quantum(storage_profile &sp, file_handle &srch) noexcept
{
- if(sp.atomic_rewrite_quantum.value != static_cast<io_handle::extent_type>(-1))
+ if(sp.atomic_rewrite_quantum.value != static_cast<byte_io_handle::extent_type>(-1))
{
return success();
}
try
{
- using off_t = io_handle::extent_type;
+ using off_t = byte_io_handle::extent_type;
sp.max_aligned_atomic_rewrite.value = 1;
sp.atomic_rewrite_quantum.value = static_cast<off_t>(-1);
size_t size = srch.requires_aligned_io() ?
@@ -451,7 +451,7 @@ namespace storage_profile
{
concurrency = 4;
}
- std::atomic<io_handle::extent_type> atomic_rewrite_quantum(sp.atomic_rewrite_quantum.value);
+ std::atomic<byte_io_handle::extent_type> atomic_rewrite_quantum(sp.atomic_rewrite_quantum.value);
std::atomic<bool> failed(false);
for(unsigned no = 0; no < concurrency; no++)
{
@@ -596,7 +596,7 @@ namespace storage_profile
{
concurrency = 4;
}
- std::atomic<io_handle::extent_type> max_aligned_atomic_rewrite(sp.max_aligned_atomic_rewrite.value);
+ std::atomic<byte_io_handle::extent_type> max_aligned_atomic_rewrite(sp.max_aligned_atomic_rewrite.value);
std::atomic<bool> failed(false);
for(unsigned no = 0; no < concurrency; no++)
{
@@ -685,7 +685,7 @@ namespace storage_profile
outcome<void> atomic_rewrite_offset_boundary(storage_profile &sp, file_handle &srch) noexcept
{
- if(sp.atomic_rewrite_offset_boundary.value != static_cast<io_handle::extent_type>(-1))
+ if(sp.atomic_rewrite_offset_boundary.value != static_cast<byte_io_handle::extent_type>(-1))
{
return success();
}
@@ -697,7 +697,7 @@ namespace storage_profile
#endif
try
{
- using off_t = io_handle::extent_type;
+ using off_t = byte_io_handle::extent_type;
auto size = static_cast<size_t>(sp.max_aligned_atomic_rewrite.value);
auto maxsize = static_cast<size_t>(sp.max_aligned_atomic_rewrite.value);
if(size > 1024)
@@ -757,7 +757,7 @@ namespace storage_profile
{
concurrency = 4;
}
- std::atomic<io_handle::extent_type> atomic_rewrite_offset_boundary(sp.atomic_rewrite_offset_boundary.value);
+ std::atomic<byte_io_handle::extent_type> atomic_rewrite_offset_boundary(sp.atomic_rewrite_offset_boundary.value);
std::atomic<bool> failed(false);
for(unsigned no = 0; no < concurrency; no++)
{
diff --git a/include/llfio/v2.0/detail/impl/test/null_multiplexer.ipp b/include/llfio/v2.0/detail/impl/test/null_multiplexer.ipp
index d5e1ef41..f0f6b410 100644
--- a/include/llfio/v2.0/detail/impl/test/null_multiplexer.ipp
+++ b/include/llfio/v2.0/detail/impl/test/null_multiplexer.ipp
@@ -22,7 +22,7 @@ Distributed under the Boost Software License, Version 1.0.
http://www.boost.org/LICENSE_1_0.txt)
*/
-#include "../../../io_handle.hpp"
+#include "../../../byte_io_handle.hpp"
#if !LLFIO_INCLUDED_BY_HEADER || !defined(LLFIO_IO_HANDLE_H)
#error This file should never be included directly
@@ -35,13 +35,13 @@ namespace test
/* You generally speaking want threadsafe and non-threadsafe implementations of
an i/o multiplexer to avoid all locking overheads in the non-threadsafe edition.
- io_multiplexer_impl is defined in io_multiplexer.ipp. It provides _lock_guard
+ byte_io_multiplexer_impl is defined in byte_io_multiplexer.ipp. It provides _lock_guard
and _lock appropriately defined to a null mutex or std::mutex so you can write
identical implementation code.
*/
- template <bool is_threadsafe> class null_multiplexer final : public io_multiplexer_impl<is_threadsafe>
+ template <bool is_threadsafe> class null_multiplexer final : public byte_io_multiplexer_impl<is_threadsafe>
{
- using _base = io_multiplexer_impl<is_threadsafe>;
+ using _base = byte_io_multiplexer_impl<is_threadsafe>;
using _multiplexer_lock_guard = typename _base::_lock_guard;
using path_type = typename _base::path_type;
@@ -57,6 +57,7 @@ namespace test
using registered_buffer_type = typename _base::registered_buffer_type;
template <class T> using io_request = typename _base::template io_request<T>;
template <class T> using io_result = typename _base::template io_result<T>;
+ using implementation_information_t = typename _base::implementation_information_t;
using io_operation_state = typename _base::io_operation_state;
using io_operation_state_visitor = typename _base::io_operation_state_visitor;
using check_for_any_completed_io_statistics = typename _base::check_for_any_completed_io_statistics;
@@ -66,7 +67,8 @@ namespace test
from which we can subclass. We can, of course, add our own i/o operation
state. Here we track time.
*/
- struct _null_operation_state final : public std::conditional_t<is_threadsafe, typename _base::_synchronised_io_operation_state, typename _base::_unsynchronised_io_operation_state>
+ struct _null_operation_state final
+ : public std::conditional_t<is_threadsafe, typename _base::_synchronised_io_operation_state, typename _base::_unsynchronised_io_operation_state>
{
using _impl = std::conditional_t<is_threadsafe, typename _base::_synchronised_io_operation_state, typename _base::_unsynchronised_io_operation_state>;
@@ -172,6 +174,20 @@ namespace test
return success();
}
+ virtual implementation_information_t implementation_information() const noexcept override
+ {
+ static auto v = []() -> implementation_information_t {
+ implementation_information_t ret;
+ ret.name = "null multiplexer";
+ ret.multiplexes.kernel.file_handle = true;
+ ret.multiplexes.kernel.pipe_handle = true;
+ ret.multiplexes.kernel.byte_socket_handle = true;
+ ret.multiplexes.kernel.listening_socket_handle = false;
+ return ret;
+ }();
+ return v;
+ }
+
// These functions are inherited from handle
virtual result<path_type> current_path() const noexcept override
{
@@ -189,21 +205,21 @@ namespace test
// virtual native_handle_type release() noexcept override { return _base::release(); }
// This registers an i/o handle with the system i/o multiplexer
- virtual result<uint8_t> do_io_handle_register(io_handle * /*unused*/) noexcept override
+ virtual result<uint8_t> do_byte_io_handle_register(byte_io_handle * /*unused*/) noexcept override
{
_multiplexer_lock_guard g(this->_lock);
return success();
}
// This deregisters an i/o handle from the system i/o multiplexer
- virtual result<void> do_io_handle_deregister(io_handle * /*unused*/) noexcept override
+ virtual result<void> do_byte_io_handle_deregister(byte_io_handle * /*unused*/) noexcept override
{
_multiplexer_lock_guard g(this->_lock);
return success();
}
// This returns the maximum *atomic* number of scatter-gather i/o that this i/o multiplexer can do
- // This is the value returned by io_handle::max_buffers()
- virtual size_t do_io_handle_max_buffers(const io_handle * /*unused*/) const noexcept override { return 1; }
+ // This is the value returned by byte_io_handle::max_buffers()
+ virtual size_t do_byte_io_handle_max_buffers(const byte_io_handle * /*unused*/) const noexcept override { return 1; }
// This allocates a registered i/o buffer with the system i/o multiplexer
// The default implementation calls mmap()/VirtualAlloc(), creates a deallocating
@@ -214,7 +230,7 @@ namespace test
// RDMA device into the CPU. The program would read and write that shared memory. Upon
// i/o, the buffer would be locked/unmapped for RDMA, thus implementing true zero whole
// system memory copy i/o
- // virtual result<registered_buffer_type> do_io_handle_allocate_registered_buffer(io_handle *h, size_t &bytes) noexcept override {}
+ // virtual result<registered_buffer_type> do_byte_io_handle_allocate_registered_buffer(byte_io_handle *h, size_t &bytes) noexcept override {}
// Code other side of the formal ABI boundary allocate the storage for i/o operation
// states. They need to know how many bytes to allocate, and what alignment they must
@@ -227,7 +243,8 @@ namespace test
virtual std::pair<size_t, size_t> io_state_requirements() noexcept override { return {sizeof(_null_operation_state), alignof(_null_operation_state)}; }
// These are straight i/o state construction functions, one each for read, write and barrier
- virtual io_operation_state *construct(span<byte> storage, io_handle *_h, io_operation_state_visitor *_visitor, registered_buffer_type &&b, deadline d, io_request<buffers_type> reqs) noexcept override
+ virtual io_operation_state *construct(span<byte> storage, byte_io_handle *_h, io_operation_state_visitor *_visitor, registered_buffer_type &&b, deadline d,
+ io_request<buffers_type> reqs) noexcept override
{
assert(storage.size() >= sizeof(_null_operation_state));
assert(((uintptr_t) storage.data() % alignof(_null_operation_state)) == 0);
@@ -237,7 +254,8 @@ namespace test
}
return new(storage.data()) _null_operation_state(_h, _visitor, std::move(b), d, std::move(reqs));
}
- virtual io_operation_state *construct(span<byte> storage, io_handle *_h, io_operation_state_visitor *_visitor, registered_buffer_type &&b, deadline d, io_request<const_buffers_type> reqs) noexcept override
+ virtual io_operation_state *construct(span<byte> storage, byte_io_handle *_h, io_operation_state_visitor *_visitor, registered_buffer_type &&b, deadline d,
+ io_request<const_buffers_type> reqs) noexcept override
{
assert(storage.size() >= sizeof(_null_operation_state));
assert(((uintptr_t) storage.data() % alignof(_null_operation_state)) == 0);
@@ -247,7 +265,8 @@ namespace test
}
return new(storage.data()) _null_operation_state(_h, _visitor, std::move(b), d, std::move(reqs));
}
- virtual io_operation_state *construct(span<byte> storage, io_handle *_h, io_operation_state_visitor *_visitor, registered_buffer_type &&b, deadline d, io_request<const_buffers_type> reqs, barrier_kind kind) noexcept override
+ virtual io_operation_state *construct(span<byte> storage, byte_io_handle *_h, io_operation_state_visitor *_visitor, registered_buffer_type &&b, deadline d,
+ io_request<const_buffers_type> reqs, barrier_kind kind) noexcept override
{
assert(storage.size() >= sizeof(_null_operation_state));
assert(((uintptr_t) storage.data() % alignof(_null_operation_state)) == 0);
@@ -269,7 +288,7 @@ namespace test
abort();
case io_operation_state_type::read_initialised:
{
- io_handle::io_result<io_handle::buffers_type> ret(state->payload.noncompleted.params.read.reqs.buffers);
+ byte_io_handle::io_result<byte_io_handle::buffers_type> ret(state->payload.noncompleted.params.read.reqs.buffers);
/* Try to eagerly complete the i/o now, if so ... */
if(false /* completed immediately */)
{
@@ -289,7 +308,7 @@ namespace test
}
case io_operation_state_type::write_initialised:
{
- io_handle::io_result<io_handle::const_buffers_type> ret(state->payload.noncompleted.params.write.reqs.buffers);
+ byte_io_handle::io_result<byte_io_handle::const_buffers_type> ret(state->payload.noncompleted.params.write.reqs.buffers);
if(false /* completed immediately */)
{
state->write_completed(std::move(ret).value());
@@ -307,7 +326,7 @@ namespace test
}
case io_operation_state_type::barrier_initialised:
{
- io_handle::io_result<io_handle::const_buffers_type> ret(state->payload.noncompleted.params.write.reqs.buffers);
+ byte_io_handle::io_result<byte_io_handle::const_buffers_type> ret(state->payload.noncompleted.params.write.reqs.buffers);
if(false /* completed immediately */)
{
state->barrier_completed(std::move(ret).value());
@@ -338,9 +357,12 @@ namespace test
// If you can combine `construct()` with `init_io_operation()` into a more efficient implementation,
// you should override these
- // virtual io_operation_state *construct_and_init_io_operation(span<byte> storage, io_handle *_h, io_operation_state_visitor *_visitor, registered_buffer_type &&b, deadline d, io_request<buffers_type> reqs) noexcept override
- // virtual io_operation_state *construct_and_init_io_operation(span<byte> storage, io_handle *_h, io_operation_state_visitor *_visitor, registered_buffer_type &&b, deadline d, io_request<const_buffers_type> reqs) noexcept override
- // virtual io_operation_state *construct_and_init_io_operation(span<byte> storage, io_handle *_h, io_operation_state_visitor *_visitor, registered_buffer_type &&b, deadline d, io_request<const_buffers_type> reqs, barrier_kind kind) noexcept override
+ // virtual io_operation_state *construct_and_init_io_operation(span<byte> storage, byte_io_handle *_h, io_operation_state_visitor *_visitor,
+ // registered_buffer_type &&b, deadline d, io_request<buffers_type> reqs) noexcept override virtual io_operation_state
+ // *construct_and_init_io_operation(span<byte> storage, byte_io_handle *_h, io_operation_state_visitor *_visitor, registered_buffer_type &&b, deadline d,
+ // io_request<const_buffers_type> reqs) noexcept override virtual io_operation_state *construct_and_init_io_operation(span<byte> storage, byte_io_handle
+ // *_h, io_operation_state_visitor *_visitor, registered_buffer_type &&b, deadline d, io_request<const_buffers_type> reqs, barrier_kind kind) noexcept
+ // override
// On some implementations, `init_io_operation()` just enqueues request packets, and
// a separate operation is required to submit the enqueued list.
@@ -369,7 +391,7 @@ namespace test
break;
case io_operation_state_type::read_initiated:
{
- io_handle::io_result<io_handle::buffers_type> ret(state->payload.noncompleted.params.read.reqs.buffers);
+ byte_io_handle::io_result<byte_io_handle::buffers_type> ret(state->payload.noncompleted.params.read.reqs.buffers);
state->_read_completed(g, std::move(ret).value());
if(_disable_immediate_completions)
{
@@ -391,7 +413,7 @@ namespace test
}
case io_operation_state_type::write_initiated:
{
- io_handle::io_result<io_handle::const_buffers_type> ret(state->payload.noncompleted.params.write.reqs.buffers);
+ byte_io_handle::io_result<byte_io_handle::const_buffers_type> ret(state->payload.noncompleted.params.write.reqs.buffers);
state->_write_completed(g, std::move(ret).value());
if(_disable_immediate_completions)
{
@@ -405,7 +427,7 @@ namespace test
}
case io_operation_state_type::barrier_initiated:
{
- io_handle::io_result<io_handle::const_buffers_type> ret(state->payload.noncompleted.params.barrier.reqs.buffers);
+ byte_io_handle::io_result<byte_io_handle::const_buffers_type> ret(state->payload.noncompleted.params.barrier.reqs.buffers);
state->_barrier_completed(g, std::move(ret).value());
if(_disable_immediate_completions)
{
@@ -451,21 +473,21 @@ namespace test
break;
case io_operation_state_type::read_initiated:
{
- io_handle::io_result<io_handle::buffers_type> ret(errc::operation_canceled);
+ byte_io_handle::io_result<byte_io_handle::buffers_type> ret(errc::operation_canceled);
state->_read_completed(g, std::move(ret).value());
state->count = 1;
return io_operation_state_type::read_completed;
}
case io_operation_state_type::write_initiated:
{
- io_handle::io_result<io_handle::const_buffers_type> ret(errc::operation_canceled);
+ byte_io_handle::io_result<byte_io_handle::const_buffers_type> ret(errc::operation_canceled);
state->_write_completed(g, std::move(ret).value());
state->count = 1;
return io_operation_state_type::write_or_barrier_completed;
}
case io_operation_state_type::barrier_initiated:
{
- io_handle::io_result<io_handle::const_buffers_type> ret(errc::operation_canceled);
+ byte_io_handle::io_result<byte_io_handle::const_buffers_type> ret(errc::operation_canceled);
state->_barrier_completed(g, std::move(ret).value());
state->count = 1;
return io_operation_state_type::write_or_barrier_completed;
@@ -497,7 +519,8 @@ namespace test
// This must check all i/o initiated or completed on this i/o multiplexer
// and invoke state transition from initiated to completed/finished, or from
// completed to finished, for no more than max_completions i/o states.
- virtual result<check_for_any_completed_io_statistics> check_for_any_completed_io(deadline d = std::chrono::seconds(0), size_t max_completions = (size_t) -1) noexcept override
+ virtual result<check_for_any_completed_io_statistics> check_for_any_completed_io(deadline d = std::chrono::seconds(0),
+ size_t max_completions = (size_t) -1) noexcept override
{
LLFIO_DEADLINE_TO_SLEEP_INIT(d);
check_for_any_completed_io_statistics ret;
@@ -559,7 +582,7 @@ namespace test
}
};
- LLFIO_HEADERS_ONLY_FUNC_SPEC result<io_multiplexer_ptr> multiplexer_null(size_t threads, bool disable_immediate_completions) noexcept
+ LLFIO_HEADERS_ONLY_FUNC_SPEC result<byte_io_multiplexer_ptr> multiplexer_null(size_t threads, bool disable_immediate_completions) noexcept
{
try
{
@@ -567,11 +590,11 @@ namespace test
{
auto ret = std::make_unique<null_multiplexer<false>>();
OUTCOME_TRY(ret->init(1, disable_immediate_completions));
- return io_multiplexer_ptr(ret.release());
+ return byte_io_multiplexer_ptr(ret.release());
}
auto ret = std::make_unique<null_multiplexer<true>>();
OUTCOME_TRY(ret->init(threads, disable_immediate_completions));
- return io_multiplexer_ptr(ret.release());
+ return byte_io_multiplexer_ptr(ret.release());
}
catch(...)
{
diff --git a/include/llfio/v2.0/detail/impl/windows/byte_io_handle.ipp b/include/llfio/v2.0/detail/impl/windows/byte_io_handle.ipp
new file mode 100644
index 00000000..c32d0aec
--- /dev/null
+++ b/include/llfio/v2.0/detail/impl/windows/byte_io_handle.ipp
@@ -0,0 +1,450 @@
+/* A handle to something
+(C) 2015-2021 Niall Douglas <http://www.nedproductions.biz/> (11 commits)
+File Created: Dec 2015
+
+
+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 "../../../byte_io_handle.hpp"
+#include "import.hpp"
+
+#include <WinSock2.h>
+#include <ws2ipdef.h>
+
+LLFIO_V2_NAMESPACE_BEGIN
+
+size_t byte_io_handle::_do_max_buffers() const noexcept
+{
+ if(is_socket())
+ {
+ /* On Windows we make use of the ApcContext parameter in NtReadFile/NtWriteFile to pass through the operation state
+ to IOCP which is not a facility WSASend provides, so if an i/o multiplexer is set, we do each scatter-gather buffer
+ individually.
+
+ Note that a non-test IOCP multiplexer would override max_buffers() in any case so this code would never get called.
+ */
+ return 64;
+ }
+ return 1; // TODO FIXME support ReadFileScatter/WriteFileGather
+}
+
+template <class BuffersType>
+inline bool do_cancel(const native_handle_type &nativeh, span<windows_nt_kernel::IO_STATUS_BLOCK> ols, byte_io_handle::io_request<BuffersType> reqs) noexcept
+{
+ using namespace windows_nt_kernel;
+ using EIOSB = windows_nt_kernel::IO_STATUS_BLOCK;
+ bool did_cancel = false;
+ ols = span<EIOSB>(ols.data(), reqs.buffers.size());
+ for(auto &ol : ols)
+ {
+ if(ol.Status == -1)
+ {
+ // No need to cancel an i/o never begun
+ continue;
+ }
+ NTSTATUS ntstat = ntcancel_pending_io(nativeh.h, ol);
+ if(ntstat < 0 && ntstat != (NTSTATUS) 0xC0000120 /*STATUS_CANCELLED*/)
+ {
+ LLFIO_LOG_FATAL(nullptr, "Failed to cancel earlier i/o");
+ abort();
+ }
+ if(ntstat == (NTSTATUS) 0xC0000120 /*STATUS_CANCELLED*/)
+ {
+ did_cancel = true;
+ }
+ }
+ return did_cancel;
+}
+
+// Returns true if operation completed immediately
+template <bool blocking, class Syscall, class BuffersType>
+inline bool do_read_write(byte_io_handle::io_result<BuffersType> &ret, Syscall &&syscall, const native_handle_type &nativeh,
+ byte_io_multiplexer::io_operation_state *state, span<windows_nt_kernel::IO_STATUS_BLOCK> ols,
+ byte_io_handle::io_request<BuffersType> reqs, deadline d) noexcept
+{
+ using namespace windows_nt_kernel;
+ using EIOSB = windows_nt_kernel::IO_STATUS_BLOCK;
+ LLFIO_WIN_DEADLINE_TO_SLEEP_INIT(d);
+ ols = span<EIOSB>(ols.data(), reqs.buffers.size());
+ memset(ols.data(), 0, reqs.buffers.size() * sizeof(EIOSB));
+ auto ol_it = ols.begin();
+ for(auto &req : reqs.buffers)
+ {
+ (void) req;
+ EIOSB &ol = *ol_it++;
+ ol.Status = -1;
+ }
+ auto cancel_io = make_scope_exit([&]() noexcept {
+ if(nativeh.is_nonblocking())
+ {
+ if(ol_it != ols.begin() + 1)
+ {
+ do_cancel(nativeh, ols, reqs);
+ }
+ }
+ });
+ ol_it = ols.begin();
+ for(auto &req : reqs.buffers)
+ {
+ EIOSB &ol = *ol_it++;
+ LARGE_INTEGER offset;
+ if(nativeh.is_append_only())
+ {
+ offset.QuadPart = -1;
+ }
+ else
+ {
+#ifndef NDEBUG
+ if(nativeh.requires_aligned_io())
+ {
+ assert((reqs.offset & 511) == 0);
+ }
+#endif
+ offset.QuadPart = reqs.offset;
+ }
+#ifndef NDEBUG
+ if(nativeh.requires_aligned_io())
+ {
+ assert(((uintptr_t) req.data() & 511) == 0);
+ // Reads must use aligned length as well
+ assert(std::is_const<typename BuffersType::value_type>::value || (req.size() & 511) == 0);
+ }
+#endif
+ reqs.offset += req.size();
+ ol.Status = 0x103 /*STATUS_PENDING*/;
+ NTSTATUS ntstat = syscall(nativeh.h, nullptr, nullptr, state, &ol, (PVOID) req.data(), static_cast<DWORD>(req.size()), &offset, nullptr);
+ if(ntstat < 0 && ntstat != 0x103 /*STATUS_PENDING*/)
+ {
+ InterlockedCompareExchange(&ol.Status, ntstat, 0x103 /*STATUS_PENDING*/);
+ ret = ntkernel_error(ntstat);
+ return true;
+ }
+ }
+ // If handle is overlapped, wait for completion of each i/o.
+ if(nativeh.is_nonblocking() && blocking)
+ {
+ for(auto &ol : ols)
+ {
+ deadline nd;
+ LLFIO_DEADLINE_TO_PARTIAL_DEADLINE(nd, d);
+ if(STATUS_TIMEOUT == ntwait(nativeh.h, ol, nd))
+ {
+ // ntwait cancels the i/o, undoer will cancel all the other i/o
+ auto r = [&]() -> result<void> {
+ LLFIO_WIN_DEADLINE_TO_TIMEOUT_LOOP(d);
+ return success();
+ }();
+ if(!r)
+ {
+ ret = std::move(r).error();
+ return true;
+ }
+ }
+ }
+ }
+ cancel_io.release();
+ if(!blocking)
+ {
+ // If all the operations already completed, great
+ for(size_t n = 0; n < reqs.buffers.size(); n++)
+ {
+ if(ols[n].Status == static_cast<ULONG_PTR>(0x103 /*STATUS_PENDING*/))
+ {
+ return false; // at least one buffer is not completed yet
+ }
+ }
+ }
+ ret = {reqs.buffers.data(), 0};
+ for(size_t n = 0; n < reqs.buffers.size(); n++)
+ {
+ assert(ols[n].Status != -1);
+ if(ols[n].Status < 0)
+ {
+ ret = ntkernel_error(static_cast<NTSTATUS>(ols[n].Status));
+ return true;
+ }
+ reqs.buffers[n] = {reqs.buffers[n].data(), ols[n].Information};
+ if(reqs.buffers[n].size() != 0)
+ {
+ ret = {reqs.buffers.data(), n + 1};
+ }
+ }
+ return true;
+}
+#ifndef LLFIO_EXCLUDE_NETWORKING
+// We use a separate implementation here so for non-multiplexed as-if blocking socket i/o so
+// we get proper atomic scatter-gather i/o. A proper IOCP multiplexer would need to implement
+// the usual tricks of appending to OVERLAPPED etc to store the i/o state pointer, and not
+// be naughty like our test IOCP multiplexer is by misusing the ApcContext parameter.
+//
+// Returns true if operation completed immediately
+template <bool blocking, class Syscall, class Flags, class BuffersType>
+inline bool do_read_write(byte_io_handle::io_result<BuffersType> &ret, Syscall &&syscall, const native_handle_type &nativeh, span<WSABUF> bufs, Flags flags,
+ byte_io_handle::io_request<BuffersType> reqs, deadline d) noexcept
+{
+ LLFIO_DEADLINE_TO_SLEEP_INIT(d);
+ for(size_t n = 0; n < reqs.buffers.size(); n++)
+ {
+ bufs[n].buf = (char *) reqs.buffers[n].data();
+ bufs[n].len = (ULONG) reqs.buffers[n].size();
+ }
+retry:
+ DWORD transferred = 0;
+ WSAOVERLAPPED ol;
+ memset(&ol, 0, sizeof(ol));
+ ol.Internal = (ULONG_PTR) -1;
+ if(SOCKET_ERROR == syscall(nativeh.sock, bufs.data(), (DWORD) reqs.buffers.size(), &transferred, flags, d ? &ol : nullptr, nullptr))
+ {
+ auto retcode = WSAGetLastError();
+ if(WSA_IO_PENDING != retcode && WSAEWOULDBLOCK != retcode)
+ {
+ if(WSAESHUTDOWN == retcode)
+ {
+ // Emulate POSIX here
+ transferred = 0;
+ goto exit_now;
+ }
+ ret = win32_error(retcode);
+ return true;
+ }
+ if(nativeh.is_nonblocking() && blocking)
+ {
+ deadline nd;
+ LLFIO_DEADLINE_TO_PARTIAL_DEADLINE(nd, d);
+ if(WSA_IO_PENDING == retcode)
+ {
+ if(STATUS_TIMEOUT == ntwait(nativeh.h, ol, nd))
+ {
+ // ntwait cancels the i/o, undoer will cancel all the other i/o
+ auto r = [&]() -> result<void> {
+ LLFIO_DEADLINE_TO_TIMEOUT_LOOP(d);
+ return success();
+ }();
+ if(!r)
+ {
+ ret = std::move(r).error();
+ return true;
+ }
+ }
+ DWORD flags_ = 0;
+ if(!WSAGetOverlappedResult(nativeh.sock, &ol, &transferred, false, &flags_))
+ {
+ ret = win32_error(WSAGetLastError());
+ return true;
+ }
+ }
+ else
+ {
+ // i/o was never started, so poll
+ static constexpr bool is_write = std::is_const<typename BuffersType::value_type>::value;
+ pollfd fds;
+ fds.fd = nativeh.sock;
+ fds.events = is_write ? POLLOUT : POLLIN;
+ fds.revents = 0;
+ int timeout = -1;
+ if(nd)
+ {
+ std::chrono::milliseconds ms;
+ if(nd.steady)
+ {
+ ms =
+ std::chrono::duration_cast<std::chrono::milliseconds>((began_steady + std::chrono::nanoseconds((nd).nsecs)) - std::chrono::steady_clock::now());
+ }
+ else
+ {
+ ms = std::chrono::duration_cast<std::chrono::milliseconds>(nd.to_time_point() - std::chrono::system_clock::now());
+ }
+ if(ms.count() < 0)
+ {
+ timeout = 0;
+ }
+ else
+ {
+ timeout = (int) ms.count();
+ }
+ }
+ if(SOCKET_ERROR == WSAPoll(&fds, 1, timeout))
+ {
+ ret = win32_error(WSAGetLastError());
+ return true;
+ }
+ auto r = [&]() -> result<void> {
+ LLFIO_DEADLINE_TO_TIMEOUT_LOOP(d);
+ return success();
+ }();
+ if(!r)
+ {
+ ret = std::move(r).error();
+ return true;
+ }
+ }
+ goto retry;
+ }
+ }
+ if(!blocking)
+ {
+ // If all the operations already completed, great
+ if(ol.Internal == static_cast<ULONG_PTR>(0x103 /*STATUS_PENDING*/))
+ {
+ return false; // at least one buffer is not completed yet
+ }
+ }
+exit_now:
+ for(size_t i = 0; i < reqs.buffers.size(); i++)
+ {
+ auto &buffer = reqs.buffers[i];
+ if(buffer.size() <= static_cast<size_t>(transferred))
+ {
+ transferred -= (DWORD) buffer.size();
+ }
+ else
+ {
+ buffer = {buffer.data(), (size_t) transferred};
+ ret = {reqs.buffers.data(), i + 1};
+ break;
+ }
+ }
+ return true;
+}
+#endif
+
+byte_io_handle::io_result<byte_io_handle::buffers_type> byte_io_handle::_do_read(byte_io_handle::io_request<byte_io_handle::buffers_type> reqs,
+ deadline d) noexcept
+{
+ windows_nt_kernel::init();
+ using namespace windows_nt_kernel;
+ LLFIO_LOG_FUNCTION_CALL(this);
+ byte_io_handle::io_result<byte_io_handle::buffers_type> ret(reqs.buffers);
+ if(d && !_v.is_nonblocking())
+ {
+ return errc::not_supported;
+ }
+ if(reqs.buffers.size() > 64)
+ {
+ return errc::argument_list_too_long;
+ }
+ if(is_socket())
+ {
+#ifdef LLFIO_EXCLUDE_NETWORKING
+ return errc::not_supported;
+#else
+ std::array<WSABUF, 64> bufs;
+ DWORD flags = 0;
+ do_read_write<true>(ret, WSARecv, _v, bufs, &flags, reqs, d);
+ return ret;
+#endif
+ }
+ using EIOSB = windows_nt_kernel::IO_STATUS_BLOCK;
+ std::array<EIOSB, 64> _ols{};
+ do_read_write<true>(ret, NtReadFile, _v, nullptr, {_ols.data(), _ols.size()}, reqs, d);
+ return ret;
+}
+
+byte_io_handle::io_result<byte_io_handle::const_buffers_type> byte_io_handle::_do_write(byte_io_handle::io_request<byte_io_handle::const_buffers_type> reqs,
+ deadline d) noexcept
+{
+ windows_nt_kernel::init();
+ using namespace windows_nt_kernel;
+ LLFIO_LOG_FUNCTION_CALL(this);
+ byte_io_handle::io_result<byte_io_handle::const_buffers_type> ret(reqs.buffers);
+ if(d && !_v.is_nonblocking())
+ {
+ return errc::not_supported;
+ }
+ if(reqs.buffers.size() > 64)
+ {
+ return errc::argument_list_too_long;
+ }
+ if(is_socket())
+ {
+#ifdef LLFIO_EXCLUDE_NETWORKING
+ return errc::not_supported;
+#else
+ std::array<WSABUF, 64> bufs;
+ DWORD flags = 0;
+ do_read_write<true>(ret, WSASend, _v, bufs, flags, reqs, d);
+ return ret;
+#endif
+ }
+ using EIOSB = windows_nt_kernel::IO_STATUS_BLOCK;
+ std::array<EIOSB, 64> _ols{};
+ do_read_write<true>(ret, NtWriteFile, _v, nullptr, {_ols.data(), _ols.size()}, reqs, d);
+ return ret;
+}
+
+byte_io_handle::io_result<byte_io_handle::const_buffers_type> byte_io_handle::_do_barrier(byte_io_handle::io_request<byte_io_handle::const_buffers_type> reqs,
+ barrier_kind kind, deadline d) noexcept
+{
+ windows_nt_kernel::init();
+ using namespace windows_nt_kernel;
+ LLFIO_LOG_FUNCTION_CALL(this);
+ if(d && !_v.is_nonblocking())
+ {
+ return errc::not_supported;
+ }
+ LLFIO_WIN_DEADLINE_TO_SLEEP_INIT(d);
+ using EIOSB = windows_nt_kernel::IO_STATUS_BLOCK;
+ EIOSB ol{};
+ memset(&ol, 0, sizeof(ol));
+ auto *isb = &ol;
+ *isb = make_iostatus();
+ NTSTATUS ntstat;
+ if(NtFlushBuffersFileEx != nullptr)
+ {
+ ULONG flags = 0;
+ switch(kind)
+ {
+ case barrier_kind::nowait_view_only:
+ case barrier_kind::wait_view_only:
+ case barrier_kind::nowait_data_only:
+ case barrier_kind::wait_data_only:
+ flags = 1 /*FLUSH_FLAGS_FILE_DATA_ONLY*/;
+ break;
+ case barrier_kind::nowait_all:
+ case barrier_kind::wait_all:
+ flags = 0;
+ break;
+ }
+ if(((uint8_t) kind & 1) == 0)
+ {
+ flags |= 2 /*FLUSH_FLAGS_NO_SYNC*/;
+ }
+ ntstat = NtFlushBuffersFileEx(_v.h, flags, nullptr, 0, isb);
+ }
+ else
+ {
+ ntstat = NtFlushBuffersFile(_v.h, isb);
+ }
+ if(STATUS_PENDING == ntstat)
+ {
+ ntstat = ntwait(_v.h, ol, d);
+ if(STATUS_TIMEOUT == ntstat)
+ {
+ return errc::timed_out;
+ }
+ }
+ if(ntstat < 0)
+ {
+ return ntkernel_error(ntstat);
+ }
+ return {reqs.buffers};
+}
+
+LLFIO_V2_NAMESPACE_END
diff --git a/include/llfio/v2.0/detail/impl/windows/byte_io_multiplexer.ipp b/include/llfio/v2.0/detail/impl/windows/byte_io_multiplexer.ipp
new file mode 100644
index 00000000..740a7f5b
--- /dev/null
+++ b/include/llfio/v2.0/detail/impl/windows/byte_io_multiplexer.ipp
@@ -0,0 +1,55 @@
+/* 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 "../../byte_io_multiplexer.hpp"
+
+#include <mutex>
+
+LLFIO_V2_NAMESPACE_BEGIN
+
+namespace this_thread
+{
+ static LLFIO_THREAD_LOCAL byte_io_multiplexer *_thread_multiplexer;
+ LLFIO_HEADERS_ONLY_FUNC_SPEC byte_io_multiplexer *multiplexer() noexcept { return _thread_multiplexer; }
+ LLFIO_HEADERS_ONLY_FUNC_SPEC void set_multiplexer(byte_io_multiplexer *ctx) noexcept { _thread_multiplexer = ctx; }
+} // namespace this_thread
+
+template <bool is_threadsafe> struct byte_io_multiplexer_impl : byte_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 byte_io_multiplexer_impl<true> : byte_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
diff --git a/include/llfio/v2.0/detail/impl/windows/byte_socket_handle.ipp b/include/llfio/v2.0/detail/impl/windows/byte_socket_handle.ipp
new file mode 100644
index 00000000..5604f320
--- /dev/null
+++ b/include/llfio/v2.0/detail/impl/windows/byte_socket_handle.ipp
@@ -0,0 +1,433 @@
+/* A handle to a byte-orientated socket
+(C) 2021-2021 Niall Douglas <http://www.nedproductions.biz/> (20 commits)
+File Created: Dec 2021
+
+
+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 "../../../byte_io_handle.hpp"
+#include "import.hpp"
+
+#include <WinSock2.h>
+#include <ws2ipdef.h>
+
+LLFIO_V2_NAMESPACE_BEGIN
+
+namespace detail
+{
+ LLFIO_HEADERS_ONLY_FUNC_SPEC std::pair<std::atomic<int>, WSAData> &socket_handle_instance_count()
+ {
+ static std::pair<std::atomic<int>, WSAData> v;
+ return v;
+ }
+ LLFIO_HEADERS_ONLY_FUNC_SPEC void register_socket_handle_instance(void *ptr) noexcept
+ {
+ auto &inst = socket_handle_instance_count();
+ auto prev = inst.first.fetch_add(1, std::memory_order_relaxed);
+ (void) ptr;
+ // std::cout << "+ " << ptr << " count = " << prev << std::endl;
+ assert(prev >= 0);
+ if(prev == 0)
+ {
+ int retcode = WSAStartup(MAKEWORD(2, 2), &inst.second);
+ if(retcode != 0)
+ {
+ LLFIO_LOG_FATAL(nullptr, "FATAL: Failed to initialise Winsock!");
+ abort();
+ }
+ }
+ }
+ LLFIO_HEADERS_ONLY_FUNC_SPEC void unregister_socket_handle_instance(void *ptr) noexcept
+ {
+ auto &inst = socket_handle_instance_count();
+ auto prev = inst.first.fetch_sub(1, std::memory_order_relaxed);
+ (void) ptr;
+ // std::cout << "- " << ptr << " count = " << prev << std::endl;
+ assert(prev >= 0);
+ if(prev == 1)
+ {
+ int retcode = WSACleanup();
+ if(retcode != 0)
+ {
+ LLFIO_LOG_FATAL(nullptr, "FATAL: Failed to deinitialise Winsock!");
+ abort();
+ }
+ }
+ }
+ inline result<void> create_socket(void *p, native_handle_type &nativeh, ip::family _family, handle::mode _mode, handle::caching _caching,
+ handle::flag flags) noexcept
+ {
+ flags &= ~handle::flag::unlink_on_first_close;
+ nativeh.behaviour |= native_handle_type::disposition::socket | native_handle_type::disposition::kernel_handle;
+ OUTCOME_TRY(access_mask_from_handle_mode(nativeh, _mode, flags));
+ OUTCOME_TRY(attributes_from_handle_caching_and_flags(nativeh, _caching, flags));
+ nativeh.behaviour &= ~native_handle_type::disposition::seekable; // not seekable
+
+ detail::register_socket_handle_instance(p);
+ const unsigned short family = (_family == ip::family::v6) ? AF_INET6 : ((_family == ip::family::v4) ? AF_INET : 0);
+ nativeh.sock =
+ WSASocketW(family, SOCK_STREAM, IPPROTO_TCP, nullptr, 0, WSA_FLAG_NO_HANDLE_INHERIT | ((flags & handle::flag::multiplexable) ? WSA_FLAG_OVERLAPPED : 0));
+ if(nativeh.sock == INVALID_SOCKET)
+ {
+ auto retcode = WSAGetLastError();
+ detail::unregister_socket_handle_instance(p);
+ return win32_error(retcode);
+ }
+ if(_caching < handle::caching::all)
+ {
+ {
+ int val = 1;
+ if(SOCKET_ERROR == ::setsockopt(nativeh.sock, SOL_SOCKET, SO_SNDBUF, (char *) &val, sizeof(val)))
+ {
+ return win32_error(WSAGetLastError());
+ }
+ }
+ {
+ BOOL val = 1;
+ if(SOCKET_ERROR == ::setsockopt(nativeh.sock, IPPROTO_TCP, TCP_NODELAY, (char *) &val, sizeof(val)))
+ {
+ return win32_error(WSAGetLastError());
+ }
+ }
+ }
+ if(flags & handle::flag::multiplexable)
+ {
+ // Also set to non-blocking so reads/writes etc return partially completed, but also
+ // connects and accepts do not block.
+ u_long val = 1;
+ if(SOCKET_ERROR == ::ioctlsocket(nativeh.sock, FIONBIO, &val))
+ {
+ return win32_error(WSAGetLastError());
+ }
+ }
+ return success();
+ }
+} // namespace detail
+
+LLFIO_HEADERS_ONLY_MEMFUNC_SPEC result<ip::address> byte_socket_handle::local_endpoint() const noexcept
+{
+ LLFIO_LOG_FUNCTION_CALL(this);
+ ip::address ret;
+ int len = (int) sizeof(ret._storage);
+ if(SOCKET_ERROR == getsockname(_v.sock, (::sockaddr *) ret._storage, &len))
+ {
+ return win32_error(WSAGetLastError());
+ }
+ return ret;
+}
+LLFIO_HEADERS_ONLY_MEMFUNC_SPEC result<ip::address> byte_socket_handle::remote_endpoint() const noexcept
+{
+ LLFIO_LOG_FUNCTION_CALL(this);
+ ip::address ret;
+ int len = (int) sizeof(ret._storage);
+ if(SOCKET_ERROR == getpeername(_v.sock, (::sockaddr *) ret._storage, &len))
+ {
+ return win32_error(WSAGetLastError());
+ }
+ return ret;
+}
+
+LLFIO_HEADERS_ONLY_MEMFUNC_SPEC result<void> byte_socket_handle::shutdown(shutdown_kind kind) noexcept
+{
+ LLFIO_LOG_FUNCTION_CALL(this);
+ const int how = (kind == shutdown_write) ? SD_SEND : ((kind == shutdown_both) ? SD_BOTH : SD_RECEIVE);
+ if(SOCKET_ERROR == ::shutdown(_v.sock, how))
+ {
+ return win32_error(WSAGetLastError());
+ }
+ return success();
+}
+
+LLFIO_HEADERS_ONLY_MEMFUNC_SPEC result<void> byte_socket_handle::_do_connect(const ip::address &addr, deadline d) noexcept
+{
+ LLFIO_LOG_FUNCTION_CALL(this);
+ if(d && !_v.is_nonblocking())
+ {
+ return errc::not_supported;
+ }
+ LLFIO_DEADLINE_TO_SLEEP_INIT(d);
+ if(!(_v.behaviour & native_handle_type::disposition::_is_connected))
+ {
+ if(SOCKET_ERROR == ::connect(_v.sock, addr.to_sockaddr(), addr.sockaddrlen()))
+ {
+ auto retcode = WSAGetLastError();
+ if(retcode != WSAEWOULDBLOCK)
+ {
+ return win32_error(retcode);
+ }
+ }
+ _v.behaviour |= native_handle_type::disposition::_is_connected;
+ }
+ if(_v.is_nonblocking())
+ {
+ for(;;)
+ {
+ /* For some reason WSAPoll() always returns Incorrect Parameter if you ask
+ to be notified of POLLERR or POLLHUP. At least Windows select() doesn't have
+ the scalability problems of Linux select().
+ */
+ fd_set writefds, errfds;
+ FD_ZERO(&writefds);
+ FD_ZERO(&errfds);
+ FD_SET(_v.sock, &writefds);
+ FD_SET(_v.sock, &errfds);
+ TIMEVAL *timeout = nullptr, _timeout;
+ if(d)
+ {
+ std::chrono::microseconds us;
+ if(d.steady)
+ {
+ us = std::chrono::duration_cast<std::chrono::microseconds>((began_steady + std::chrono::nanoseconds((d).nsecs)) - std::chrono::steady_clock::now());
+ }
+ else
+ {
+ us = std::chrono::duration_cast<std::chrono::microseconds>(d.to_time_point() - std::chrono::system_clock::now());
+ }
+ if(us.count() < 0)
+ {
+ _timeout.tv_sec = 0;
+ _timeout.tv_usec = 0;
+ }
+ else
+ {
+ _timeout.tv_sec = (long) (us.count() / 1000000UL);
+ _timeout.tv_usec = (long) (us.count() % 1000000UL);
+ }
+ timeout = &_timeout;
+ }
+ if(SOCKET_ERROR == ::select(1, nullptr, &writefds, &errfds, timeout))
+ {
+ return win32_error(WSAGetLastError());
+ }
+ if(FD_ISSET(_v.sock, &writefds))
+ {
+ break;
+ }
+ if(FD_ISSET(_v.sock, &errfds))
+ {
+ return errc::connection_refused;
+ }
+ LLFIO_DEADLINE_TO_TIMEOUT_LOOP(d);
+ }
+ }
+ if(!is_writable())
+ {
+ OUTCOME_TRY(shutdown(shutdown_write));
+ }
+ else if(!is_readable())
+ {
+ OUTCOME_TRY(shutdown(shutdown_read));
+ }
+ return success();
+}
+
+
+LLFIO_HEADERS_ONLY_MEMFUNC_SPEC result<byte_socket_handle> byte_socket_handle::byte_socket(ip::family _family, mode _mode, caching _caching,
+ flag flags) noexcept
+{
+ result<byte_socket_handle> ret(byte_socket_handle(native_handle_type(), _caching, flags, nullptr));
+ native_handle_type &nativeh = ret.value()._v;
+ LLFIO_LOG_FUNCTION_CALL(&ret);
+ OUTCOME_TRY(detail::create_socket(&ret.value(), nativeh, _family, _mode, _caching, flags));
+ return ret;
+}
+
+LLFIO_HEADERS_ONLY_MEMFUNC_SPEC result<void> byte_socket_handle::close() noexcept
+{
+ LLFIO_LOG_FUNCTION_CALL(this);
+ if(_v)
+ {
+ if(are_safety_barriers_issued() && is_writable())
+ {
+ auto r = shutdown();
+ if(r)
+ {
+ byte buffer[4096];
+ for(;;)
+ {
+ OUTCOME_TRY(auto readed, read(0, {{buffer}}));
+ if(readed == 0)
+ {
+ break;
+ }
+ }
+ }
+ else if(r.error() != errc::not_connected)
+ {
+ OUTCOME_TRY(std::move(r));
+ }
+ }
+ if(SOCKET_ERROR == ::closesocket(_v.sock))
+ {
+ return win32_error(WSAGetLastError());
+ }
+ _v = {};
+ detail::unregister_socket_handle_instance(this);
+ }
+ return success();
+}
+
+
+/*******************************************************************************************************************/
+
+LLFIO_HEADERS_ONLY_MEMFUNC_SPEC result<ip::address> listening_socket_handle::local_endpoint() const noexcept
+{
+ LLFIO_LOG_FUNCTION_CALL(this);
+ ip::address ret;
+ int len = (int) sizeof(ret._storage);
+ if(SOCKET_ERROR == getsockname(_v.sock, (::sockaddr *) ret._storage, &len))
+ {
+ return win32_error(WSAGetLastError());
+ }
+ return ret;
+}
+
+LLFIO_HEADERS_ONLY_MEMFUNC_SPEC result<void> listening_socket_handle::bind(const ip::address &addr, creation _creation, int backlog) noexcept
+{
+ LLFIO_LOG_FUNCTION_CALL(this);
+ if(_creation != creation::only_if_not_exist)
+ {
+ BOOL val = 1;
+ if(SOCKET_ERROR == ::setsockopt(_v.sock, SOL_SOCKET, SO_REUSEADDR, (char *) &val, sizeof(val)))
+ {
+ return win32_error(WSAGetLastError());
+ }
+ }
+ if(SOCKET_ERROR == ::bind(_v.sock, addr.to_sockaddr(), addr.sockaddrlen()))
+ {
+ return win32_error(WSAGetLastError());
+ }
+ if(SOCKET_ERROR == ::listen(_v.sock, (backlog == -1) ? SOMAXCONN : backlog))
+ {
+ return win32_error(WSAGetLastError());
+ }
+ return success();
+}
+
+LLFIO_HEADERS_ONLY_MEMFUNC_SPEC result<listening_socket_handle> listening_socket_handle::listening_socket(ip::family family, mode _mode, caching _caching,
+ flag flags) noexcept
+{
+ result<listening_socket_handle> ret(listening_socket_handle(native_handle_type(), _caching, flags, nullptr));
+ native_handle_type &nativeh = ret.value()._v;
+ OUTCOME_TRY(detail::create_socket(&ret.value(), nativeh, family, _mode, _caching, flags));
+ return ret;
+}
+
+LLFIO_HEADERS_ONLY_MEMFUNC_SPEC result<listening_socket_handle::buffers_type> listening_socket_handle::_do_read(io_request<buffers_type> req,
+ deadline d) noexcept
+{
+ LLFIO_LOG_FUNCTION_CALL(this);
+ if(req.buffers.empty())
+ {
+ return std::move(req.buffers);
+ }
+ LLFIO_DEADLINE_TO_SLEEP_INIT(d);
+ mode _mode = this->is_append_only() ? mode::append : (this->is_writable() ? mode::write : mode::read);
+ caching _caching = this->kernel_caching();
+ auto &b = *req.buffers.begin();
+ native_handle_type nativeh;
+ nativeh.behaviour |= native_handle_type::disposition::socket | native_handle_type::disposition::kernel_handle;
+ OUTCOME_TRY(access_mask_from_handle_mode(nativeh, _mode, _flags));
+ OUTCOME_TRY(attributes_from_handle_caching_and_flags(nativeh, _caching, _flags));
+ nativeh.behaviour &= ~native_handle_type::disposition::seekable; // not seekable
+ for(;;)
+ {
+ bool ready_to_accept = true;
+ if(d)
+ {
+ ready_to_accept = false;
+ pollfd readfds;
+ readfds.fd = _v.sock;
+ readfds.events = POLLIN;
+ readfds.revents = 0;
+ int timeout = -1;
+ std::chrono::milliseconds ms;
+ if(d.steady)
+ {
+ ms = std::chrono::duration_cast<std::chrono::milliseconds>((began_steady + std::chrono::nanoseconds((d).nsecs)) - std::chrono::steady_clock::now());
+ }
+ else
+ {
+ ms = std::chrono::duration_cast<std::chrono::milliseconds>(d.to_time_point() - std::chrono::system_clock::now());
+ }
+ if(ms.count() < 0)
+ {
+ timeout = 0;
+ }
+ else
+ {
+ timeout = (int) ms.count();
+ }
+ if(SOCKET_ERROR == WSAPoll(&readfds, 1, timeout))
+ {
+ return win32_error(WSAGetLastError());
+ }
+ if(readfds.revents & POLLIN)
+ {
+ ready_to_accept = true;
+ }
+ }
+ if(ready_to_accept)
+ {
+ int len = (int) sizeof(b.second._storage);
+ nativeh.sock = WSAAccept(_v.sock, (sockaddr *) b.second._storage, &len, nullptr, 0);
+ if(INVALID_SOCKET != nativeh.sock)
+ {
+ break;
+ }
+ auto retcode = WSAGetLastError();
+ if(WSAEWOULDBLOCK != retcode)
+ {
+ return win32_error(retcode);
+ }
+ }
+ LLFIO_DEADLINE_TO_TIMEOUT_LOOP(d);
+ }
+ nativeh.behaviour |= native_handle_type::disposition::_is_connected;
+ if(_caching < caching::all)
+ {
+ {
+ int val = 1;
+ if(SOCKET_ERROR == ::setsockopt(nativeh.sock, SOL_SOCKET, SO_SNDBUF, (char *) &val, sizeof(val)))
+ {
+ return win32_error(WSAGetLastError());
+ }
+ }
+ {
+ BOOL val = 1;
+ if(SOCKET_ERROR == ::setsockopt(nativeh.sock, IPPROTO_TCP, TCP_NODELAY, (char *) &val, sizeof(val)))
+ {
+ return win32_error(WSAGetLastError());
+ }
+ }
+ }
+ b.first = byte_socket_handle(nativeh, _caching, _flags, _ctx);
+ if(_mode == mode::read)
+ {
+ OUTCOME_TRY(b.first.shutdown(byte_socket_handle::shutdown_write));
+ }
+ else if(_mode == mode::append)
+ {
+ OUTCOME_TRY(b.first.shutdown(byte_socket_handle::shutdown_read));
+ }
+ return std::move(req.buffers);
+}
+
+LLFIO_V2_NAMESPACE_END
diff --git a/include/llfio/v2.0/detail/impl/windows/directory_handle.ipp b/include/llfio/v2.0/detail/impl/windows/directory_handle.ipp
index 4df473c6..06006274 100644
--- a/include/llfio/v2.0/detail/impl/windows/directory_handle.ipp
+++ b/include/llfio/v2.0/detail/impl/windows/directory_handle.ipp
@@ -43,7 +43,7 @@ result<directory_handle> directory_handle::directory(const path_handle &base, pa
result<directory_handle> ret(directory_handle(native_handle_type(), 0, 0, _caching, flags));
native_handle_type &nativeh = ret.value()._v;
LLFIO_LOG_FUNCTION_CALL(&ret);
- nativeh.behaviour |= native_handle_type::disposition::directory | native_handle_type::disposition::path;
+ nativeh.behaviour |= native_handle_type::disposition::directory | native_handle_type::disposition::path | native_handle_type::disposition::kernel_handle;
DWORD fileshare = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE;
// Trying to truncate a directory returns EISDIR rather than some internal Win32 error code uncomparable to errc
if(_creation == creation::truncate_existing)
diff --git a/include/llfio/v2.0/detail/impl/windows/file_handle.ipp b/include/llfio/v2.0/detail/impl/windows/file_handle.ipp
index 721641a4..500f1f49 100644
--- a/include/llfio/v2.0/detail/impl/windows/file_handle.ipp
+++ b/include/llfio/v2.0/detail/impl/windows/file_handle.ipp
@@ -40,7 +40,7 @@ result<file_handle> file_handle::file(const path_handle &base, file_handle::path
result<file_handle> ret(in_place_type<file_handle>, native_handle_type(), _caching, flags, nullptr);
native_handle_type &nativeh = ret.value()._v;
LLFIO_LOG_FUNCTION_CALL(&ret);
- nativeh.behaviour |= native_handle_type::disposition::file;
+ nativeh.behaviour |= native_handle_type::disposition::file | native_handle_type::disposition::kernel_handle;
DWORD fileshare = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE;
OUTCOME_TRY(auto &&access, access_mask_from_handle_mode(nativeh, _mode, flags));
OUTCOME_TRY(auto &&attribs, attributes_from_handle_caching_and_flags(nativeh, _caching, flags));
@@ -196,7 +196,7 @@ result<file_handle> file_handle::temp_inode(const path_handle &dirh, mode _mode,
result<file_handle> ret(in_place_type<file_handle>, native_handle_type(), _caching, flags, nullptr);
native_handle_type &nativeh = ret.value()._v;
LLFIO_LOG_FUNCTION_CALL(&ret);
- nativeh.behaviour |= native_handle_type::disposition::file;
+ nativeh.behaviour |= native_handle_type::disposition::file | native_handle_type::disposition::kernel_handle;
DWORD fileshare = /* no read nor write access for others */ FILE_SHARE_DELETE;
OUTCOME_TRY(auto &&access, access_mask_from_handle_mode(nativeh, _mode, flags));
OUTCOME_TRY(auto &&attribs, attributes_from_handle_caching_and_flags(nativeh, _caching, flags));
@@ -407,7 +407,7 @@ result<std::vector<file_handle::extent_pair>> file_handle::extents() const noexc
}
}
-result<file_handle::extent_pair> file_handle::clone_extents_to(file_handle::extent_pair extent, io_handle &dest_, io_handle::extent_type destoffset, deadline d,
+result<file_handle::extent_pair> file_handle::clone_extents_to(file_handle::extent_pair extent, byte_io_handle &dest_, byte_io_handle::extent_type destoffset, deadline d,
bool force_copy_now, bool emulate_if_unsupported) noexcept
{
try
diff --git a/include/llfio/v2.0/detail/impl/windows/handle.ipp b/include/llfio/v2.0/detail/impl/windows/handle.ipp
index 7f3a86d9..8393b4a4 100644
--- a/include/llfio/v2.0/detail/impl/windows/handle.ipp
+++ b/include/llfio/v2.0/detail/impl/windows/handle.ipp
@@ -35,6 +35,12 @@ handle::~handle()
auto ret = handle::close();
if(ret.has_error())
{
+#ifndef NDEBUG
+ char msg[1024];
+ snprintf(msg, 1023, "handle::~handle() failed with '%s'", ret.error().message().c_str());
+ msg[1023] = 0;
+ LLFIO_LOG_DEBUG(_v.h, msg);
+#endif
LLFIO_LOG_FATAL(_v.h, "handle::~handle() close failed");
abort();
}
@@ -54,7 +60,7 @@ result<handle::path_type> handle::current_path() const noexcept
// Should I use FILE_NAME_OPENED here instead of the default FILE_NAME_NORMALIZED?
// I think the latter more likely to trap buggy assumptions, so let's do that. If
// people really want FILE_NAME_OPENED, see to_win32_path().
- DWORD len = GetFinalPathNameByHandleW(_v.h, _buffer + 3, (DWORD)(buffer.size() - 4 * sizeof(wchar_t)), VOLUME_NAME_NT); // NOLINT
+ DWORD len = GetFinalPathNameByHandleW(_v.h, _buffer + 3, (DWORD) (buffer.size() - 4 * sizeof(wchar_t)), VOLUME_NAME_NT); // NOLINT
if(len == 0)
{
return win32_error();
diff --git a/include/llfio/v2.0/detail/impl/windows/import.hpp b/include/llfio/v2.0/detail/impl/windows/import.hpp
index dd4b0ac2..f89cda74 100644
--- a/include/llfio/v2.0/detail/impl/windows/import.hpp
+++ b/include/llfio/v2.0/detail/impl/windows/import.hpp
@@ -1942,7 +1942,7 @@ inline void do_unlock_file_range(native_handle_type &_v, handle::extent_type off
{
auto ret = win32_error();
(void) ret;
- LLFIO_LOG_FATAL(_v.h, "io_handle::unlock() failed");
+ LLFIO_LOG_FATAL(_v.h, "byte_io_handle::unlock() failed");
std::terminate();
}
}
@@ -1956,7 +1956,7 @@ inline void do_unlock_file_range(native_handle_type &_v, handle::extent_type off
ol.Internal = ol.Internal & 0xffffffff;
auto ret = ntkernel_error(static_cast<NTSTATUS>(ol.Internal));
(void) ret;
- LLFIO_LOG_FATAL(_v.h, "io_handle::unlock() failed");
+ LLFIO_LOG_FATAL(_v.h, "byte_io_handle::unlock() failed");
std::terminate();
}
}
diff --git a/include/llfio/v2.0/detail/impl/windows/io_handle.ipp b/include/llfio/v2.0/detail/impl/windows/io_handle.ipp
deleted file mode 100644
index dba06f24..00000000
--- a/include/llfio/v2.0/detail/impl/windows/io_handle.ipp
+++ /dev/null
@@ -1,270 +0,0 @@
-/* A handle to something
-(C) 2015-2021 Niall Douglas <http://www.nedproductions.biz/> (11 commits)
-File Created: Dec 2015
-
-
-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_handle.hpp"
-#include "import.hpp"
-
-LLFIO_V2_NAMESPACE_BEGIN
-
-size_t io_handle::_do_max_buffers() const noexcept
-{
- return 1; // TODO FIXME support ReadFileScatter/WriteFileGather
-}
-
-template <class BuffersType> inline bool do_cancel(const native_handle_type &nativeh, span<windows_nt_kernel::IO_STATUS_BLOCK> ols, io_handle::io_request<BuffersType> reqs) noexcept
-{
- using namespace windows_nt_kernel;
- using EIOSB = windows_nt_kernel::IO_STATUS_BLOCK;
- bool did_cancel = false;
- ols = span<EIOSB>(ols.data(), reqs.buffers.size());
- for(auto &ol : ols)
- {
- if(ol.Status == -1)
- {
- // No need to cancel an i/o never begun
- continue;
- }
- NTSTATUS ntstat = ntcancel_pending_io(nativeh.h, ol);
- if(ntstat < 0 && ntstat != (NTSTATUS) 0xC0000120 /*STATUS_CANCELLED*/)
- {
- LLFIO_LOG_FATAL(nullptr, "Failed to cancel earlier i/o");
- abort();
- }
- if(ntstat == (NTSTATUS) 0xC0000120 /*STATUS_CANCELLED*/)
- {
- did_cancel = true;
- }
- }
- return did_cancel;
-}
-
-// Returns true if operation completed immediately
-template <bool blocking, class Syscall, class BuffersType>
-inline bool do_read_write(io_handle::io_result<BuffersType> &ret, Syscall &&syscall, const native_handle_type &nativeh, io_multiplexer::io_operation_state *state, span<windows_nt_kernel::IO_STATUS_BLOCK> ols, io_handle::io_request<BuffersType> reqs, deadline d) noexcept
-{
- using namespace windows_nt_kernel;
- using EIOSB = windows_nt_kernel::IO_STATUS_BLOCK;
- if(d && !nativeh.is_nonblocking())
- {
- ret = errc::not_supported;
- return true;
- }
- LLFIO_WIN_DEADLINE_TO_SLEEP_INIT(d);
- ols = span<EIOSB>(ols.data(), reqs.buffers.size());
- memset(ols.data(), 0, reqs.buffers.size() * sizeof(EIOSB));
- auto ol_it = ols.begin();
- for(auto &req : reqs.buffers)
- {
- (void) req;
- EIOSB &ol = *ol_it++;
- ol.Status = -1;
- }
- auto cancel_io = make_scope_exit([&]() noexcept {
- if(nativeh.is_nonblocking())
- {
- if(ol_it != ols.begin() + 1)
- {
- do_cancel(nativeh, ols, reqs);
- }
- }
- });
- ol_it = ols.begin();
- for(auto &req : reqs.buffers)
- {
- EIOSB &ol = *ol_it++;
- LARGE_INTEGER offset;
- if(nativeh.is_append_only())
- {
- offset.QuadPart = -1;
- }
- else
- {
-#ifndef NDEBUG
- if(nativeh.requires_aligned_io())
- {
- assert((reqs.offset & 511) == 0);
- }
-#endif
- offset.QuadPart = reqs.offset;
- }
-#ifndef NDEBUG
- if(nativeh.requires_aligned_io())
- {
- assert(((uintptr_t) req.data() & 511) == 0);
- // Reads must use aligned length as well
- assert(std::is_const<typename BuffersType::value_type>::value || (req.size() & 511) == 0);
- }
-#endif
- reqs.offset += req.size();
- ol.Status = 0x103 /*STATUS_PENDING*/;
- NTSTATUS ntstat = syscall(nativeh.h, nullptr, nullptr, state, &ol, (PVOID) req.data(), static_cast<DWORD>(req.size()), &offset, nullptr);
- if(ntstat < 0 && ntstat != 0x103 /*STATUS_PENDING*/)
- {
- InterlockedCompareExchange(&ol.Status, ntstat, 0x103 /*STATUS_PENDING*/);
- ret = ntkernel_error(ntstat);
- return true;
- }
- }
- // If handle is overlapped, wait for completion of each i/o.
- if(nativeh.is_nonblocking() && blocking)
- {
- for(auto &ol : ols)
- {
- deadline nd;
- LLFIO_DEADLINE_TO_PARTIAL_DEADLINE(nd, d);
- if(STATUS_TIMEOUT == ntwait(nativeh.h, ol, nd))
- {
- // ntwait cancels the i/o, undoer will cancel all the other i/o
- auto r = [&]() -> result<void> {
- LLFIO_WIN_DEADLINE_TO_TIMEOUT_LOOP(d);
- return success();
- }();
- if(!r)
- {
- ret = std::move(r).error();
- return true;
- }
- }
- }
- }
- cancel_io.release();
- if(!blocking)
- {
- // If all the operations already completed, great
- for(size_t n = 0; n < reqs.buffers.size(); n++)
- {
- if(ols[n].Status == static_cast<ULONG_PTR>(0x103 /*STATUS_PENDING*/))
- {
- return false; // at least one buffer is not completed yet
- }
- }
- }
- ret = {reqs.buffers.data(), 0};
- for(size_t n = 0; n < reqs.buffers.size(); n++)
- {
- assert(ols[n].Status != -1);
- if(ols[n].Status < 0)
- {
- ret = ntkernel_error(static_cast<NTSTATUS>(ols[n].Status));
- return true;
- }
- reqs.buffers[n] = {reqs.buffers[n].data(), ols[n].Information};
- if(reqs.buffers[n].size() != 0)
- {
- ret = {reqs.buffers.data(), n + 1};
- }
- }
- return true;
-}
-
-io_handle::io_result<io_handle::buffers_type> io_handle::_do_read(io_handle::io_request<io_handle::buffers_type> reqs, deadline d) noexcept
-{
- windows_nt_kernel::init();
- using namespace windows_nt_kernel;
- LLFIO_LOG_FUNCTION_CALL(this);
- using EIOSB = windows_nt_kernel::IO_STATUS_BLOCK;
- std::array<EIOSB, 64> _ols{};
- if(reqs.buffers.size() > 64)
- {
- return errc::argument_list_too_long;
- }
- io_handle::io_result<io_handle::buffers_type> ret(reqs.buffers);
- do_read_write<true>(ret, NtReadFile, _v, nullptr, {_ols.data(), _ols.size()}, reqs, d);
- return ret;
-}
-
-io_handle::io_result<io_handle::const_buffers_type> io_handle::_do_write(io_handle::io_request<io_handle::const_buffers_type> reqs, deadline d) noexcept
-{
- windows_nt_kernel::init();
- using namespace windows_nt_kernel;
- LLFIO_LOG_FUNCTION_CALL(this);
- using EIOSB = windows_nt_kernel::IO_STATUS_BLOCK;
- std::array<EIOSB, 64> _ols{};
- if(reqs.buffers.size() > 64)
- {
- return errc::argument_list_too_long;
- }
- io_handle::io_result<io_handle::const_buffers_type> ret(reqs.buffers);
- do_read_write<true>(ret, NtWriteFile, _v, nullptr, {_ols.data(), _ols.size()}, reqs, d);
- return ret;
-}
-
-io_handle::io_result<io_handle::const_buffers_type> io_handle::_do_barrier(io_handle::io_request<io_handle::const_buffers_type> reqs, barrier_kind kind, deadline d) noexcept
-{
- windows_nt_kernel::init();
- using namespace windows_nt_kernel;
- LLFIO_LOG_FUNCTION_CALL(this);
- if(d && !_v.is_nonblocking())
- {
- return errc::not_supported;
- }
- LLFIO_WIN_DEADLINE_TO_SLEEP_INIT(d);
- using EIOSB = windows_nt_kernel::IO_STATUS_BLOCK;
- EIOSB ol{};
- memset(&ol, 0, sizeof(ol));
- auto *isb = &ol;
- *isb = make_iostatus();
- NTSTATUS ntstat;
- if(NtFlushBuffersFileEx != nullptr)
- {
- ULONG flags = 0;
- switch(kind)
- {
- case barrier_kind::nowait_view_only:
- case barrier_kind::wait_view_only:
- case barrier_kind::nowait_data_only:
- case barrier_kind::wait_data_only:
- flags = 1 /*FLUSH_FLAGS_FILE_DATA_ONLY*/;
- break;
- case barrier_kind::nowait_all:
- case barrier_kind::wait_all:
- flags = 0;
- break;
- }
- if(((uint8_t) kind & 1) == 0)
- {
- flags |= 2 /*FLUSH_FLAGS_NO_SYNC*/;
- }
- ntstat = NtFlushBuffersFileEx(_v.h, flags, nullptr, 0, isb);
- }
- else
- {
- ntstat = NtFlushBuffersFile(_v.h, isb);
- }
- if(STATUS_PENDING == ntstat)
- {
- ntstat = ntwait(_v.h, ol, d);
- if(STATUS_TIMEOUT == ntstat)
- {
- return errc::timed_out;
- }
- }
- if(ntstat < 0)
- {
- return ntkernel_error(ntstat);
- }
- return {reqs.buffers};
-}
-
-LLFIO_V2_NAMESPACE_END
diff --git a/include/llfio/v2.0/detail/impl/windows/lockable_io_handle.ipp b/include/llfio/v2.0/detail/impl/windows/lockable_byte_io_handle.ipp
index 8fe3a636..e17ce3d5 100644
--- a/include/llfio/v2.0/detail/impl/windows/lockable_io_handle.ipp
+++ b/include/llfio/v2.0/detail/impl/windows/lockable_byte_io_handle.ipp
@@ -22,56 +22,56 @@ Distributed under the Boost Software License, Version 1.0.
http://www.boost.org/LICENSE_1_0.txt)
*/
-#include "../../../lockable_io_handle.hpp"
+#include "../../../lockable_byte_io_handle.hpp"
#include "import.hpp"
LLFIO_V2_NAMESPACE_BEGIN
-result<void> lockable_io_handle::lock_file() noexcept
+result<void> lockable_byte_io_handle::lock_file() noexcept
{
LLFIO_LOG_FUNCTION_CALL(this);
OUTCOME_TRY(do_lock_file_range(_v, 0xffffffffffffffffULL, 1, true, {}));
return success();
}
-bool lockable_io_handle::try_lock_file() noexcept
+bool lockable_byte_io_handle::try_lock_file() noexcept
{
LLFIO_LOG_FUNCTION_CALL(this);
auto r = do_lock_file_range(_v, 0xffffffffffffffffULL, 1, true, std::chrono::seconds(0));
return !!r;
}
-void lockable_io_handle::unlock_file() noexcept
+void lockable_byte_io_handle::unlock_file() noexcept
{
LLFIO_LOG_FUNCTION_CALL(this);
do_unlock_file_range(_v, 0xffffffffffffffffULL, 1);
}
-result<void> lockable_io_handle::lock_file_shared() noexcept
+result<void> lockable_byte_io_handle::lock_file_shared() noexcept
{
LLFIO_LOG_FUNCTION_CALL(this);
OUTCOME_TRY(do_lock_file_range(_v, 0xffffffffffffffffULL, 1, false, {}));
return success();
}
-bool lockable_io_handle::try_lock_file_shared() noexcept
+bool lockable_byte_io_handle::try_lock_file_shared() noexcept
{
LLFIO_LOG_FUNCTION_CALL(this);
auto r = do_lock_file_range(_v, 0xffffffffffffffffULL, 1, false, std::chrono::seconds(0));
return !!r;
}
-void lockable_io_handle::unlock_file_shared() noexcept
+void lockable_byte_io_handle::unlock_file_shared() noexcept
{
LLFIO_LOG_FUNCTION_CALL(this);
do_unlock_file_range(_v, 0xffffffffffffffffULL, 1);
}
-result<lockable_io_handle::extent_guard> lockable_io_handle::lock_file_range(io_handle::extent_type offset, io_handle::extent_type bytes, lock_kind kind, deadline d) noexcept
+result<lockable_byte_io_handle::extent_guard> lockable_byte_io_handle::lock_file_range(byte_io_handle::extent_type offset, byte_io_handle::extent_type bytes, lock_kind kind, deadline d) noexcept
{
LLFIO_LOG_FUNCTION_CALL(this);
OUTCOME_TRY(do_lock_file_range(_v, offset, bytes, kind != lock_kind::shared, d));
return extent_guard(this, offset, bytes, kind);
}
-void lockable_io_handle::unlock_file_range(io_handle::extent_type offset, io_handle::extent_type bytes) noexcept
+void lockable_byte_io_handle::unlock_file_range(byte_io_handle::extent_type offset, byte_io_handle::extent_type bytes) noexcept
{
LLFIO_LOG_FUNCTION_CALL(this);
do_unlock_file_range(_v, offset, bytes);
diff --git a/include/llfio/v2.0/detail/impl/windows/map_handle.ipp b/include/llfio/v2.0/detail/impl/windows/map_handle.ipp
index 3a8dd905..42878c52 100644
--- a/include/llfio/v2.0/detail/impl/windows/map_handle.ipp
+++ b/include/llfio/v2.0/detail/impl/windows/map_handle.ipp
@@ -122,7 +122,7 @@ result<section_handle> section_handle::section(file_handle &backing, extent_type
attribs |= SEC_LARGE_PAGES;
}
}
- nativeh.behaviour |= native_handle_type::disposition::section;
+ nativeh.behaviour |= native_handle_type::disposition::section | native_handle_type::disposition::kernel_handle;
OBJECT_ATTRIBUTES oa{}, *poa = nullptr;
UNICODE_STRING _path{};
if(_flag & flag::singleton)
@@ -252,7 +252,7 @@ result<section_handle> section_handle::section(extent_type bytes, const path_han
attribs |= SEC_LARGE_PAGES;
}
}
- nativeh.behaviour |= native_handle_type::disposition::section;
+ nativeh.behaviour |= native_handle_type::disposition::section | native_handle_type::disposition::kernel_handle;
LARGE_INTEGER _maximum_size{}, *pmaximum_size = &_maximum_size;
_maximum_size.QuadPart = bytes;
LLFIO_LOG_FUNCTION_CALL(&ret);
@@ -722,7 +722,7 @@ result<map_handle> map_handle::map(section_handle &section, size_type bytes, ext
ret.value()._pagesize = pagesize;
// Make my handle borrow the native handle of my backing storage
ret.value()._v.h = section.backing_native_handle().h;
- nativeh.behaviour |= native_handle_type::disposition::allocation;
+ nativeh.behaviour |= native_handle_type::disposition::allocation | native_handle_type::disposition::kernel_handle;
// Windows has no way of getting the kernel to prefault maps on creation, so ...
if(ret.value()._flag & section_handle::flag::prefault)
diff --git a/include/llfio/v2.0/detail/impl/windows/path_handle.ipp b/include/llfio/v2.0/detail/impl/windows/path_handle.ipp
index 44548b6a..7fab40ab 100644
--- a/include/llfio/v2.0/detail/impl/windows/path_handle.ipp
+++ b/include/llfio/v2.0/detail/impl/windows/path_handle.ipp
@@ -81,7 +81,7 @@ result<path_handle> path_handle::path(const path_handle &base, path_handle::path
result<path_handle> ret{path_handle(native_handle_type(), caching::none, flag::none)};
native_handle_type &nativeh = ret.value()._v;
LLFIO_LOG_FUNCTION_CALL(&ret);
- nativeh.behaviour |= native_handle_type::disposition::path;
+ nativeh.behaviour |= native_handle_type::disposition::path | native_handle_type::disposition::kernel_handle;
DWORD fileshare = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE;
// Open directory with no access requested, this is much faster than asking for access
OUTCOME_TRY(auto &&access, access_mask_from_handle_mode(nativeh, mode::none, flag::none));
diff --git a/include/llfio/v2.0/detail/impl/windows/pipe_handle.ipp b/include/llfio/v2.0/detail/impl/windows/pipe_handle.ipp
index 986618b4..ef9c5bec 100644
--- a/include/llfio/v2.0/detail/impl/windows/pipe_handle.ipp
+++ b/include/llfio/v2.0/detail/impl/windows/pipe_handle.ipp
@@ -35,7 +35,7 @@ result<pipe_handle> pipe_handle::pipe(pipe_handle::path_view_type path, pipe_han
result<pipe_handle> ret(pipe_handle(native_handle_type(), 0, 0, _caching, flags, nullptr));
native_handle_type &nativeh = ret.value()._v;
LLFIO_LOG_FUNCTION_CALL(&ret);
- nativeh.behaviour |= native_handle_type::disposition::pipe;
+ nativeh.behaviour |= native_handle_type::disposition::pipe | native_handle_type::disposition::kernel_handle;
DWORD fileshare = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE;
OUTCOME_TRY(auto &&access, access_mask_from_handle_mode(nativeh, _mode, flags));
OUTCOME_TRY(auto &&attribs, attributes_from_handle_caching_and_flags(nativeh, _caching, flags));
@@ -176,7 +176,7 @@ result<std::pair<pipe_handle, pipe_handle>> pipe_handle::anonymous_pipe(caching
OUTCOME_TRY(auto &&access, access_mask_from_handle_mode(writenativeh, mode::append, flags));
access = SYNCHRONIZE | DELETE | GENERIC_WRITE; // correct for pipes
OUTCOME_TRY(auto &&attribs, attributes_from_handle_caching_and_flags(writenativeh, _caching, flags));
- writenativeh.behaviour |= native_handle_type::disposition::pipe;
+ writenativeh.behaviour |= native_handle_type::disposition::pipe | native_handle_type::disposition::kernel_handle;
writenativeh.behaviour &= ~native_handle_type::disposition::seekable; // not seekable
LLFIO_LOG_FUNCTION_CALL(&ret.first);
@@ -245,13 +245,13 @@ pipe_handle::io_result<pipe_handle::buffers_type> pipe_handle::_do_read(pipe_han
}
_v.behaviour |= native_handle_type::disposition::_is_connected;
}
- return io_handle::_do_read(reqs, d);
+ return byte_io_handle::_do_read(reqs, d);
}
pipe_handle::io_result<pipe_handle::const_buffers_type> pipe_handle::_do_write(pipe_handle::io_request<pipe_handle::const_buffers_type> reqs, deadline d) noexcept
{
LLFIO_LOG_FUNCTION_CALL(this);
- return io_handle::_do_write(reqs, d);
+ return byte_io_handle::_do_write(reqs, d);
}
LLFIO_V2_NAMESPACE_END
diff --git a/include/llfio/v2.0/detail/impl/windows/process_handle.ipp b/include/llfio/v2.0/detail/impl/windows/process_handle.ipp
index a6679209..11e152a1 100644
--- a/include/llfio/v2.0/detail/impl/windows/process_handle.ipp
+++ b/include/llfio/v2.0/detail/impl/windows/process_handle.ipp
@@ -166,7 +166,7 @@ LLFIO_HEADERS_ONLY_MEMFUNC_SPEC result<process_handle> process_handle::launch_pr
result<process_handle> ret(in_place_type<process_handle>, native_handle_type(), flags);
native_handle_type &nativeh = ret.value()._v;
LLFIO_LOG_FUNCTION_CALL(&ret);
- nativeh.behaviour |= native_handle_type::disposition::process;
+ nativeh.behaviour |= native_handle_type::disposition::process | native_handle_type::disposition::kernel_handle;
pipe_handle childinpipe, childoutpipe, childerrorpipe;
pipe_handle::flag pipeflags = !(flags & flag::no_multiplexable_pipes) ? pipe_handle::flag::multiplexable : pipe_handle::flag::none;
diff --git a/include/llfio/v2.0/detail/impl/windows/symlink_handle.ipp b/include/llfio/v2.0/detail/impl/windows/symlink_handle.ipp
index e239d2fd..b3b07c24 100644
--- a/include/llfio/v2.0/detail/impl/windows/symlink_handle.ipp
+++ b/include/llfio/v2.0/detail/impl/windows/symlink_handle.ipp
@@ -44,7 +44,7 @@ LLFIO_HEADERS_ONLY_MEMFUNC_SPEC result<symlink_handle> symlink_handle::symlink(c
result<symlink_handle> ret(symlink_handle(native_handle_type(), 0, 0, flags));
native_handle_type &nativeh = ret.value()._v;
LLFIO_LOG_FUNCTION_CALL(&ret);
- nativeh.behaviour |= native_handle_type::disposition::symlink;
+ nativeh.behaviour |= native_handle_type::disposition::symlink | native_handle_type::disposition::kernel_handle;
DWORD fileshare = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE;
if(_mode == mode::append || _creation == creation::truncate_existing)
{
diff --git a/include/llfio/v2.0/detail/impl/windows/test/iocp_multiplexer.ipp b/include/llfio/v2.0/detail/impl/windows/test/iocp_multiplexer.ipp
index 3e33ff74..54492222 100644
--- a/include/llfio/v2.0/detail/impl/windows/test/iocp_multiplexer.ipp
+++ b/include/llfio/v2.0/detail/impl/windows/test/iocp_multiplexer.ipp
@@ -1,5 +1,5 @@
/* Multiplex file i/o
-(C) 2019 Niall Douglas <http://www.nedproductions.biz/> (9 commits)
+(C) 2019-2021 Niall Douglas <http://www.nedproductions.biz/> (9 commits)
File Created: Nov 2019
@@ -22,7 +22,7 @@ Distributed under the Boost Software License, Version 1.0.
http://www.boost.org/LICENSE_1_0.txt)
*/
-#include "../io_handle.ipp"
+#include "../byte_io_handle.ipp"
#ifndef _WIN32
#error This implementation file is for Microsoft Windows only
@@ -32,9 +32,9 @@ LLFIO_V2_NAMESPACE_BEGIN
namespace test
{
- template <bool is_threadsafe> class win_iocp_multiplexer final : public io_multiplexer_impl<is_threadsafe>
+ template <bool is_threadsafe> class win_iocp_multiplexer final : public byte_io_multiplexer_impl<is_threadsafe>
{
- using _base = io_multiplexer_impl<is_threadsafe>;
+ using _base = byte_io_multiplexer_impl<is_threadsafe>;
using _multiplexer_lock_guard = typename _base::_lock_guard;
using barrier_kind = typename _base::barrier_kind;
@@ -43,6 +43,7 @@ namespace test
using registered_buffer_type = typename _base::registered_buffer_type;
template <class T> using io_request = typename _base::template io_request<T>;
template <class T> using io_result = typename _base::template io_result<T>;
+ using implementation_information_t = typename _base::implementation_information_t;
using io_operation_state = typename _base::io_operation_state;
using io_operation_state_visitor = typename _base::io_operation_state_visitor;
using check_for_any_completed_io_statistics = typename _base::check_for_any_completed_io_statistics;
@@ -94,10 +95,24 @@ namespace test
return win32_error();
}
_disable_immediate_completions = disable_immediate_completions;
- this->_v.behaviour |= native_handle_type::disposition::multiplexer;
+ this->_v.behaviour |= native_handle_type::disposition::multiplexer | native_handle_type::disposition::kernel_handle;
return success();
}
+ virtual implementation_information_t implementation_information() const noexcept override
+ {
+ static auto v = []() -> implementation_information_t {
+ implementation_information_t ret;
+ ret.name = "IOCP multiplexer";
+ ret.multiplexes.kernel.file_handle = true;
+ ret.multiplexes.kernel.pipe_handle = true;
+ ret.multiplexes.kernel.byte_socket_handle = true;
+ ret.multiplexes.kernel.listening_socket_handle = false;
+ return ret;
+ }();
+ return v;
+ }
+
// virtual result<path_type> current_path() const noexcept override;
virtual result<void> close() noexcept override
{
@@ -112,11 +127,15 @@ namespace test
}
// virtual native_handle_type release() noexcept override { return _base::release(); }
- virtual result<uint8_t> do_io_handle_register(io_handle *h) noexcept override
+ virtual result<uint8_t> do_byte_io_handle_register(byte_io_handle *h) noexcept override
{
windows_nt_kernel::init();
using namespace windows_nt_kernel;
LLFIO_LOG_FUNCTION_CALL(this);
+ if(!h->is_kernel_handle())
+ {
+ return errc::bad_file_descriptor;
+ }
IO_STATUS_BLOCK isb = make_iostatus();
FILE_COMPLETION_INFORMATION fci{};
memset(&fci, 0, sizeof(fci));
@@ -140,7 +159,7 @@ namespace test
// we successfully executed this
return SetFileCompletionNotificationModes(h->native_handle().h, FILE_SKIP_COMPLETION_PORT_ON_SUCCESS | FILE_SKIP_SET_EVENT_ON_HANDLE) ? (uint8_t) 1 : (uint8_t) 0;
}
- virtual result<void> do_io_handle_deregister(io_handle *h) noexcept override
+ virtual result<void> do_byte_io_handle_deregister(byte_io_handle *h) noexcept override
{
windows_nt_kernel::init();
using namespace windows_nt_kernel;
@@ -161,10 +180,10 @@ namespace test
}
return success();
}
- // virtual size_t do_io_handle_max_buffers(const io_handle *h) const noexcept override {}
- // virtual result<registered_buffer_type> do_io_handle_allocate_registered_buffer(io_handle *h, size_t &bytes) noexcept override {}
+ // virtual size_t do_byte_io_handle_max_buffers(const byte_io_handle *h) const noexcept override {}
+ // virtual result<registered_buffer_type> do_byte_io_handle_allocate_registered_buffer(byte_io_handle *h, size_t &bytes) noexcept override {}
virtual std::pair<size_t, size_t> io_state_requirements() noexcept override { return {sizeof(_iocp_operation_state), alignof(_iocp_operation_state)}; }
- virtual io_operation_state *construct(span<byte> storage, io_handle *_h, io_operation_state_visitor *_visitor, registered_buffer_type &&b, deadline d, io_request<buffers_type> reqs) noexcept override
+ virtual io_operation_state *construct(span<byte> storage, byte_io_handle *_h, io_operation_state_visitor *_visitor, registered_buffer_type &&b, deadline d, io_request<buffers_type> reqs) noexcept override
{
assert(storage.size() >= sizeof(_iocp_operation_state));
// assert(((uintptr_t) storage.data() & (_iocp_operation_state_alignment - 1)) == 0);
@@ -174,7 +193,7 @@ namespace test
}
return new(storage.data()) _iocp_operation_state(_h, _visitor, std::move(b), d, std::move(reqs));
}
- virtual io_operation_state *construct(span<byte> storage, io_handle *_h, io_operation_state_visitor *_visitor, registered_buffer_type &&b, deadline d, io_request<const_buffers_type> reqs) noexcept override
+ virtual io_operation_state *construct(span<byte> storage, byte_io_handle *_h, io_operation_state_visitor *_visitor, registered_buffer_type &&b, deadline d, io_request<const_buffers_type> reqs) noexcept override
{
assert(storage.size() >= sizeof(_iocp_operation_state));
// assert(((uintptr_t) storage.data() & (_iocp_operation_state_alignment - 1)) == 0);
@@ -184,7 +203,7 @@ namespace test
}
return new(storage.data()) _iocp_operation_state(_h, _visitor, std::move(b), d, std::move(reqs));
}
- virtual io_operation_state *construct(span<byte> storage, io_handle *_h, io_operation_state_visitor *_visitor, registered_buffer_type &&b, deadline d, io_request<const_buffers_type> reqs, barrier_kind kind) noexcept override
+ virtual io_operation_state *construct(span<byte> storage, byte_io_handle *_h, io_operation_state_visitor *_visitor, registered_buffer_type &&b, deadline d, io_request<const_buffers_type> reqs, barrier_kind kind) noexcept override
{
assert(storage.size() >= sizeof(_iocp_operation_state));
// assert(((uintptr_t) storage.data() & (_iocp_operation_state_alignment - 1)) == 0);
@@ -206,7 +225,7 @@ namespace test
abort();
case io_operation_state_type::read_initialised:
{
- io_handle::io_result<io_handle::buffers_type> ret(state->payload.noncompleted.params.read.reqs.buffers);
+ byte_io_handle::io_result<byte_io_handle::buffers_type> ret(state->payload.noncompleted.params.read.reqs.buffers);
if(do_read_write<false>(ret, NtReadFile, state->h->native_handle(), state, state->_ols, state->payload.noncompleted.params.read.reqs, state->payload.noncompleted.d))
{
// Completed immediately
@@ -229,7 +248,7 @@ namespace test
}
case io_operation_state_type::write_initialised:
{
- io_handle::io_result<io_handle::const_buffers_type> ret(state->payload.noncompleted.params.write.reqs.buffers);
+ byte_io_handle::io_result<byte_io_handle::const_buffers_type> ret(state->payload.noncompleted.params.write.reqs.buffers);
if(do_read_write<false>(ret, NtWriteFile, state->h->native_handle(), state, state->_ols, state->payload.noncompleted.params.write.reqs, state->payload.noncompleted.d))
{
// Completed immediately
@@ -520,7 +539,7 @@ namespace test
}
};
- LLFIO_HEADERS_ONLY_FUNC_SPEC result<io_multiplexer_ptr> multiplexer_win_iocp(size_t threads, bool disable_immediate_completions) noexcept
+ LLFIO_HEADERS_ONLY_FUNC_SPEC result<byte_io_multiplexer_ptr> multiplexer_win_iocp(size_t threads, bool disable_immediate_completions) noexcept
{
try
{
diff --git a/include/llfio/v2.0/dynamic_thread_pool_group.hpp b/include/llfio/v2.0/dynamic_thread_pool_group.hpp
index 09828a6e..0db7caa0 100644
--- a/include/llfio/v2.0/dynamic_thread_pool_group.hpp
+++ b/include/llfio/v2.0/dynamic_thread_pool_group.hpp
@@ -38,7 +38,7 @@ Distributed under the Boost Software License, Version 1.0.
LLFIO_V2_NAMESPACE_EXPORT_BEGIN
class dynamic_thread_pool_group_impl;
-class io_handle;
+class byte_io_handle;
namespace detail
{
@@ -67,7 +67,7 @@ must be careful as there are gotchas. For non-seekable i/o, it is very possible
that there could be 100k handles upon which we do i/o. Doing i/o on
100k handles using a dynamic thread pool would in theory cause the creation
of 100k kernel threads, which would not be wise. A much better solution is
-to use an `io_multiplexer` to await changes in large sets of i/o handles.
+to use an `byte_io_multiplexer` to await changes in large sets of i/o handles.
For seekable i/o, the same problem applies, but worse again: an i/o bound problem
would cause a rapid increase in the number of kernel threads, which by
@@ -363,10 +363,10 @@ public:
uint32_t max_iosinprogress{32};
#endif
//! Information about an i/o handle this work item will use
- struct io_handle_awareness
+ struct byte_io_handle_awareness
{
//! An i/o handle this work item will use
- io_handle *h{nullptr};
+ byte_io_handle *h{nullptr};
//! The relative amount of reading done by this work item from the handle.
float reads{0};
//! The relative amount of writing done by this work item to the handle.
@@ -378,7 +378,7 @@ public:
};
private:
- const span<io_handle_awareness> _handles;
+ const span<byte_io_handle_awareness> _handles;
LLFIO_HEADERS_ONLY_VIRTUAL_SPEC intptr_t next(deadline &d) noexcept override final;
@@ -395,7 +395,7 @@ public:
each with `reads/writes/barriers = 200/100/0` on entry would have `0.22/0.11/0.0`
each after construction.
*/
- explicit LLFIO_HEADERS_ONLY_MEMFUNC_SPEC io_aware_work_item(span<io_handle_awareness> hs);
+ explicit LLFIO_HEADERS_ONLY_MEMFUNC_SPEC io_aware_work_item(span<byte_io_handle_awareness> hs);
io_aware_work_item(io_aware_work_item &&o) noexcept
: work_item(std::move(o))
, _handles(o._handles)
@@ -404,7 +404,7 @@ public:
LLFIO_HEADERS_ONLY_MEMFUNC_SPEC ~io_aware_work_item();
//! The handles originally registered during construction.
- span<io_handle_awareness> handles() const noexcept { return _handles; }
+ span<byte_io_handle_awareness> handles() const noexcept { return _handles; }
/*! \brief As for `work_item::next()`, but deadline may be extended to
reduce i/o congestion on the hardware devices to which the handles
diff --git a/include/llfio/v2.0/fast_random_file_handle.hpp b/include/llfio/v2.0/fast_random_file_handle.hpp
index e710fc3a..d121e0f6 100644
--- a/include/llfio/v2.0/fast_random_file_handle.hpp
+++ b/include/llfio/v2.0/fast_random_file_handle.hpp
@@ -85,19 +85,19 @@ public:
using dev_t = file_handle::dev_t;
using ino_t = file_handle::ino_t;
using path_view_type = file_handle::path_view_type;
- using path_type = io_handle::path_type;
- using extent_type = io_handle::extent_type;
- using size_type = io_handle::size_type;
- using mode = io_handle::mode;
- using creation = io_handle::creation;
- using caching = io_handle::caching;
- using flag = io_handle::flag;
- using buffer_type = io_handle::buffer_type;
- using const_buffer_type = io_handle::const_buffer_type;
- using buffers_type = io_handle::buffers_type;
- using const_buffers_type = io_handle::const_buffers_type;
- template <class T> using io_request = io_handle::io_request<T>;
- template <class T> using io_result = io_handle::io_result<T>;
+ using path_type = byte_io_handle::path_type;
+ using extent_type = byte_io_handle::extent_type;
+ using size_type = byte_io_handle::size_type;
+ using mode = byte_io_handle::mode;
+ using creation = byte_io_handle::creation;
+ using caching = byte_io_handle::caching;
+ using flag = byte_io_handle::flag;
+ using buffer_type = byte_io_handle::buffer_type;
+ using const_buffer_type = byte_io_handle::const_buffer_type;
+ using buffers_type = byte_io_handle::buffers_type;
+ using const_buffers_type = byte_io_handle::const_buffers_type;
+ template <class T> using io_request = byte_io_handle::io_request<T>;
+ template <class T> using io_result = byte_io_handle::io_result<T>;
protected:
struct prng : public QUICKCPPLIB_NAMESPACE::algorithm::small_prng::small_prng
diff --git a/include/llfio/v2.0/file_handle.hpp b/include/llfio/v2.0/file_handle.hpp
index 995a65dc..034ed8a5 100644
--- a/include/llfio/v2.0/file_handle.hpp
+++ b/include/llfio/v2.0/file_handle.hpp
@@ -25,7 +25,7 @@ Distributed under the Boost Software License, Version 1.0.
#ifndef LLFIO_FILE_HANDLE_H
#define LLFIO_FILE_HANDLE_H
-#include "lockable_io_handle.hpp"
+#include "lockable_byte_io_handle.hpp"
#include "path_discovery.hpp"
#include "utils.hpp"
@@ -49,24 +49,24 @@ with uncached i/o</td></tr>
</table>
*/
-class LLFIO_DECL file_handle : public lockable_io_handle, public fs_handle
+class LLFIO_DECL file_handle : public lockable_byte_io_handle, public fs_handle
{
LLFIO_HEADERS_ONLY_VIRTUAL_SPEC const handle &_get_handle() const noexcept final { return *this; }
public:
- using path_type = io_handle::path_type;
- using extent_type = io_handle::extent_type;
- using size_type = io_handle::size_type;
- using mode = io_handle::mode;
- using creation = io_handle::creation;
- using caching = io_handle::caching;
- using flag = io_handle::flag;
- using buffer_type = io_handle::buffer_type;
- using const_buffer_type = io_handle::const_buffer_type;
- using buffers_type = io_handle::buffers_type;
- using const_buffers_type = io_handle::const_buffers_type;
- template <class T> using io_request = io_handle::io_request<T>;
- template <class T> using io_result = io_handle::io_result<T>;
+ using path_type = byte_io_handle::path_type;
+ using extent_type = byte_io_handle::extent_type;
+ using size_type = byte_io_handle::size_type;
+ using mode = byte_io_handle::mode;
+ using creation = byte_io_handle::creation;
+ using caching = byte_io_handle::caching;
+ using flag = byte_io_handle::flag;
+ using buffer_type = byte_io_handle::buffer_type;
+ using const_buffer_type = byte_io_handle::const_buffer_type;
+ using buffers_type = byte_io_handle::buffers_type;
+ using const_buffers_type = byte_io_handle::const_buffers_type;
+ template <class T> using io_request = byte_io_handle::io_request<T>;
+ template <class T> using io_result = byte_io_handle::io_result<T>;
using dev_t = fs_handle::dev_t;
using ino_t = fs_handle::ino_t;
using path_view_type = fs_handle::path_view_type;
@@ -75,14 +75,14 @@ public:
//! Default constructor
constexpr file_handle() {} // NOLINT
//! Construct a handle from a supplied native handle
- constexpr file_handle(native_handle_type h, dev_t devid, ino_t inode, caching caching, flag flags, io_multiplexer *ctx)
- : lockable_io_handle(std::move(h), caching, flags, ctx)
+ constexpr file_handle(native_handle_type h, dev_t devid, ino_t inode, caching caching, flag flags, byte_io_multiplexer *ctx)
+ : lockable_byte_io_handle(std::move(h), caching, flags, ctx)
, fs_handle(devid, inode)
{
}
//! Construct a handle from a supplied native handle
- constexpr file_handle(native_handle_type h, caching caching, flag flags, io_multiplexer *ctx)
- : lockable_io_handle(std::move(h), caching, flags, ctx)
+ constexpr file_handle(native_handle_type h, caching caching, flag flags, byte_io_multiplexer *ctx)
+ : lockable_byte_io_handle(std::move(h), caching, flags, ctx)
{
}
//! No copy construction (use clone())
@@ -91,30 +91,30 @@ public:
file_handle &operator=(const file_handle &) = delete;
//! Implicit move construction of file_handle permitted
constexpr file_handle(file_handle &&o) noexcept
- : lockable_io_handle(std::move(o))
+ : lockable_byte_io_handle(std::move(o))
, fs_handle(std::move(o))
{
}
//! Explicit conversion from handle permitted
- explicit constexpr file_handle(handle &&o, dev_t devid, ino_t inode, io_multiplexer *ctx) noexcept
- : lockable_io_handle(std::move(o), ctx)
+ explicit constexpr file_handle(handle &&o, dev_t devid, ino_t inode, byte_io_multiplexer *ctx) noexcept
+ : lockable_byte_io_handle(std::move(o), ctx)
, fs_handle(devid, inode)
{
}
//! Explicit conversion from handle permitted
- explicit constexpr file_handle(handle &&o, io_multiplexer *ctx) noexcept
- : lockable_io_handle(std::move(o), ctx)
+ explicit constexpr file_handle(handle &&o, byte_io_multiplexer *ctx) noexcept
+ : lockable_byte_io_handle(std::move(o), ctx)
{
}
- //! Explicit conversion from io_handle permitted
- explicit constexpr file_handle(io_handle &&o, dev_t devid, ino_t inode) noexcept
- : lockable_io_handle(std::move(o))
+ //! Explicit conversion from byte_io_handle permitted
+ explicit constexpr file_handle(byte_io_handle &&o, dev_t devid, ino_t inode) noexcept
+ : lockable_byte_io_handle(std::move(o))
, fs_handle(devid, inode)
{
}
- //! Explicit conversion from io_handle permitted
- explicit constexpr file_handle(io_handle &&o) noexcept
- : lockable_io_handle(std::move(o))
+ //! Explicit conversion from byte_io_handle permitted
+ explicit constexpr file_handle(byte_io_handle &&o) noexcept
+ : lockable_byte_io_handle(std::move(o))
{
}
//! Move assignment of file_handle permitted
@@ -246,7 +246,7 @@ public:
_v.behaviour |= native_handle_type::disposition::_child_close_executed;
}
#endif
- return io_handle::close();
+ return byte_io_handle::close();
}
/*! Reopen this handle (copy constructor is disabled to avoid accidental copying),
@@ -351,11 +351,11 @@ public:
*/
LLFIO_MAKE_FREE_FUNCTION
LLFIO_HEADERS_ONLY_VIRTUAL_SPEC
- result<extent_pair> clone_extents_to(extent_pair extent, io_handle &dest, io_handle::extent_type destoffset, deadline d = {}, bool force_copy_now = false,
+ result<extent_pair> clone_extents_to(extent_pair extent, byte_io_handle &dest, byte_io_handle::extent_type destoffset, deadline d = {}, bool force_copy_now = false,
bool emulate_if_unsupported = true) noexcept;
//! \overload
LLFIO_MAKE_FREE_FUNCTION
- result<extent_pair> clone_extents_to(io_handle &dest, deadline d = {}, bool force_copy_now = false, bool emulate_if_unsupported = true) noexcept
+ result<extent_pair> clone_extents_to(byte_io_handle &dest, deadline d = {}, bool force_copy_now = false, bool emulate_if_unsupported = true) noexcept
{
return clone_extents_to({(extent_type)-1, (extent_type)-1}, dest, 0, d, force_copy_now, emulate_if_unsupported);
}
diff --git a/include/llfio/v2.0/handle.hpp b/include/llfio/v2.0/handle.hpp
index 22046e4b..b37b9ab0 100644
--- a/include/llfio/v2.0/handle.hpp
+++ b/include/llfio/v2.0/handle.hpp
@@ -1,5 +1,5 @@
/* A handle to something
-(C) 2015-2019 Niall Douglas <http://www.nedproductions.biz/> (20 commits)
+(C) 2015-2021 Niall Douglas <http://www.nedproductions.biz/> (20 commits)
File Created: Dec 2015
@@ -376,6 +376,8 @@ public:
bool is_seekable() const noexcept { return _v.is_seekable(); }
//! True if requires aligned i/o
bool requires_aligned_io() const noexcept { return _v.requires_aligned_io(); }
+ //! True if `native_handle()` is a valid kernel handle
+ bool is_kernel_handle() const noexcept { return _v.is_kernel_handle(); }
//! True if a regular file or device
bool is_regular() const noexcept { return _v.is_regular(); }
@@ -397,6 +399,10 @@ public:
bool is_allocation() const noexcept { return _v.is_allocation(); }
//! True if a path or a directory
bool is_path() const noexcept { return _v.is_path(); }
+ //! True if a TLS socket
+ bool is_tls_socket() const noexcept { return _v.is_tls_socket(); }
+ //! True if a HTTP socket
+ bool is_http_socket() const noexcept { return _v.is_http_socket(); }
//! Kernel cache strategy used by this handle
caching kernel_caching() const noexcept
diff --git a/include/llfio/v2.0/llfio.hpp b/include/llfio/v2.0/llfio.hpp
index 9ecf3845..d6f6ed92 100644
--- a/include/llfio/v2.0/llfio.hpp
+++ b/include/llfio/v2.0/llfio.hpp
@@ -69,6 +69,9 @@ import LLFIO_MODULE_NAME;
#include "fast_random_file_handle.hpp"
#include "file_handle.hpp"
#include "process_handle.hpp"
+#ifndef LLFIO_EXCLUDE_NETWORKING
+#include "byte_socket_handle.hpp"
+#endif
#include "statfs.hpp"
#ifdef LLFIO_INCLUDE_STORAGE_PROFILE
#include "storage_profile.hpp"
diff --git a/include/llfio/v2.0/lockable_io_handle.hpp b/include/llfio/v2.0/lockable_byte_io_handle.hpp
index ae46c671..399b23a3 100644
--- a/include/llfio/v2.0/lockable_io_handle.hpp
+++ b/include/llfio/v2.0/lockable_byte_io_handle.hpp
@@ -25,11 +25,11 @@ Distributed under the Boost Software License, Version 1.0.
#ifndef LLFIO_LOCKABLE_IO_HANDLE_H
#define LLFIO_LOCKABLE_IO_HANDLE_H
-#include "io_handle.hpp"
+#include "byte_io_handle.hpp"
#include <cassert>
-//! \file lockable_io_handle.hpp Provides a lockable i/o handle
+//! \file lockable_byte_io_handle.hpp Provides a lockable i/o handle
#ifdef _MSC_VER
#pragma warning(push)
@@ -46,7 +46,7 @@ enum class lock_kind
exclusive //!< Exclude those requesting any kind of lock on the same inode.
};
-/*! \class lockable_io_handle
+/*! \class lockable_byte_io_handle
\brief A handle to something capable of scatter-gather i/o and which can exclude other concurrent users.
Models `SharedMutex`, though note that the locks are per-handle, not per-thread.
@@ -54,50 +54,50 @@ Models `SharedMutex`, though note that the locks are per-handle, not per-thread.
emulation of advisory whole-file locking. This causes byte range locks to work (probably) independently
of these locks.
*/
-class LLFIO_DECL lockable_io_handle : public io_handle
+class LLFIO_DECL lockable_byte_io_handle : public byte_io_handle
{
public:
- using path_type = io_handle::path_type;
- using extent_type = io_handle::extent_type;
- using size_type = io_handle::size_type;
- using mode = io_handle::mode;
- using creation = io_handle::creation;
- using caching = io_handle::caching;
- using flag = io_handle::flag;
- using buffer_type = io_handle::buffer_type;
- using const_buffer_type = io_handle::const_buffer_type;
- using buffers_type = io_handle::buffers_type;
- using const_buffers_type = io_handle::const_buffers_type;
- template <class T> using io_request = io_handle::io_request<T>;
- template <class T> using io_result = io_handle::io_result<T>;
+ using path_type = byte_io_handle::path_type;
+ using extent_type = byte_io_handle::extent_type;
+ using size_type = byte_io_handle::size_type;
+ using mode = byte_io_handle::mode;
+ using creation = byte_io_handle::creation;
+ using caching = byte_io_handle::caching;
+ using flag = byte_io_handle::flag;
+ using buffer_type = byte_io_handle::buffer_type;
+ using const_buffer_type = byte_io_handle::const_buffer_type;
+ using buffers_type = byte_io_handle::buffers_type;
+ using const_buffers_type = byte_io_handle::const_buffers_type;
+ template <class T> using io_request = byte_io_handle::io_request<T>;
+ template <class T> using io_result = byte_io_handle::io_result<T>;
public:
//! Default constructor
- constexpr lockable_io_handle() {} // NOLINT
- ~lockable_io_handle() = default;
+ constexpr lockable_byte_io_handle() {} // NOLINT
+ ~lockable_byte_io_handle() = default;
//! Construct a handle from a supplied native handle
- constexpr explicit lockable_io_handle(native_handle_type h, caching caching, flag flags, io_multiplexer *ctx)
- : io_handle(h, caching, flags, ctx)
+ constexpr explicit lockable_byte_io_handle(native_handle_type h, caching caching, flag flags, byte_io_multiplexer *ctx)
+ : byte_io_handle(h, caching, flags, ctx)
{
}
//! Explicit conversion from `handle` permitted
- explicit constexpr lockable_io_handle(handle &&o, io_multiplexer *ctx) noexcept
- : io_handle(std::move(o), ctx)
+ explicit constexpr lockable_byte_io_handle(handle &&o, byte_io_multiplexer *ctx) noexcept
+ : byte_io_handle(std::move(o), ctx)
{
}
- //! Explicit conversion from `io_handle` permitted
- explicit constexpr lockable_io_handle(io_handle &&o) noexcept
- : io_handle(std::move(o))
+ //! Explicit conversion from `byte_io_handle` permitted
+ explicit constexpr lockable_byte_io_handle(byte_io_handle &&o) noexcept
+ : byte_io_handle(std::move(o))
{
}
//! Move construction permitted
- lockable_io_handle(lockable_io_handle &&) = default;
+ lockable_byte_io_handle(lockable_byte_io_handle &&) = default;
//! No copy construction (use `clone()`)
- lockable_io_handle(const lockable_io_handle &) = delete;
+ lockable_byte_io_handle(const lockable_byte_io_handle &) = delete;
//! Move assignment permitted
- lockable_io_handle &operator=(lockable_io_handle &&) = default;
+ lockable_byte_io_handle &operator=(lockable_byte_io_handle &&) = default;
//! No copy assignment
- lockable_io_handle &operator=(const lockable_io_handle &) = delete;
+ lockable_byte_io_handle &operator=(const lockable_byte_io_handle &) = delete;
/*! \brief Locks the inode referred to by the open handle for exclusive access.
@@ -147,13 +147,13 @@ public:
*/
class extent_guard
{
- friend class lockable_io_handle;
- lockable_io_handle *_h{nullptr};
+ friend class lockable_byte_io_handle;
+ lockable_byte_io_handle *_h{nullptr};
extent_type _offset{0}, _length{0};
lock_kind _kind{lock_kind::unlocked};
protected:
- constexpr extent_guard(lockable_io_handle *h, extent_type offset, extent_type length, lock_kind kind)
+ constexpr extent_guard(lockable_byte_io_handle *h, extent_type offset, extent_type length, lock_kind kind)
: _h(h)
, _offset(offset)
, _length(length)
@@ -201,10 +201,10 @@ public:
//! True if extent guard is valid
explicit operator bool() const noexcept { return _h != nullptr; }
- //! The `lockable_io_handle` to be unlocked
- lockable_io_handle *handle() const noexcept { return _h; }
- //! Sets the `lockable_io_handle` to be unlocked
- void set_handle(lockable_io_handle *h) noexcept { _h = h; }
+ //! The `lockable_byte_io_handle` to be unlocked
+ lockable_byte_io_handle *handle() const noexcept { return _h; }
+ //! Sets the `lockable_byte_io_handle` to be unlocked
+ void set_handle(lockable_byte_io_handle *h) noexcept { _h = h; }
//! The extent to be unlocked
std::tuple<extent_type, extent_type, lock_kind> extent() const noexcept { return std::make_tuple(_offset, _length, _kind); }
@@ -307,16 +307,16 @@ public:
LLFIO_HEADERS_ONLY_VIRTUAL_SPEC void unlock_file_range(extent_type offset, extent_type bytes) noexcept;
};
-/*! \brief RAII locker matching `std::unique_lock` for `lockable_io_handle`, but untemplated.
+/*! \brief RAII locker matching `std::unique_lock` for `lockable_byte_io_handle`, but untemplated.
*/
class unique_file_lock
{
- lockable_io_handle *_h{nullptr};
+ lockable_byte_io_handle *_h{nullptr};
lock_kind _lock_state{lock_kind::unlocked};
public:
unique_file_lock() = default;
- explicit unique_file_lock(lockable_io_handle &h, lock_kind kind)
+ explicit unique_file_lock(lockable_byte_io_handle &h, lock_kind kind)
: _h(&h)
{
if(kind == lock_kind::exclusive)
@@ -364,16 +364,16 @@ public:
}
//! Returns the associated mutex
- lockable_io_handle *mutex() const noexcept { return _h; }
+ lockable_byte_io_handle *mutex() const noexcept { return _h; }
//! True if the associated mutex is owned by this lock
bool owns_lock() const noexcept { return (_h != nullptr) && _lock_state != lock_kind::unlocked; }
//! True if the associated mutex is owned by this lock
explicit operator bool() const noexcept { return owns_lock(); }
//! Releases the mutex from management
- lockable_io_handle *release() noexcept
+ lockable_byte_io_handle *release() noexcept
{
- lockable_io_handle *ret = _h;
+ lockable_byte_io_handle *ret = _h;
_h = nullptr;
return ret;
}
@@ -480,9 +480,9 @@ LLFIO_V2_NAMESPACE_END
#if LLFIO_HEADERS_ONLY == 1 && !defined(DOXYGEN_SHOULD_SKIP_THIS)
#define LLFIO_INCLUDED_BY_HEADER 1
#ifdef _WIN32
-#include "detail/impl/windows/lockable_io_handle.ipp"
+#include "detail/impl/windows/lockable_byte_io_handle.ipp"
#else
-#include "detail/impl/posix/lockable_io_handle.ipp"
+#include "detail/impl/posix/lockable_byte_io_handle.ipp"
#endif
#undef LLFIO_INCLUDED_BY_HEADER
#endif
diff --git a/include/llfio/v2.0/map_handle.hpp b/include/llfio/v2.0/map_handle.hpp
index 3962be16..b49d771e 100644
--- a/include/llfio/v2.0/map_handle.hpp
+++ b/include/llfio/v2.0/map_handle.hpp
@@ -292,10 +292,10 @@ Upon return, one knows that memory in the returned buffer has been barriered
(it may be empty if there is no support for this operation in LLFIO, or if the current CPU does not
support this operation). You may find the `is_nvram()` observer of particular use here.
*/
-inline io_handle::const_buffer_type nvram_barrier(io_handle::const_buffer_type req, bool evict = false) noexcept
+inline byte_io_handle::const_buffer_type nvram_barrier(byte_io_handle::const_buffer_type req, bool evict = false) noexcept
{
- auto *tp = (io_handle::const_buffer_type::pointer) (((uintptr_t) req.data()) & ~63);
- io_handle::const_buffer_type ret{tp, (size_t) (req.data() + 63 + req.size() - tp) & ~63};
+ auto *tp = (byte_io_handle::const_buffer_type::pointer) (((uintptr_t) req.data()) & ~63);
+ byte_io_handle::const_buffer_type ret{tp, (size_t) (req.data() + 63 + req.size() - tp) & ~63};
if(memory_flush_none == mem_flush_stores(ret.data(), ret.size(), evict ? memory_flush_evict : memory_flush_retain))
{
ret = {tp, 0};
@@ -399,7 +399,7 @@ address space you need to manually call `map_handle::trim_cache()` from time to
## Barriers:
-`map_handle`, because it implements `io_handle`, implements `barrier()` in a very conservative way
+`map_handle`, because it implements `byte_io_handle`, implements `barrier()` in a very conservative way
to account for OS differences i.e. it calls `msync()`, and then the `barrier()` implementation for the backing file
(probably `fsync()` or equivalent on most platforms, which synchronises the entire file).
@@ -468,23 +468,23 @@ not be used when a large page allocation was requested.
\sa `mapped_file_handle`, `algorithm::mapped_span`
*/
-class LLFIO_DECL map_handle : public lockable_io_handle
+class LLFIO_DECL map_handle : public lockable_byte_io_handle
{
friend class mapped_file_handle;
public:
- using extent_type = io_handle::extent_type;
- using size_type = io_handle::size_type;
- using mode = io_handle::mode;
- using creation = io_handle::creation;
- using caching = io_handle::caching;
- using flag = io_handle::flag;
- using buffer_type = io_handle::buffer_type;
- using const_buffer_type = io_handle::const_buffer_type;
- using buffers_type = io_handle::buffers_type;
- using const_buffers_type = io_handle::const_buffers_type;
- template <class T> using io_request = io_handle::io_request<T>;
- template <class T> using io_result = io_handle::io_result<T>;
+ using extent_type = byte_io_handle::extent_type;
+ using size_type = byte_io_handle::size_type;
+ using mode = byte_io_handle::mode;
+ using creation = byte_io_handle::creation;
+ using caching = byte_io_handle::caching;
+ using flag = byte_io_handle::flag;
+ using buffer_type = byte_io_handle::buffer_type;
+ using const_buffer_type = byte_io_handle::const_buffer_type;
+ using buffers_type = byte_io_handle::buffers_type;
+ using const_buffers_type = byte_io_handle::const_buffers_type;
+ template <class T> using io_request = byte_io_handle::io_request<T>;
+ template <class T> using io_result = byte_io_handle::io_result<T>;
protected:
section_handle *_section{nullptr};
@@ -525,7 +525,7 @@ public:
}
//! Implicit move construction of map_handle permitted
constexpr map_handle(map_handle &&o) noexcept
- : lockable_io_handle(std::move(o))
+ : lockable_byte_io_handle(std::move(o))
, _section(o._section)
, _addr(o._addr)
, _offset(o._offset)
@@ -884,7 +884,7 @@ public:
\mallocs None.
*/
#endif
- using io_handle::read;
+ using byte_io_handle::read;
#if 0
/*! \brief Write data to the mapped view.
@@ -911,7 +911,7 @@ public:
\mallocs None if a `QUICKCPPLIB_NAMESPACE::signal_guard_install` is already instanced.
*/
#endif
- using io_handle::write;
+ using byte_io_handle::write;
};
//! \brief Constructor for `map_handle`
@@ -950,22 +950,22 @@ template <class T> constexpr inline span<T> in_place_attach(map_handle &mh) noex
namespace detail
{
- inline result<io_handle::registered_buffer_type> map_handle_allocate_registered_buffer(size_t &bytes) noexcept
+ inline result<byte_io_handle::registered_buffer_type> map_handle_allocate_registered_buffer(size_t &bytes) noexcept
{
try
{
- auto make_shared = [](map_handle h) -> io_handle::registered_buffer_type
+ auto make_shared = [](map_handle h) -> byte_io_handle::registered_buffer_type
{
- struct registered_buffer_type_indirect : io_multiplexer::_registered_buffer_type
+ struct registered_buffer_type_indirect : byte_io_multiplexer::_registered_buffer_type
{
map_handle h;
registered_buffer_type_indirect(map_handle _h)
- : io_multiplexer::_registered_buffer_type(_h.as_span())
+ : byte_io_multiplexer::_registered_buffer_type(_h.as_span())
, h(std::move(_h))
{
}
};
- return io_handle::registered_buffer_type(std::make_shared<registered_buffer_type_indirect>(std::move(h)));
+ return byte_io_handle::registered_buffer_type(std::make_shared<registered_buffer_type_indirect>(std::move(h)));
};
const auto &page_sizes = utils::page_sizes(true);
size_t idx = 0;
@@ -1019,8 +1019,8 @@ namespace detail
}
} // namespace detail
-// Implement io_handle::_do_allocate_registered_buffer()
-inline result<io_handle::registered_buffer_type> io_handle::_do_allocate_registered_buffer(size_t &bytes) noexcept
+// Implement byte_io_handle::_do_allocate_registered_buffer()
+inline result<byte_io_handle::registered_buffer_type> byte_io_handle::_do_allocate_registered_buffer(size_t &bytes) noexcept
{
return detail::map_handle_allocate_registered_buffer(bytes);
}
diff --git a/include/llfio/v2.0/mapped_file_handle.hpp b/include/llfio/v2.0/mapped_file_handle.hpp
index 423ef906..8c06a802 100644
--- a/include/llfio/v2.0/mapped_file_handle.hpp
+++ b/include/llfio/v2.0/mapped_file_handle.hpp
@@ -170,19 +170,19 @@ public:
using dev_t = file_handle::dev_t;
using ino_t = file_handle::ino_t;
using path_view_type = file_handle::path_view_type;
- using path_type = io_handle::path_type;
- using extent_type = io_handle::extent_type;
- using size_type = io_handle::size_type;
- using mode = io_handle::mode;
- using creation = io_handle::creation;
- using caching = io_handle::caching;
- using flag = io_handle::flag;
- using buffer_type = io_handle::buffer_type;
- using const_buffer_type = io_handle::const_buffer_type;
- using buffers_type = io_handle::buffers_type;
- using const_buffers_type = io_handle::const_buffers_type;
- template <class T> using io_request = io_handle::io_request<T>;
- template <class T> using io_result = io_handle::io_result<T>;
+ using path_type = byte_io_handle::path_type;
+ using extent_type = byte_io_handle::extent_type;
+ using size_type = byte_io_handle::size_type;
+ using mode = byte_io_handle::mode;
+ using creation = byte_io_handle::creation;
+ using caching = byte_io_handle::caching;
+ using flag = byte_io_handle::flag;
+ using buffer_type = byte_io_handle::buffer_type;
+ using const_buffer_type = byte_io_handle::const_buffer_type;
+ using buffers_type = byte_io_handle::buffers_type;
+ using const_buffers_type = byte_io_handle::const_buffers_type;
+ template <class T> using io_request = byte_io_handle::io_request<T>;
+ template <class T> using io_result = byte_io_handle::io_result<T>;
protected:
size_type _reservation{0};
@@ -580,7 +580,7 @@ public:
}
}
LLFIO_DEADLINE_TRY_FOR_UNTIL(reopen)
- LLFIO_HEADERS_ONLY_VIRTUAL_SPEC result<void> set_multiplexer(io_multiplexer *c = this_thread::multiplexer()) noexcept override
+ LLFIO_HEADERS_ONLY_VIRTUAL_SPEC result<void> set_multiplexer(byte_io_multiplexer *c = this_thread::multiplexer()) noexcept override
{
OUTCOME_TRY(file_handle::set_multiplexer(c));
return _mh.set_multiplexer(file_handle::multiplexer());
diff --git a/include/llfio/v2.0/multiplex.hpp b/include/llfio/v2.0/multiplex.hpp
index 070d4fae..dd257703 100644
--- a/include/llfio/v2.0/multiplex.hpp
+++ b/include/llfio/v2.0/multiplex.hpp
@@ -37,11 +37,11 @@ template <class IoHandleType> class read_receiver
{
public:
//! The i/o handle type this read receiver is for
- using io_handle_type = IoHandleType;
+ using byte_io_handle_type = IoHandleType;
//! The buffers type this receiver receives
- using buffers_type = typename io_handle_type::buffers_type;
+ using buffers_type = typename byte_io_handle_type::buffers_type;
//! The successful read result type this receiver receives
- using value_type = typename io_handle_type::io_result<buffers_type>;
+ using value_type = typename byte_io_handle_type::io_result<buffers_type>;
//! The failure result type this receiver receives
using error_type = typename value_type::error_type;
@@ -59,11 +59,11 @@ template <class IoHandleType> class write_receiver
{
public:
//! The i/o handle type this read receiver is for
- using io_handle_type = IoHandleType;
+ using byte_io_handle_type = IoHandleType;
//! The buffers type this receiver receives
- using buffers_type = typename io_handle_type::const_buffers_type;
+ using buffers_type = typename byte_io_handle_type::const_buffers_type;
//! The successful write result type this receiver receives
- using value_type = typename io_handle_type::io_result<buffers_type>;
+ using value_type = typename byte_io_handle_type::io_result<buffers_type>;
//! The failure result type this receiver receives
using error_type = typename value_type::error_type;
diff --git a/include/llfio/v2.0/native_handle_type.hpp b/include/llfio/v2.0/native_handle_type.hpp
index cd2a42cd..23915d98 100644
--- a/include/llfio/v2.0/native_handle_type.hpp
+++ b/include/llfio/v2.0/native_handle_type.hpp
@@ -52,6 +52,7 @@ struct native_handle_type // NOLINT
nonblocking = 1U << 4U, //!< Requires additional synchronisation (Windows: `OVERLAPPED`; POSIX: `O_NONBLOCK`)
seekable = 1U << 5U, //!< Is seekable
aligned_io = 1U << 6U, //!< Requires sector aligned i/o (typically 512 or 4096)
+ kernel_handle = 1U << 7U, //!< Handle is a valid kernel handle
file = 1U << 8U, //!< Is a regular file
directory = 1U << 9U, //!< Is a directory
@@ -63,6 +64,8 @@ struct native_handle_type // NOLINT
section = 1U << 15U, //!< Is a memory section
allocation = 1U << 16U, //!< Is a memory allocation
path = 1U << 17U, //!< Is a path
+ tls_socket = 1U << 18U, //!< Is a TLS socket
+ http_socket = 1U << 19U, //!< Is a HTTP or HTTPS socket
safety_barriers = 1U << 20U, //!< Issue write reordering barriers at various points
cache_metadata = 1U << 21U, //!< Is serving metadata from the kernel cache
@@ -85,6 +88,8 @@ struct native_handle_type // NOLINT
int pid; // NOLINT
//! A Windows HANDLE
win::handle h; // NOLINT
+ //! A Windows SOCKET
+ win::socket sock;
//! A third party pointer
void *ptr;
};
@@ -166,6 +171,8 @@ struct native_handle_type // NOLINT
constexpr bool is_seekable() const noexcept { return (behaviour & disposition::seekable) ? true : false; }
//! True if requires aligned i/o
constexpr bool requires_aligned_io() const noexcept { return (behaviour & disposition::aligned_io) ? true : false; }
+ //! True if handle is a valid kernel handle
+ constexpr bool is_kernel_handle() const noexcept { return (behaviour & disposition::kernel_handle) ? true : false; }
//! True if a regular file or device
constexpr bool is_regular() const noexcept { return (behaviour & disposition::file) ? true : false; }
@@ -187,6 +194,10 @@ struct native_handle_type // NOLINT
constexpr bool is_allocation() const noexcept { return (behaviour & disposition::allocation) ? true : false; }
//! True if a path or a directory
constexpr bool is_path() const noexcept { return (behaviour & disposition::path) ? true : false; }
+ //! True if a TLS socket
+ constexpr bool is_tls_socket() const noexcept { return (behaviour & disposition::tls_socket) ? true : false; }
+ //! True if a HTTP socket
+ constexpr bool is_http_socket() const noexcept { return (behaviour & disposition::http_socket) ? true : false; }
};
static_assert((sizeof(void *) == 4 && sizeof(native_handle_type) == 8) || (sizeof(void *) == 8 && sizeof(native_handle_type) == 12),
"native_handle_type is not 8 or 12 bytes in size!");
diff --git a/include/llfio/v2.0/pipe_handle.hpp b/include/llfio/v2.0/pipe_handle.hpp
index b41c7698..6dc49496 100644
--- a/include/llfio/v2.0/pipe_handle.hpp
+++ b/include/llfio/v2.0/pipe_handle.hpp
@@ -25,7 +25,7 @@ Distributed under the Boost Software License, Version 1.0.
#ifndef LLFIO_PIPE_HANDLE_H
#define LLFIO_PIPE_HANDLE_H
-#include "io_handle.hpp"
+#include "byte_io_handle.hpp"
//! \file pipe_handle.hpp Provides `pipe_handle`
@@ -83,24 +83,29 @@ So long as you use `path_discovery::temporary_named_pipes_directory()`
as your base directory, you can write quite portable code between POSIX
and Windows.
*/
-class LLFIO_DECL pipe_handle : public io_handle, public fs_handle
+class LLFIO_DECL pipe_handle : public byte_io_handle,
+ public fs_handle
+#ifndef _WIN32
+ ,
+ public pollable_handle
+#endif
{
LLFIO_HEADERS_ONLY_VIRTUAL_SPEC const handle &_get_handle() const noexcept final { return *this; }
public:
- using path_type = io_handle::path_type;
- using extent_type = io_handle::extent_type;
- using size_type = io_handle::size_type;
- using mode = io_handle::mode;
- using creation = io_handle::creation;
- using caching = io_handle::caching;
- using flag = io_handle::flag;
- using buffer_type = io_handle::buffer_type;
- using const_buffer_type = io_handle::const_buffer_type;
- using buffers_type = io_handle::buffers_type;
- using const_buffers_type = io_handle::const_buffers_type;
- template <class T> using io_request = io_handle::io_request<T>;
- template <class T> using io_result = io_handle::io_result<T>;
+ using path_type = byte_io_handle::path_type;
+ using extent_type = byte_io_handle::extent_type;
+ using size_type = byte_io_handle::size_type;
+ using mode = byte_io_handle::mode;
+ using creation = byte_io_handle::creation;
+ using caching = byte_io_handle::caching;
+ using flag = byte_io_handle::flag;
+ using buffer_type = byte_io_handle::buffer_type;
+ using const_buffer_type = byte_io_handle::const_buffer_type;
+ using buffers_type = byte_io_handle::buffers_type;
+ using const_buffers_type = byte_io_handle::const_buffers_type;
+ template <class T> using io_request = byte_io_handle::io_request<T>;
+ template <class T> using io_result = byte_io_handle::io_result<T>;
using dev_t = fs_handle::dev_t;
using ino_t = fs_handle::ino_t;
using path_view_type = fs_handle::path_view_type;
@@ -109,14 +114,14 @@ public:
//! Default constructor
constexpr pipe_handle() {} // NOLINT
//! Construct a handle from a supplied native handle
- constexpr pipe_handle(native_handle_type h, dev_t devid, ino_t inode, caching caching, flag flags, io_multiplexer *ctx)
- : io_handle(std::move(h), caching, flags, ctx)
+ constexpr pipe_handle(native_handle_type h, dev_t devid, ino_t inode, caching caching, flag flags, byte_io_multiplexer *ctx)
+ : byte_io_handle(std::move(h), caching, flags, ctx)
, fs_handle(devid, inode)
{
}
//! Construct a handle from a supplied native handle
- constexpr pipe_handle(native_handle_type h, caching caching, flag flags, io_multiplexer *ctx)
- : io_handle(std::move(h), caching, flags, ctx)
+ constexpr pipe_handle(native_handle_type h, caching caching, flag flags, byte_io_multiplexer *ctx)
+ : byte_io_handle(std::move(h), caching, flags, ctx)
{
}
//! No copy construction (use clone())
@@ -125,30 +130,30 @@ public:
pipe_handle &operator=(const pipe_handle &) = delete;
//! Implicit move construction of `pipe_handle` permitted
constexpr pipe_handle(pipe_handle &&o) noexcept
- : io_handle(std::move(o))
+ : byte_io_handle(std::move(o))
, fs_handle(std::move(o))
{
}
//! Explicit conversion from handle permitted
- explicit constexpr pipe_handle(handle &&o, dev_t devid, ino_t inode, io_multiplexer *ctx) noexcept
- : io_handle(std::move(o), ctx)
+ explicit constexpr pipe_handle(handle &&o, dev_t devid, ino_t inode, byte_io_multiplexer *ctx) noexcept
+ : byte_io_handle(std::move(o), ctx)
, fs_handle(devid, inode)
{
}
//! Explicit conversion from handle permitted
- explicit constexpr pipe_handle(handle &&o, io_multiplexer *ctx) noexcept
- : io_handle(std::move(o), ctx)
+ explicit constexpr pipe_handle(handle &&o, byte_io_multiplexer *ctx) noexcept
+ : byte_io_handle(std::move(o), ctx)
{
}
- //! Explicit conversion from io_handle permitted
- explicit constexpr pipe_handle(io_handle &&o, dev_t devid, ino_t inode) noexcept
- : io_handle(std::move(o))
+ //! Explicit conversion from byte_io_handle permitted
+ explicit constexpr pipe_handle(byte_io_handle &&o, dev_t devid, ino_t inode) noexcept
+ : byte_io_handle(std::move(o))
, fs_handle(devid, inode)
{
}
- //! Explicit conversion from io_handle permitted
- explicit constexpr pipe_handle(io_handle &&o) noexcept
- : io_handle(std::move(o))
+ //! Explicit conversion from byte_io_handle permitted
+ explicit constexpr pipe_handle(byte_io_handle &&o) noexcept
+ : byte_io_handle(std::move(o))
{
}
//! Move assignment of `pipe_handle` permitted
@@ -183,19 +188,29 @@ public:
\errors Any of the values POSIX `open()`, `mkfifo()`, `NtCreateFile()` or `NtCreateNamedPipeFile()` can return.
*/
LLFIO_MAKE_FREE_FUNCTION
- static LLFIO_HEADERS_ONLY_MEMFUNC_SPEC result<pipe_handle> pipe(path_view_type path, mode _mode, creation _creation, caching _caching = caching::all, flag flags = flag::none, const path_handle &base = path_discovery::temporary_named_pipes_directory()) noexcept;
+ static LLFIO_HEADERS_ONLY_MEMFUNC_SPEC result<pipe_handle> pipe(path_view_type path, mode _mode, creation _creation, caching _caching = caching::all,
+ flag flags = flag::none,
+ const path_handle &base = path_discovery::temporary_named_pipes_directory()) noexcept;
/*! Convenience overload for `pipe()` creating a new named pipe if
needed, and with read-only privileges. Unless `flag::multiplexable`
is specified, this will block until the other end connects.
*/
LLFIO_MAKE_FREE_FUNCTION
- static inline result<pipe_handle> pipe_create(path_view_type path, caching _caching = caching::all, flag flags = flag::unlink_on_first_close, const path_handle &base = path_discovery::temporary_named_pipes_directory()) noexcept { return pipe(path, mode::read, creation::if_needed, _caching, flags, base); }
+ static inline result<pipe_handle> pipe_create(path_view_type path, caching _caching = caching::all, flag flags = flag::unlink_on_first_close,
+ const path_handle &base = path_discovery::temporary_named_pipes_directory()) noexcept
+ {
+ return pipe(path, mode::read, creation::if_needed, _caching, flags, base);
+ }
/*! Convenience overload for `pipe()` opening an existing named pipe
with write-only privileges. This will fail if no reader is waiting
on the other end of the pipe.
*/
LLFIO_MAKE_FREE_FUNCTION
- static inline result<pipe_handle> pipe_open(path_view_type path, caching _caching = caching::all, flag flags = flag::none, const path_handle &base = path_discovery::temporary_named_pipes_directory()) noexcept { return pipe(path, mode::append, creation::open_existing, _caching, flags, base); }
+ static inline result<pipe_handle> pipe_open(path_view_type path, caching _caching = caching::all, flag flags = flag::none,
+ const path_handle &base = path_discovery::temporary_named_pipes_directory()) noexcept
+ {
+ return pipe(path, mode::append, creation::open_existing, _caching, flags, base);
+ }
/*! Create a pipe handle creating a randomly named pipe on a path.
The pipe is opened exclusively with `creation::only_if_not_exist` so it
@@ -204,7 +219,8 @@ public:
\errors Any of the values POSIX `open()`, `mkfifo()`, `NtCreateFile()` or `NtCreateNamedPipeFile()` can return.
*/
LLFIO_MAKE_FREE_FUNCTION
- static inline result<pipe_handle> random_pipe(mode _mode = mode::read, caching _caching = caching::all, flag flags = flag::unlink_on_first_close, const path_handle &dirpath = path_discovery::temporary_named_pipes_directory()) noexcept
+ static inline result<pipe_handle> random_pipe(mode _mode = mode::read, caching _caching = caching::all, flag flags = flag::unlink_on_first_close,
+ const path_handle &dirpath = path_discovery::temporary_named_pipes_directory()) noexcept
{
try
{
@@ -233,7 +249,8 @@ public:
\errors Any of the values POSIX `pipe()` or `NtCreateNamedPipeFile()` can return.
*/
LLFIO_MAKE_FREE_FUNCTION
- static LLFIO_HEADERS_ONLY_MEMFUNC_SPEC result<std::pair<pipe_handle, pipe_handle>> anonymous_pipe(caching _caching = caching::all, flag flags = flag::none) noexcept;
+ static LLFIO_HEADERS_ONLY_MEMFUNC_SPEC result<std::pair<pipe_handle, pipe_handle>> anonymous_pipe(caching _caching = caching::all,
+ flag flags = flag::none) noexcept;
LLFIO_HEADERS_ONLY_VIRTUAL_SPEC ~pipe_handle() override
{
@@ -264,7 +281,7 @@ public:
_v.behaviour |= native_handle_type::disposition::_child_close_executed;
}
#endif
- return io_handle::close();
+ return byte_io_handle::close();
}
#ifdef _WIN32
diff --git a/include/llfio/v2.0/storage_profile.hpp b/include/llfio/v2.0/storage_profile.hpp
index 32c8601d..5789ac83 100644
--- a/include/llfio/v2.0/storage_profile.hpp
+++ b/include/llfio/v2.0/storage_profile.hpp
@@ -25,7 +25,7 @@ Distributed under the Boost Software License, Version 1.0.
#ifndef LLFIO_STORAGE_PROFILE_H
#define LLFIO_STORAGE_PROFILE_H
-#include "io_handle.hpp"
+#include "byte_io_handle.hpp"
#if LLFIO_EXPERIMENTAL_STATUS_CODE
#include "outcome/experimental/status_outcome.hpp"
@@ -73,8 +73,8 @@ namespace storage_profile
//! Specialise for a different default value for T
template <class T> constexpr T default_value() { return T{}; }
- template <> constexpr storage_types map_to_storage_type<io_handle::extent_type>() { return storage_types::extent_type; }
- template <> constexpr io_handle::extent_type default_value<io_handle::extent_type>() { return static_cast<io_handle::extent_type>(-1); }
+ template <> constexpr storage_types map_to_storage_type<byte_io_handle::extent_type>() { return storage_types::extent_type; }
+ template <> constexpr byte_io_handle::extent_type default_value<byte_io_handle::extent_type>() { return static_cast<byte_io_handle::extent_type>(-1); }
template <> constexpr storage_types map_to_storage_type<unsigned int>() { return storage_types::unsigned_int; }
template <> constexpr unsigned int default_value<unsigned int>() { return static_cast<unsigned int>(-1); }
// template<> constexpr storage_types map_to_storage_type<unsigned long long>() { return storage_types::unsigned_long_long; }
@@ -148,7 +148,7 @@ namespace storage_profile
switch(type)
{
case storage_types::extent_type:
- return f(*reinterpret_cast<const item<io_handle::extent_type> *>(static_cast<const item_base *>(this)));
+ return f(*reinterpret_cast<const item<byte_io_handle::extent_type> *>(static_cast<const item_base *>(this)));
case storage_types::unsigned_int:
return f(*reinterpret_cast<const item<unsigned int> *>(static_cast<const item_base *>(this)));
case storage_types::unsigned_long_long:
@@ -309,21 +309,21 @@ namespace storage_profile
// Storage characteristics
item<std::string> device_name = {"storage:device:name", &storage::device}; // e.g. WDC WD30EFRX-68EUZN0
item<unsigned> device_min_io_size = {"storage:device:min_io_size", &storage::device}; // e.g. 4096
- item<io_handle::extent_type> device_size = {"storage:device:size", &storage::device};
+ item<byte_io_handle::extent_type> device_size = {"storage:device:size", &storage::device};
// Filing system characteristics
item<std::string> fs_name = {"storage:fs:name", &storage::fs};
item<std::string> fs_config = {"storage:fs:config", &storage::fs}; // POSIX mount options, ZFS pool properties etc
// item<std::string> fs_ffeatures = { "storage:fs:features" }; // Standardised features???
- item<io_handle::extent_type> fs_size = {"storage:fs:size", &storage::fs};
+ item<byte_io_handle::extent_type> fs_size = {"storage:fs:size", &storage::fs};
item<float> fs_in_use = {"storage:fs:in_use", &storage::fs};
// Test results on this filing system, storage and system
- item<io_handle::extent_type> atomic_rewrite_quantum = {"concurrency:atomic_rewrite_quantum", concurrency::atomic_rewrite_quantum, "The i/o modify quantum guaranteed to be atomically visible to readers irrespective of rewrite quantity"};
- item<io_handle::extent_type> max_aligned_atomic_rewrite = {"concurrency:max_aligned_atomic_rewrite", concurrency::atomic_rewrite_quantum,
+ item<byte_io_handle::extent_type> atomic_rewrite_quantum = {"concurrency:atomic_rewrite_quantum", concurrency::atomic_rewrite_quantum, "The i/o modify quantum guaranteed to be atomically visible to readers irrespective of rewrite quantity"};
+ item<byte_io_handle::extent_type> max_aligned_atomic_rewrite = {"concurrency:max_aligned_atomic_rewrite", concurrency::atomic_rewrite_quantum,
"The maximum single aligned i/o modify quantity atomically visible to readers (can be [potentially unreliably] much larger than atomic_rewrite_quantum). "
"A very common value on modern hardware with direct i/o thanks to PCIe DMA is 4096, don't trust values higher than this because of potentially discontiguous memory page mapping."};
- item<io_handle::extent_type> atomic_rewrite_offset_boundary = {"concurrency:atomic_rewrite_offset_boundary", concurrency::atomic_rewrite_offset_boundary, "The multiple of offset in a file where update atomicity breaks, so if you wrote 4096 bytes at a 512 offset and "
+ item<byte_io_handle::extent_type> atomic_rewrite_offset_boundary = {"concurrency:atomic_rewrite_offset_boundary", concurrency::atomic_rewrite_offset_boundary, "The multiple of offset in a file where update atomicity breaks, so if you wrote 4096 bytes at a 512 offset and "
"this value was 4096, your write would tear at 3584 because all writes would tear on a 4096 offset multiple. "
"Linux has a famously broken kernel i/o design which causes this value to be a page multiple, except on "
"filing systems which take special measures to work around it. Windows NT appears to lose all atomicity as soon as "
diff --git a/programs/benchmark-async/main.cpp b/programs/benchmark-async/main.cpp
index 613f338d..1b4a69e3 100644
--- a/programs/benchmark-async/main.cpp
+++ b/programs/benchmark-async/main.cpp
@@ -800,21 +800,21 @@ template <class C, class... Args> void benchmark(llfio::path_view csv, size_t ma
of << "\n";
}
-struct NoHandle final : public llfio::io_handle
+struct NoHandle final : public llfio::byte_io_handle
{
- using mode = typename llfio::io_handle::mode;
- using creation = typename llfio::io_handle::creation;
- using caching = typename llfio::io_handle::caching;
- using flag = typename llfio::io_handle::flag;
- using buffer_type = typename llfio::io_handle::buffer_type;
- using const_buffer_type = typename llfio::io_handle::const_buffer_type;
- using buffers_type = typename llfio::io_handle::buffers_type;
- using const_buffers_type = typename llfio::io_handle::const_buffers_type;
- template <class T> using io_request = typename llfio::io_handle::template io_request<T>;
- template <class T> using io_result = typename llfio::io_handle::template io_result<T>;
+ using mode = typename llfio::byte_io_handle::mode;
+ using creation = typename llfio::byte_io_handle::creation;
+ using caching = typename llfio::byte_io_handle::caching;
+ using flag = typename llfio::byte_io_handle::flag;
+ using buffer_type = typename llfio::byte_io_handle::buffer_type;
+ using const_buffer_type = typename llfio::byte_io_handle::const_buffer_type;
+ using buffers_type = typename llfio::byte_io_handle::buffers_type;
+ using const_buffers_type = typename llfio::byte_io_handle::const_buffers_type;
+ template <class T> using io_request = typename llfio::byte_io_handle::template io_request<T>;
+ template <class T> using io_result = typename llfio::byte_io_handle::template io_result<T>;
NoHandle()
- : llfio::io_handle(llfio::native_handle_type(llfio::native_handle_type::disposition::nonblocking | llfio::native_handle_type::disposition::readable | llfio::native_handle_type::disposition::writable, -2 /* fake being open */), llfio::io_handle::caching::all, llfio::io_handle::flag::multiplexable, nullptr)
+ : llfio::byte_io_handle(llfio::native_handle_type(llfio::native_handle_type::disposition::nonblocking | llfio::native_handle_type::disposition::readable | llfio::native_handle_type::disposition::writable, -2 /* fake being open */), llfio::byte_io_handle::caching::all, llfio::byte_io_handle::flag::multiplexable, nullptr)
{
}
~NoHandle()
@@ -862,14 +862,14 @@ template <class HandleType = NoHandle> struct benchmark_llfio
static constexpr bool launch_writer_thread = !std::is_same<HandleType, NoHandle>::value;
- struct receiver_type final : public llfio::io_multiplexer::io_operation_state_visitor
+ struct receiver_type final : public llfio::byte_io_multiplexer::io_operation_state_visitor
{
benchmark_llfio *parent{nullptr};
HandleType read_handle;
std::unique_ptr<llfio::byte[]> io_state_ptr;
llfio::byte _buffer[sizeof(size_t)];
buffer_type buffer;
- llfio::io_multiplexer::io_operation_state *io_state{nullptr};
+ llfio::byte_io_multiplexer::io_operation_state *io_state{nullptr};
std::chrono::high_resolution_clock::time_point when_read_completed;
explicit receiver_type(benchmark_llfio *_parent, HandleType &&h)
@@ -922,7 +922,7 @@ template <class HandleType = NoHandle> struct benchmark_llfio
}
// Called when the read completes
- virtual bool read_completed(llfio::io_multiplexer::io_operation_state::lock_guard & /*g*/, llfio::io_operation_state_type /*former*/, io_result<buffers_type> &&res) override
+ virtual bool read_completed(llfio::byte_io_multiplexer::io_operation_state::lock_guard & /*g*/, llfio::io_operation_state_type /*former*/, io_result<buffers_type> &&res) override
{
when_read_completed = std::chrono::high_resolution_clock::now();
if(!res)
@@ -940,19 +940,19 @@ template <class HandleType = NoHandle> struct benchmark_llfio
}
// Called when the state for the read can be disposed
- virtual void read_finished(llfio::io_multiplexer::io_operation_state::lock_guard & /*g*/, llfio::io_operation_state_type /*former*/) override
+ virtual void read_finished(llfio::byte_io_multiplexer::io_operation_state::lock_guard & /*g*/, llfio::io_operation_state_type /*former*/) override
{
io_state->~io_operation_state();
io_state = nullptr;
}
};
- llfio::io_multiplexer_ptr multiplexer;
+ llfio::byte_io_multiplexer_ptr multiplexer;
std::vector<HandleType> write_handles;
std::vector<receiver_type> read_states;
receiver_type *to_restart{nullptr};
- explicit benchmark_llfio(size_t count, llfio::io_multiplexer_ptr (*make_multiplexer)())
+ explicit benchmark_llfio(size_t count, llfio::byte_io_multiplexer_ptr (*make_multiplexer)())
{
multiplexer = make_multiplexer();
read_states.reserve(count);
@@ -1126,22 +1126,22 @@ struct benchmark_asio_pipe
int main(void)
{
std::cout << "Warming up ..." << std::endl;
- do_benchmark<benchmark_llfio<>>(-1, []() -> llfio::io_multiplexer_ptr { return llfio::test::multiplexer_null(2, true).value(); });
+ do_benchmark<benchmark_llfio<>>(-1, []() -> llfio::byte_io_multiplexer_ptr { return llfio::test::multiplexer_null(2, true).value(); });
benchmark<benchmark_llfio<>>("llfio-null-unsynchronised.csv", 64, "Null i/o multiplexer unsynchronised", //
- []() -> llfio::io_multiplexer_ptr { return llfio::test::multiplexer_null(1, false).value(); });
+ []() -> llfio::byte_io_multiplexer_ptr { return llfio::test::multiplexer_null(1, false).value(); });
benchmark<benchmark_llfio<>>("llfio-null-synchronised.csv", 64, "Null i/o multiplexer synchronised", //
- []() -> llfio::io_multiplexer_ptr { return llfio::test::multiplexer_null(2, true).value(); });
+ []() -> llfio::byte_io_multiplexer_ptr { return llfio::test::multiplexer_null(2, true).value(); });
#ifdef _WIN32
std::cout << "\nWarming up ..." << std::endl;
do_benchmark<benchmark_llfio<llfio::pipe_handle>>(-1, //
- []() -> llfio::io_multiplexer_ptr { return llfio::test::multiplexer_win_iocp(2, true).value(); });
+ []() -> llfio::byte_io_multiplexer_ptr { return llfio::test::multiplexer_win_iocp(2, true).value(); });
// No locking, enable IOCP immediate completions. ASIO can't compete with this.
benchmark<benchmark_llfio<llfio::pipe_handle>>("llfio-pipe-handle-unsynchronised.csv", 64, "llfio::pipe_handle and IOCP unsynchronised", //
- []() -> llfio::io_multiplexer_ptr { return llfio::test::multiplexer_win_iocp(1, false).value(); });
+ []() -> llfio::byte_io_multiplexer_ptr { return llfio::test::multiplexer_win_iocp(1, false).value(); });
// Locking enabled, disable IOCP immediate completions so it's a fair comparison with ASIO
benchmark<benchmark_llfio<llfio::pipe_handle>>("llfio-pipe-handle-synchronised.csv", 64, "llfio::pipe_handle and IOCP synchronised", //
- []() -> llfio::io_multiplexer_ptr { return llfio::test::multiplexer_win_iocp(2, true).value(); });
+ []() -> llfio::byte_io_multiplexer_ptr { return llfio::test::multiplexer_win_iocp(2, true).value(); });
#endif
#if ENABLE_ASIO
diff --git a/programs/benchmark-io-congestion/main.cpp b/programs/benchmark-io-congestion/main.cpp
index f7e929a5..257dbffd 100644
--- a/programs/benchmark-io-congestion/main.cpp
+++ b/programs/benchmark-io-congestion/main.cpp
@@ -131,7 +131,7 @@ struct llfio_runner_unpaced
llfio::dynamic_thread_pool_group_ptr group = llfio::make_dynamic_thread_pool_group().value();
std::vector<llfio::dynamic_thread_pool_group::work_item *> workitems;
- llfio_runner_unpaced(llfio::io_handle * /*unused*/) {}
+ llfio_runner_unpaced(llfio::byte_io_handle * /*unused*/) {}
~llfio_runner_unpaced()
{
for(auto *p : workitems)
@@ -176,11 +176,11 @@ struct llfio_runner_paced
{
std::atomic<bool> cancel{false};
llfio::dynamic_thread_pool_group_ptr group = llfio::make_dynamic_thread_pool_group().value();
- llfio::dynamic_thread_pool_group::io_aware_work_item::io_handle_awareness awareness;
+ llfio::dynamic_thread_pool_group::io_aware_work_item::byte_io_handle_awareness awareness;
std::vector<llfio::dynamic_thread_pool_group::work_item *> workitems;
std::atomic<int64_t> last_pace{0};
- llfio_runner_paced(llfio::io_handle *h)
+ llfio_runner_paced(llfio::byte_io_handle *h)
: awareness{h, 1.0f /* 100% reads */}
{
}
@@ -243,7 +243,7 @@ struct asio_runner
std::atomic<bool> cancel{false};
asio::io_context ctx;
- asio_runner(llfio::io_handle * /*unused*/) {}
+ asio_runner(llfio::byte_io_handle * /*unused*/) {}
template <class F> struct C
{
asio_runner *parent;
diff --git a/test/tests/byte_socket_handle.cpp b/test/tests/byte_socket_handle.cpp
new file mode 100644
index 00000000..dc082ca4
--- /dev/null
+++ b/test/tests/byte_socket_handle.cpp
@@ -0,0 +1,729 @@
+/* Integration test kernel for whether socket handles work
+(C) 2021-2022 Niall Douglas <http://www.nedproductions.biz/> (2 commits)
+File Created: Dec 2021
+
+
+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 "../test_kernel_decl.hpp"
+
+#include <future>
+#include <sstream>
+#include <unordered_set>
+
+static inline void TestSocketAddress()
+{
+ namespace llfio = LLFIO_V2_NAMESPACE;
+ {
+ auto a = llfio::ip::address_v4::loopback();
+ BOOST_CHECK(a.is_loopback());
+ BOOST_CHECK(!a.is_multicast());
+ BOOST_CHECK(!a.is_any());
+ BOOST_CHECK(a.is_v4());
+ BOOST_CHECK(!a.is_v6());
+ BOOST_CHECK(a.port() == 0);
+ BOOST_CHECK(a.to_bytes().size() == 4);
+ BOOST_CHECK(a.to_bytes()[0] == llfio::to_byte(127));
+ BOOST_CHECK(a.to_bytes()[1] == llfio::to_byte(0));
+ BOOST_CHECK(a.to_bytes()[2] == llfio::to_byte(0));
+ BOOST_CHECK(a.to_bytes()[3] == llfio::to_byte(1));
+ BOOST_CHECK(a.to_uint() == 0x7f000001);
+ std::stringstream ss;
+ ss << a;
+ std::cout << ss.str() << std::endl;
+ BOOST_CHECK(ss.str() == "127.0.0.1:0");
+ } // namespace ;
+ {
+ auto a = llfio::ip::address_v6::loopback();
+ BOOST_CHECK(a.is_loopback());
+ BOOST_CHECK(!a.is_multicast());
+ BOOST_CHECK(!a.is_any());
+ BOOST_CHECK(!a.is_v4());
+ BOOST_CHECK(a.is_v6());
+ BOOST_CHECK(a.port() == 0);
+ BOOST_CHECK(a.to_bytes().size() == 16);
+ BOOST_CHECK(a.to_bytes()[0] == llfio::to_byte(0));
+ BOOST_CHECK(a.to_bytes()[1] == llfio::to_byte(0));
+ BOOST_CHECK(a.to_bytes()[2] == llfio::to_byte(0));
+ BOOST_CHECK(a.to_bytes()[3] == llfio::to_byte(0));
+ BOOST_CHECK(a.to_bytes()[4] == llfio::to_byte(0));
+ BOOST_CHECK(a.to_bytes()[5] == llfio::to_byte(0));
+ BOOST_CHECK(a.to_bytes()[6] == llfio::to_byte(0));
+ BOOST_CHECK(a.to_bytes()[7] == llfio::to_byte(0));
+ BOOST_CHECK(a.to_bytes()[8] == llfio::to_byte(0));
+ BOOST_CHECK(a.to_bytes()[9] == llfio::to_byte(0));
+ BOOST_CHECK(a.to_bytes()[10] == llfio::to_byte(0));
+ BOOST_CHECK(a.to_bytes()[11] == llfio::to_byte(0));
+ BOOST_CHECK(a.to_bytes()[12] == llfio::to_byte(0));
+ BOOST_CHECK(a.to_bytes()[13] == llfio::to_byte(0));
+ BOOST_CHECK(a.to_bytes()[14] == llfio::to_byte(0));
+ BOOST_CHECK(a.to_bytes()[15] == llfio::to_byte(1));
+ std::stringstream ss;
+ ss << a;
+ std::cout << ss.str() << std::endl;
+ BOOST_CHECK(ss.str() == "[::1]:0");
+ }
+ {
+ auto a = llfio::ip::address_v4::any();
+ BOOST_CHECK(!a.is_loopback());
+ BOOST_CHECK(!a.is_multicast());
+ BOOST_CHECK(a.is_any());
+ BOOST_CHECK(a.is_v4());
+ BOOST_CHECK(!a.is_v6());
+ BOOST_CHECK(a.port() == 0);
+ BOOST_CHECK(a.to_bytes().size() == 4);
+ BOOST_CHECK(a.to_bytes()[0] == llfio::to_byte(0));
+ BOOST_CHECK(a.to_bytes()[1] == llfio::to_byte(0));
+ BOOST_CHECK(a.to_bytes()[2] == llfio::to_byte(0));
+ BOOST_CHECK(a.to_bytes()[3] == llfio::to_byte(0));
+ BOOST_CHECK(a.to_uint() == 0x0000000);
+ std::stringstream ss;
+ ss << a;
+ std::cout << ss.str() << std::endl;
+ BOOST_CHECK(ss.str() == "0.0.0.0:0");
+ }
+ {
+ auto a = llfio::ip::address_v6::any();
+ BOOST_CHECK(!a.is_loopback());
+ BOOST_CHECK(!a.is_multicast());
+ BOOST_CHECK(a.is_any());
+ BOOST_CHECK(!a.is_v4());
+ BOOST_CHECK(a.is_v6());
+ BOOST_CHECK(a.port() == 0);
+ BOOST_CHECK(a.to_bytes().size() == 16);
+ BOOST_CHECK(a.to_bytes()[0] == llfio::to_byte(0));
+ BOOST_CHECK(a.to_bytes()[1] == llfio::to_byte(0));
+ BOOST_CHECK(a.to_bytes()[2] == llfio::to_byte(0));
+ BOOST_CHECK(a.to_bytes()[3] == llfio::to_byte(0));
+ BOOST_CHECK(a.to_bytes()[4] == llfio::to_byte(0));
+ BOOST_CHECK(a.to_bytes()[5] == llfio::to_byte(0));
+ BOOST_CHECK(a.to_bytes()[6] == llfio::to_byte(0));
+ BOOST_CHECK(a.to_bytes()[7] == llfio::to_byte(0));
+ BOOST_CHECK(a.to_bytes()[8] == llfio::to_byte(0));
+ BOOST_CHECK(a.to_bytes()[9] == llfio::to_byte(0));
+ BOOST_CHECK(a.to_bytes()[10] == llfio::to_byte(0));
+ BOOST_CHECK(a.to_bytes()[11] == llfio::to_byte(0));
+ BOOST_CHECK(a.to_bytes()[12] == llfio::to_byte(0));
+ BOOST_CHECK(a.to_bytes()[13] == llfio::to_byte(0));
+ BOOST_CHECK(a.to_bytes()[14] == llfio::to_byte(0));
+ BOOST_CHECK(a.to_bytes()[15] == llfio::to_byte(0));
+ std::stringstream ss;
+ ss << a;
+ std::cout << ss.str() << std::endl;
+ BOOST_CHECK(ss.str() == "[::]:0");
+ }
+ {
+ auto a = llfio::ip::make_address("78.68.1.255:1234").value();
+ BOOST_CHECK(!a.is_loopback());
+ BOOST_CHECK(!a.is_multicast());
+ BOOST_CHECK(!a.is_any());
+ BOOST_CHECK(a.is_v4());
+ BOOST_CHECK(!a.is_v6());
+ BOOST_CHECK(a.port() == 1234);
+ BOOST_CHECK(a.to_bytes().size() == 4);
+ BOOST_CHECK(a.to_bytes()[0] == llfio::to_byte(78));
+ BOOST_CHECK(a.to_bytes()[1] == llfio::to_byte(68));
+ BOOST_CHECK(a.to_bytes()[2] == llfio::to_byte(1));
+ BOOST_CHECK(a.to_bytes()[3] == llfio::to_byte(255));
+ std::stringstream ss;
+ ss << a;
+ std::cout << ss.str() << std::endl;
+ BOOST_CHECK(ss.str() == "78.68.1.255:1234");
+ }
+ {
+ auto a = llfio::ip::make_address("[78aa:bb68::1:255]:1234").value();
+ BOOST_CHECK(!a.is_loopback());
+ BOOST_CHECK(!a.is_multicast());
+ BOOST_CHECK(!a.is_any());
+ BOOST_CHECK(!a.is_v4());
+ BOOST_CHECK(a.is_v6());
+ BOOST_CHECK(a.port() == 1234);
+ BOOST_CHECK(a.to_bytes().size() == 16);
+ BOOST_CHECK(a.to_bytes()[0] == llfio::to_byte(0x78));
+ BOOST_CHECK(a.to_bytes()[1] == llfio::to_byte(0xaa));
+ BOOST_CHECK(a.to_bytes()[2] == llfio::to_byte(0xbb));
+ BOOST_CHECK(a.to_bytes()[3] == llfio::to_byte(0x68));
+ BOOST_CHECK(a.to_bytes()[4] == llfio::to_byte(0));
+ BOOST_CHECK(a.to_bytes()[5] == llfio::to_byte(0));
+ BOOST_CHECK(a.to_bytes()[6] == llfio::to_byte(0));
+ BOOST_CHECK(a.to_bytes()[7] == llfio::to_byte(0));
+ BOOST_CHECK(a.to_bytes()[8] == llfio::to_byte(0));
+ BOOST_CHECK(a.to_bytes()[9] == llfio::to_byte(0));
+ BOOST_CHECK(a.to_bytes()[10] == llfio::to_byte(0));
+ BOOST_CHECK(a.to_bytes()[11] == llfio::to_byte(0));
+ BOOST_CHECK(a.to_bytes()[12] == llfio::to_byte(0));
+ BOOST_CHECK(a.to_bytes()[13] == llfio::to_byte(0x1));
+ BOOST_CHECK(a.to_bytes()[14] == llfio::to_byte(0x2));
+ BOOST_CHECK(a.to_bytes()[15] == llfio::to_byte(0x55));
+ std::stringstream ss;
+ ss << a;
+ std::cout << ss.str() << std::endl;
+ BOOST_CHECK(ss.str() == "[78aa:bb68::1:255]:1234");
+ }
+
+ {
+ auto a = llfio::ip::make_address("[78aa:0:0:bb68:0:0:0:255]:1234").value();
+ BOOST_CHECK(!a.is_loopback());
+ BOOST_CHECK(!a.is_multicast());
+ BOOST_CHECK(!a.is_any());
+ BOOST_CHECK(!a.is_v4());
+ BOOST_CHECK(a.is_v6());
+ BOOST_CHECK(a.port() == 1234);
+ BOOST_CHECK(a.to_bytes().size() == 16);
+ BOOST_CHECK(a.to_bytes()[0] == llfio::to_byte(0x78));
+ BOOST_CHECK(a.to_bytes()[1] == llfio::to_byte(0xaa));
+ BOOST_CHECK(a.to_bytes()[2] == llfio::to_byte(0));
+ BOOST_CHECK(a.to_bytes()[3] == llfio::to_byte(0));
+ BOOST_CHECK(a.to_bytes()[4] == llfio::to_byte(0));
+ BOOST_CHECK(a.to_bytes()[5] == llfio::to_byte(0));
+ BOOST_CHECK(a.to_bytes()[6] == llfio::to_byte(0xbb));
+ BOOST_CHECK(a.to_bytes()[7] == llfio::to_byte(0x68));
+ BOOST_CHECK(a.to_bytes()[8] == llfio::to_byte(0));
+ BOOST_CHECK(a.to_bytes()[9] == llfio::to_byte(0));
+ BOOST_CHECK(a.to_bytes()[10] == llfio::to_byte(0));
+ BOOST_CHECK(a.to_bytes()[11] == llfio::to_byte(0));
+ BOOST_CHECK(a.to_bytes()[12] == llfio::to_byte(0));
+ BOOST_CHECK(a.to_bytes()[13] == llfio::to_byte(0));
+ BOOST_CHECK(a.to_bytes()[14] == llfio::to_byte(0x2));
+ BOOST_CHECK(a.to_bytes()[15] == llfio::to_byte(0x55));
+ std::stringstream ss;
+ ss << a;
+ std::cout << ss.str() << std::endl;
+ BOOST_CHECK(ss.str() == "[78aa:0:0:bb68::255]:1234");
+ }
+ {
+ auto a = llfio::ip::make_address("[78aa:0:0:0:bb68:0:0:255]:1234").value();
+ BOOST_CHECK(!a.is_loopback());
+ BOOST_CHECK(!a.is_multicast());
+ BOOST_CHECK(!a.is_any());
+ BOOST_CHECK(!a.is_v4());
+ BOOST_CHECK(a.is_v6());
+ BOOST_CHECK(a.port() == 1234);
+ BOOST_CHECK(a.to_bytes().size() == 16);
+ BOOST_CHECK(a.to_bytes()[0] == llfio::to_byte(0x78));
+ BOOST_CHECK(a.to_bytes()[1] == llfio::to_byte(0xaa));
+ BOOST_CHECK(a.to_bytes()[2] == llfio::to_byte(0));
+ BOOST_CHECK(a.to_bytes()[3] == llfio::to_byte(0));
+ BOOST_CHECK(a.to_bytes()[4] == llfio::to_byte(0));
+ BOOST_CHECK(a.to_bytes()[5] == llfio::to_byte(0));
+ BOOST_CHECK(a.to_bytes()[6] == llfio::to_byte(0));
+ BOOST_CHECK(a.to_bytes()[7] == llfio::to_byte(0));
+ BOOST_CHECK(a.to_bytes()[8] == llfio::to_byte(0xbb));
+ BOOST_CHECK(a.to_bytes()[9] == llfio::to_byte(0x68));
+ BOOST_CHECK(a.to_bytes()[10] == llfio::to_byte(0));
+ BOOST_CHECK(a.to_bytes()[11] == llfio::to_byte(0));
+ BOOST_CHECK(a.to_bytes()[12] == llfio::to_byte(0));
+ BOOST_CHECK(a.to_bytes()[13] == llfio::to_byte(0));
+ BOOST_CHECK(a.to_bytes()[14] == llfio::to_byte(0x2));
+ BOOST_CHECK(a.to_bytes()[15] == llfio::to_byte(0x55));
+ std::stringstream ss;
+ ss << a;
+ std::cout << ss.str() << std::endl;
+ BOOST_CHECK(ss.str() == "[78aa::bb68:0:0:255]:1234");
+ }
+ {
+ auto a = llfio::ip::make_address("[1000::]:1234").value();
+ BOOST_CHECK(!a.is_loopback());
+ BOOST_CHECK(!a.is_multicast());
+ BOOST_CHECK(!a.is_any());
+ BOOST_CHECK(!a.is_v4());
+ BOOST_CHECK(a.is_v6());
+ BOOST_CHECK(a.port() == 1234);
+ BOOST_CHECK(a.to_bytes().size() == 16);
+ BOOST_CHECK(a.to_bytes()[0] == llfio::to_byte(0x10));
+ BOOST_CHECK(a.to_bytes()[1] == llfio::to_byte(0));
+ BOOST_CHECK(a.to_bytes()[2] == llfio::to_byte(0));
+ BOOST_CHECK(a.to_bytes()[3] == llfio::to_byte(0));
+ BOOST_CHECK(a.to_bytes()[4] == llfio::to_byte(0));
+ BOOST_CHECK(a.to_bytes()[5] == llfio::to_byte(0));
+ BOOST_CHECK(a.to_bytes()[6] == llfio::to_byte(0));
+ BOOST_CHECK(a.to_bytes()[7] == llfio::to_byte(0));
+ BOOST_CHECK(a.to_bytes()[8] == llfio::to_byte(0));
+ BOOST_CHECK(a.to_bytes()[9] == llfio::to_byte(0));
+ BOOST_CHECK(a.to_bytes()[10] == llfio::to_byte(0));
+ BOOST_CHECK(a.to_bytes()[11] == llfio::to_byte(0));
+ BOOST_CHECK(a.to_bytes()[12] == llfio::to_byte(0));
+ BOOST_CHECK(a.to_bytes()[13] == llfio::to_byte(0));
+ BOOST_CHECK(a.to_bytes()[14] == llfio::to_byte(0));
+ BOOST_CHECK(a.to_bytes()[15] == llfio::to_byte(0));
+ std::stringstream ss;
+ ss << a;
+ std::cout << ss.str() << std::endl;
+ BOOST_CHECK(ss.str() == "[1000::]:1234");
+ }
+}
+
+static inline void TestBlockingSocketHandles()
+{
+ namespace llfio = LLFIO_V2_NAMESPACE;
+ auto serversocket = llfio::listening_socket_handle::listening_socket(llfio::ip::family::v4, llfio::listening_socket_handle::mode::read).value();
+ BOOST_REQUIRE(serversocket.is_valid());
+ BOOST_CHECK(serversocket.is_socket());
+ BOOST_CHECK(serversocket.is_readable());
+ BOOST_CHECK(!serversocket.is_writable());
+ serversocket.bind(llfio::ip::address_v4::loopback()).value();
+ auto endpoint = serversocket.local_endpoint().value();
+ std::cout << "Server socket is listening on " << endpoint << std::endl;
+ if(endpoint.family() == llfio::ip::family::unknown && getenv("CI") != nullptr)
+ {
+ std::cout << "\nNOTE: Currently on CI and couldn't bind a listening socket to loopback, assuming it is CI host restrictions and skipping this test."
+ << std::endl;
+ return;
+ }
+ auto readerthread = std::async([serversocket = std::move(serversocket)]() mutable {
+ std::pair<llfio::byte_socket_handle, llfio::ip::address> s;
+ serversocket.read({s}).value(); // This immediately blocks in blocking mode
+ BOOST_REQUIRE(s.first.is_valid());
+ BOOST_CHECK(s.first.is_socket());
+ BOOST_CHECK(s.first.is_readable());
+ BOOST_CHECK(!s.first.is_writable());
+ std::cout << "Server thread sees incoming connection from " << s.second << std::endl;
+ serversocket.close().value();
+ llfio::byte buffer[64];
+ auto read = s.first.read(0, {{buffer, 64}}).value();
+ BOOST_REQUIRE(read == 5);
+ BOOST_CHECK(0 == memcmp(buffer, "hello", 5));
+ s.first.close().value();
+ });
+ auto begin = std::chrono::steady_clock::now();
+ while(std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now() - begin).count() < 100)
+ {
+ std::this_thread::sleep_for(std::chrono::milliseconds(100));
+ }
+ if(std::future_status::ready == readerthread.wait_for(std::chrono::seconds(0)))
+ {
+ readerthread.get(); // rethrow exception
+ }
+ llfio::byte_socket_handle writer;
+ begin = std::chrono::steady_clock::now();
+ while(std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now() - begin).count() < 1000)
+ {
+ writer =
+ llfio::byte_socket_handle::byte_socket(llfio::ip::family::v4, llfio::byte_socket_handle::mode::append, llfio::byte_socket_handle::caching::reads).value();
+ auto r = writer.connect(endpoint);
+ if(r)
+ {
+ break;
+ }
+ }
+ BOOST_REQUIRE(writer.is_valid());
+ BOOST_CHECK(writer.is_socket());
+ BOOST_CHECK(!writer.is_readable());
+ BOOST_CHECK(writer.is_writable());
+ auto written = writer.write(0, {{(const llfio::byte *) "hello", 5}}).value();
+ BOOST_REQUIRE(written == 5);
+ writer.shutdown_and_close().value();
+ readerthread.get();
+}
+
+static inline void TestNonBlockingSocketHandles()
+{
+ namespace llfio = LLFIO_V2_NAMESPACE;
+ auto serversocket = llfio::listening_socket_handle::listening_socket(llfio::ip::family::v4, llfio::listening_socket_handle::mode::read,
+ llfio::byte_socket_handle::caching::all, llfio::byte_socket_handle::flag::multiplexable)
+ .value();
+ BOOST_REQUIRE(serversocket.is_valid());
+ BOOST_CHECK(serversocket.is_socket());
+ BOOST_CHECK(serversocket.is_readable());
+ BOOST_CHECK(!serversocket.is_writable());
+ serversocket.bind(llfio::ip::address_v4::loopback()).value();
+ auto endpoint = serversocket.local_endpoint().value();
+ std::cout << "Server socket is listening on " << endpoint << std::endl;
+ if(endpoint.family() == llfio::ip::family::unknown && getenv("CI") != nullptr)
+ {
+ std::cout << "\nNOTE: Currently on CI and couldn't bind a listening socket to loopback, assuming it is CI host restrictions and skipping this test."
+ << std::endl;
+ return;
+ }
+
+ std::pair<llfio::byte_socket_handle, llfio::ip::address> reader;
+ { // no incoming, so non-blocking read should time out
+ auto read = serversocket.read({reader}, std::chrono::milliseconds(0));
+ BOOST_REQUIRE(read.has_error());
+ BOOST_REQUIRE(read.error() == llfio::errc::timed_out);
+ }
+ { // no incoming, so blocking read should time out
+ auto read = serversocket.read({reader}, std::chrono::seconds(1));
+ BOOST_REQUIRE(read.has_error());
+ BOOST_REQUIRE(read.error() == llfio::errc::timed_out);
+ }
+
+ // Form the connection.
+ auto writer = llfio::byte_socket_handle::byte_socket(llfio::ip::family::v4, llfio::byte_socket_handle::mode::append,
+ llfio::byte_socket_handle::caching::reads, llfio::byte_socket_handle::flag::multiplexable)
+ .value();
+ writer.connect(endpoint).value();
+ serversocket.read({reader}, std::chrono::seconds(1)).value();
+ std::cout << "Server socket sees incoming connection from " << reader.second << std::endl;
+
+ llfio::byte buffer[64];
+ { // no data, so non-blocking read should time out
+ auto read = reader.first.read(0, {{buffer, 64}}, std::chrono::milliseconds(0));
+ BOOST_REQUIRE(read.has_error());
+ BOOST_REQUIRE(read.error() == llfio::errc::timed_out);
+ }
+ { // no data, so blocking read should time out
+ auto read = reader.first.read(0, {{buffer, 64}}, std::chrono::seconds(1));
+ BOOST_REQUIRE(read.has_error());
+ BOOST_REQUIRE(read.error() == llfio::errc::timed_out);
+ }
+ auto written = writer.write(0, {{(const llfio::byte *) "hello", 5}}).value();
+ BOOST_REQUIRE(written == 5);
+ // writer.shutdown_and_close().value(); // would block until socket drained by reader
+ // writer.close().value(); // would cause all further reads to fail due to socket broken
+ auto read = reader.first.read(0, {{buffer, 64}}, std::chrono::milliseconds(1));
+ BOOST_REQUIRE(read.value() == 5);
+ BOOST_CHECK(0 == memcmp(buffer, "hello", 5));
+ writer.shutdown_and_close().value(); // must not block nor fail
+ writer.close().value();
+ reader.first.close().value();
+}
+
+#if LLFIO_ENABLE_TEST_IO_MULTIPLEXERS
+static inline void TestMultiplexedSocketHandles()
+{
+ static constexpr size_t MAX_SOCKETS = 64;
+ namespace llfio = LLFIO_V2_NAMESPACE;
+ auto test_multiplexer = [](llfio::byte_io_multiplexer_ptr multiplexer) {
+ std::vector<llfio::byte_socket_handle> read_sockets, write_sockets;
+ std::vector<size_t> received_for(MAX_SOCKETS);
+ struct checking_receiver final : public llfio::byte_io_multiplexer::io_operation_state_visitor
+ {
+ size_t myindex;
+ std::unique_ptr<llfio::byte[]> io_state_ptr;
+ std::vector<size_t> &received_for;
+ union
+ {
+ llfio::byte _buffer[sizeof(size_t)];
+ size_t _index;
+ };
+ llfio::byte_socket_handle::buffer_type buffer;
+ llfio::byte_io_multiplexer::io_operation_state *io_state{nullptr};
+
+ checking_receiver(size_t _myindex, llfio::byte_io_multiplexer_ptr &multiplexer, std::vector<size_t> &r)
+ : myindex(_myindex)
+ , io_state_ptr(std::make_unique<llfio::byte[]>(multiplexer->io_state_requirements().first))
+ , received_for(r)
+ , buffer(_buffer, sizeof(_buffer))
+ {
+ memset(_buffer, 0, sizeof(_buffer));
+ }
+ checking_receiver(const checking_receiver &) = delete;
+ checking_receiver(checking_receiver &&o) = default;
+ checking_receiver &operator=(const checking_receiver &) = delete;
+ checking_receiver &operator=(checking_receiver &&) = default;
+ ~checking_receiver()
+ {
+ if(io_state != nullptr)
+ {
+ if(!is_finished(io_state->current_state()))
+ {
+ abort();
+ }
+ io_state->~io_operation_state();
+ io_state = nullptr;
+ }
+ }
+
+ // Initiated the read
+ llfio::result<void> read_begin(llfio::byte_io_multiplexer_ptr &multiplexer, llfio::byte_io_handle &h)
+ {
+ if(io_state != nullptr)
+ {
+ BOOST_REQUIRE(is_finished(io_state->current_state()));
+ io_state->~io_operation_state();
+ io_state = nullptr;
+ }
+ buffer = {_buffer, sizeof(_buffer)};
+ io_state = multiplexer->construct_and_init_io_operation(
+ {io_state_ptr.get(), 4096 /*lies*/}, &h, this, {}, {}, llfio::byte_socket_handle::io_request<llfio::byte_socket_handle::buffers_type>({&buffer, 1}, 0));
+ return llfio::success();
+ }
+
+ // Called if the read did not complete immediately
+ virtual void read_initiated(llfio::byte_io_multiplexer::io_operation_state::lock_guard & /*g*/, llfio::io_operation_state_type /*former*/) override
+ {
+ std::cout << " Socket " << myindex << " will complete read later" << std::endl;
+ }
+
+ // Called when the read completes
+ virtual bool read_completed(llfio::byte_io_multiplexer::io_operation_state::lock_guard & /*g*/, llfio::io_operation_state_type former,
+ llfio::byte_socket_handle::io_result<llfio::byte_socket_handle::buffers_type> &&res) override
+ {
+ if(is_initialised(former))
+ {
+ std::cout << " Socket " << myindex << " read completes immediately" << std::endl;
+ }
+ else
+ {
+ std::cout << " Socket " << myindex << " read completes asynchronously" << std::endl;
+ }
+ BOOST_CHECK(res.has_value());
+ if(res)
+ {
+ BOOST_REQUIRE(res.value().size() == 1);
+ BOOST_CHECK(res.value()[0].data() == _buffer);
+ BOOST_CHECK(res.value()[0].size() == sizeof(size_t));
+ BOOST_REQUIRE(_index < MAX_SOCKETS);
+ BOOST_CHECK(_index == myindex);
+ received_for[_index]++;
+ }
+ return true;
+ }
+
+ // Called when the state for the read can be disposed
+ virtual void read_finished(llfio::byte_io_multiplexer::io_operation_state::lock_guard & /*g*/, llfio::io_operation_state_type former) override
+ {
+ std::cout << " Socket " << myindex << " read finishes" << std::endl;
+ BOOST_REQUIRE(former == llfio::io_operation_state_type::read_completed);
+ }
+ };
+ auto serversocket =
+ llfio::listening_socket_handle::listening_socket(llfio::ip::family::v4, llfio::listening_socket_handle::mode::write,
+ llfio::byte_socket_handle::caching::all, llfio::byte_socket_handle::flag::multiplexable)
+ .value();
+ serversocket.bind(llfio::ip::address_v4::loopback()).value();
+ auto endpoint = serversocket.local_endpoint().value();
+ std::cout << "Server socket is listening on " << endpoint << std::endl;
+ if(endpoint.family() == llfio::ip::family::unknown && getenv("CI") != nullptr)
+ {
+ std::cout << "\nNOTE: Currently on CI and couldn't bind a listening socket to loopback, assuming it is CI host restrictions and skipping this test."
+ << std::endl;
+ return;
+ }
+
+ std::vector<checking_receiver> async_reads;
+ for(size_t n = 0; n < MAX_SOCKETS; n++)
+ {
+ auto write_socket = llfio::byte_socket_handle::byte_socket(llfio::ip::family::v4, llfio::byte_socket_handle::mode::write,
+ llfio::byte_socket_handle::caching::all, llfio::byte_socket_handle::flag::multiplexable)
+ .value();
+ write_socket.connect(endpoint).value();
+ std::pair<llfio::byte_socket_handle, llfio::ip::address> read_socket;
+ serversocket.read({read_socket}).value();
+ read_socket.first.set_multiplexer(multiplexer.get()).value();
+ async_reads.push_back(checking_receiver(n, multiplexer, received_for));
+ read_sockets.push_back(std::move(read_socket.first));
+ write_sockets.push_back(std::move(write_socket));
+ }
+ auto writerthread = std::async([&] {
+ std::this_thread::sleep_for(std::chrono::milliseconds(500));
+ for(size_t n = MAX_SOCKETS - 1; n < MAX_SOCKETS; n--)
+ {
+ auto r = write_sockets[n].write(0, {{(llfio::byte *) &n, sizeof(n)}});
+ if(!r)
+ {
+ abort();
+ }
+ }
+ });
+ // Start the socket reads. They cannot move in memory until complete
+ for(size_t n = 0; n < MAX_SOCKETS; n++)
+ {
+ async_reads[n].read_begin(multiplexer, read_sockets[n]).value();
+ }
+ // Wait for all reads to complete
+ for(size_t n = 0; n < MAX_SOCKETS; n++)
+ {
+ // Spin until this i/o completes
+ for(;;)
+ {
+ auto state = multiplexer->check_io_operation(async_reads[n].io_state);
+ if(is_completed(state) || is_finished(state))
+ {
+ break;
+ }
+ }
+ }
+ for(size_t n = 0; n < MAX_SOCKETS; n++)
+ {
+ BOOST_CHECK(received_for[n] == 1);
+ }
+ // Wait for all reads to finish
+ for(size_t n = 0; n < MAX_SOCKETS; n++)
+ {
+ llfio::io_operation_state_type state;
+ while(!is_finished(state = async_reads[n].io_state->current_state()))
+ {
+ multiplexer->check_for_any_completed_io().value();
+ }
+ }
+ writerthread.get();
+ };
+#ifdef _WIN32
+ std::cout << "\nSingle threaded IOCP, immediate completions:\n";
+ test_multiplexer(llfio::test::multiplexer_win_iocp(1, false).value());
+ std::cout << "\nSingle threaded IOCP, reactor completions:\n";
+ test_multiplexer(llfio::test::multiplexer_win_iocp(1, true).value());
+ std::cout << "\nMultithreaded IOCP, immediate completions:\n";
+ test_multiplexer(llfio::test::multiplexer_win_iocp(2, false).value());
+ std::cout << "\nMultithreaded IOCP, reactor completions:\n";
+ test_multiplexer(llfio::test::multiplexer_win_iocp(2, true).value());
+#else
+#error Not implemented yet
+#endif
+}
+
+#if LLFIO_ENABLE_COROUTINES
+static inline void TestCoroutinedSocketHandles()
+{
+ static constexpr size_t MAX_SOCKETS = 70;
+ namespace llfio = LLFIO_V2_NAMESPACE;
+ auto test_multiplexer = [](llfio::byte_io_multiplexer_ptr multiplexer) {
+ struct coroutine
+ {
+ llfio::byte_socket_handle read_socket, write_socket;
+ size_t received_for{0};
+
+ explicit coroutine(llfio::byte_socket_handle &&r, llfio::byte_socket_handle &&w)
+ : read_socket(std::move(r))
+ , write_socket(std::move(w))
+ {
+ }
+ llfio::eager<llfio::result<void>> operator()()
+ {
+ union
+ {
+ llfio::byte _buffer[sizeof(size_t)];
+ size_t _index;
+ };
+ llfio::byte_socket_handle::buffer_type buffer;
+ for(;;)
+ {
+ buffer = {_buffer, sizeof(_buffer)};
+ // This will never return if the coroutine gets cancelled
+ auto r = co_await read_socket.co_read({{&buffer, 1}, 0});
+ if(!r)
+ {
+ co_return std::move(r).error();
+ }
+ BOOST_CHECK(r.value().size() == 1);
+ BOOST_CHECK(r.value()[0].size() == sizeof(_buffer));
+ received_for += _index;
+ }
+ }
+ };
+ auto serversocket =
+ llfio::listening_socket_handle::listening_socket(true, llfio::listening_socket_handle::mode::write, llfio::byte_socket_handle::caching::all,
+ llfio::byte_socket_handle::flag::multiplexable)
+ .value();
+ serversocket.bind(llfio::ip::address_v4::loopback()).value();
+ auto endpoint = serversocket.local_endpoint().value();
+ std::cout << "Server socket is listening on " << endpoint << std::endl;
+ if(endpoint.family() == llfio::ip::family::unknown && getenv("CI") != nullptr)
+ {
+ std::cout << "\nNOTE: Currently on CI and couldn't bind a listening socket to loopback, assuming it is CI host restrictions and skipping this test."
+ << std::endl;
+ return;
+ }
+
+ std::vector<coroutine> coroutines;
+ for(size_t n = 0; n < MAX_SOCKETS; n++)
+ {
+ auto write_socket = llfio::byte_socket_handle::byte_socket(llfio::ip::family::v4, llfio::byte_socket_handle::mode::write,
+ llfio::byte_socket_handle::caching::all, llfio::byte_socket_handle::flag::multiplexable)
+ .value();
+ write_socket.connect(endpoint).value();
+ std::pair<llfio::byte_socket_handle, llfio::ip::address> read_socket;
+ serversocket.read({read_socket}).value();
+ read_socket.first.set_multiplexer(multiplexer.get()).value();
+ coroutines.push_back(coroutine(std::move(read_socket.first), std::move(write_socket)));
+ }
+ // Start the coroutines, all of whom will begin a read and then suspend
+ std::vector<llfio::optional<llfio::eager<llfio::result<void>>>> states(MAX_SOCKETS);
+ for(size_t n = 0; n < MAX_SOCKETS; n++)
+ {
+ states[n].emplace(coroutines[n]());
+ }
+ // Write to all the sockets, then pump coroutine resumption until all completions done
+ size_t count = 0, failures = 0;
+ for(size_t i = 1; i <= 10; i++)
+ {
+ std::cout << "\nWrite no " << i << std::endl;
+ for(size_t n = MAX_SOCKETS - 1; n < MAX_SOCKETS; n--)
+ {
+ coroutines[n].write_socket.write(0, {{(llfio::byte *) &i, sizeof(i)}}).value();
+ }
+ // Take a copy of all pending i/o
+ for(;;)
+ {
+ // Have the kernel tell me when an i/o completion is ready
+ auto r = multiplexer->check_for_any_completed_io();
+ if(r.value().initiated_ios_completed == 0 && r.value().initiated_ios_finished == 0)
+ {
+ break;
+ }
+ }
+ count += i;
+ for(size_t n = 0; n < MAX_SOCKETS; n++)
+ {
+ if(coroutines[n].received_for != count)
+ {
+ std::cout << "Coroutine " << n << " has count " << coroutines[n].received_for << " instead of " << count << std::endl;
+ failures++;
+ }
+ BOOST_CHECK(coroutines[n].received_for == count);
+ }
+ BOOST_REQUIRE(failures == 0);
+ }
+ // Rethrow any failures
+ for(size_t n = 0; n < MAX_SOCKETS; n++)
+ {
+ if(states[n]->await_ready())
+ {
+ states[n]->await_resume().value();
+ }
+ }
+ // Destruction of coroutines when they are suspended must work.
+ // This will cancel any pending i/o and immediately exit the
+ // coroutines
+ states.clear();
+ // Now clear all the coroutines
+ coroutines.clear();
+ };
+#ifdef _WIN32
+ std::cout << "\nSingle threaded IOCP, immediate completions:\n";
+ test_multiplexer(llfio::test::multiplexer_win_iocp(1, false).value());
+ std::cout << "\nSingle threaded IOCP, reactor completions:\n";
+ test_multiplexer(llfio::test::multiplexer_win_iocp(1, true).value());
+ std::cout << "\nMultithreaded IOCP, immediate completions:\n";
+ test_multiplexer(llfio::test::multiplexer_win_iocp(2, false).value());
+ std::cout << "\nMultithreaded IOCP, reactor completions:\n";
+ test_multiplexer(llfio::test::multiplexer_win_iocp(2, true).value());
+#else
+#error Not implemented yet
+#endif
+}
+#endif
+#endif
+
+KERNELTEST_TEST_KERNEL(integration, llfio, ip, address, "Tests that llfio::ip::address works as expected", TestSocketAddress())
+KERNELTEST_TEST_KERNEL(integration, llfio, socket_handle, blocking, "Tests that blocking llfio::byte_socket_handle works as expected",
+ TestBlockingSocketHandles())
+KERNELTEST_TEST_KERNEL(integration, llfio, socket_handle, nonblocking, "Tests that nonblocking llfio::byte_socket_handle works as expected",
+ TestNonBlockingSocketHandles())
+#if LLFIO_ENABLE_TEST_IO_MULTIPLEXERS
+KERNELTEST_TEST_KERNEL(integration, llfio, socket_handle, multiplexed, "Tests that multiplexed llfio::byte_socket_handle works as expected",
+ TestMultiplexedSocketHandles())
+#if LLFIO_ENABLE_COROUTINES
+KERNELTEST_TEST_KERNEL(integration, llfio, socket_handle, coroutined, "Tests that coroutined llfio::byte_socket_handle works as expected",
+ TestCoroutinedSocketHandles())
+#endif
+#endif
diff --git a/test/tests/dynamic_thread_pool_group.cpp b/test/tests/dynamic_thread_pool_group.cpp
index 99ae5951..e4aa95f2 100644
--- a/test/tests/dynamic_thread_pool_group.cpp
+++ b/test/tests/dynamic_thread_pool_group.cpp
@@ -454,7 +454,7 @@ static inline void TestDynamicThreadPoolGroupIoAwareWorks()
struct shared_state_t
{
llfio::file_handle h;
- llfio::dynamic_thread_pool_group::io_aware_work_item::io_handle_awareness awareness;
+ llfio::dynamic_thread_pool_group::io_aware_work_item::byte_io_handle_awareness awareness;
std::atomic<size_t> concurrency{0}, max_concurrency{0};
std::atomic<uint64_t> current_pacing{0};
} shared_state;
diff --git a/test/tests/pipe_handle.cpp b/test/tests/pipe_handle.cpp
index 31bf8172..56d3f95d 100644
--- a/test/tests/pipe_handle.cpp
+++ b/test/tests/pipe_handle.cpp
@@ -99,10 +99,10 @@ static inline void TestMultiplexedPipeHandle()
{
static constexpr size_t MAX_PIPES = 64;
namespace llfio = LLFIO_V2_NAMESPACE;
- auto test_multiplexer = [](llfio::io_multiplexer_ptr multiplexer) {
+ auto test_multiplexer = [](llfio::byte_io_multiplexer_ptr multiplexer) {
std::vector<llfio::pipe_handle> read_pipes, write_pipes;
std::vector<size_t> received_for(MAX_PIPES);
- struct checking_receiver final : public llfio::io_multiplexer::io_operation_state_visitor
+ struct checking_receiver final : public llfio::byte_io_multiplexer::io_operation_state_visitor
{
size_t myindex;
std::unique_ptr<llfio::byte[]> io_state_ptr;
@@ -112,9 +112,9 @@ static inline void TestMultiplexedPipeHandle()
size_t _index;
};
llfio::pipe_handle::buffer_type buffer;
- llfio::io_multiplexer::io_operation_state *io_state{nullptr};
+ llfio::byte_io_multiplexer::io_operation_state *io_state{nullptr};
- checking_receiver(size_t _myindex, llfio::io_multiplexer_ptr &multiplexer, std::vector<size_t> &r)
+ checking_receiver(size_t _myindex, llfio::byte_io_multiplexer_ptr &multiplexer, std::vector<size_t> &r)
: myindex(_myindex)
, io_state_ptr(std::make_unique<llfio::byte[]>(multiplexer->io_state_requirements().first))
, received_for(r)
@@ -140,7 +140,7 @@ static inline void TestMultiplexedPipeHandle()
}
// Initiated the read
- llfio::result<void> read_begin(llfio::io_multiplexer_ptr &multiplexer, llfio::io_handle &h)
+ llfio::result<void> read_begin(llfio::byte_io_multiplexer_ptr &multiplexer, llfio::byte_io_handle &h)
{
if(io_state != nullptr)
{
@@ -154,10 +154,10 @@ static inline void TestMultiplexedPipeHandle()
}
// Called if the read did not complete immediately
- virtual void read_initiated(llfio::io_multiplexer::io_operation_state::lock_guard & /*g*/, llfio::io_operation_state_type /*former*/) override { std::cout << " Pipe " << myindex << " will complete read later" << std::endl; }
+ virtual void read_initiated(llfio::byte_io_multiplexer::io_operation_state::lock_guard & /*g*/, llfio::io_operation_state_type /*former*/) override { std::cout << " Pipe " << myindex << " will complete read later" << std::endl; }
// Called when the read completes
- virtual bool read_completed(llfio::io_multiplexer::io_operation_state::lock_guard & /*g*/, llfio::io_operation_state_type former, llfio::pipe_handle::io_result<llfio::pipe_handle::buffers_type> &&res) override
+ virtual bool read_completed(llfio::byte_io_multiplexer::io_operation_state::lock_guard & /*g*/, llfio::io_operation_state_type former, llfio::pipe_handle::io_result<llfio::pipe_handle::buffers_type> &&res) override
{
if(is_initialised(former))
{
@@ -181,7 +181,7 @@ static inline void TestMultiplexedPipeHandle()
}
// Called when the state for the read can be disposed
- virtual void read_finished(llfio::io_multiplexer::io_operation_state::lock_guard & /*g*/, llfio::io_operation_state_type former) override
+ virtual void read_finished(llfio::byte_io_multiplexer::io_operation_state::lock_guard & /*g*/, llfio::io_operation_state_type former) override
{
std::cout << " Pipe " << myindex << " read finishes" << std::endl;
BOOST_REQUIRE(former == llfio::io_operation_state_type::read_completed);
@@ -259,7 +259,7 @@ static inline void TestCoroutinedPipeHandle()
{
static constexpr size_t MAX_PIPES = 70;
namespace llfio = LLFIO_V2_NAMESPACE;
- auto test_multiplexer = [](llfio::io_multiplexer_ptr multiplexer) {
+ auto test_multiplexer = [](llfio::byte_io_multiplexer_ptr multiplexer) {
struct coroutine
{
llfio::pipe_handle read_pipe, write_pipe;