|
14 | 14 | #include <ylt/coro_io/coro_io.hpp> |
15 | 15 | #include <ylt/coro_io/io_context_pool.hpp> |
16 | 16 |
|
| 17 | +#if !defined(ASIO_WINDOWS) |
| 18 | +#include <fcntl.h> |
| 19 | +#include <unistd.h> |
| 20 | +#include <cstdlib> |
| 21 | +#include <cstring> |
| 22 | +#endif |
| 23 | + |
17 | 24 | namespace fs = std::filesystem; |
18 | 25 | using namespace coro_io; |
19 | 26 |
|
@@ -923,3 +930,153 @@ TEST_CASE("large_file_write_with_pool_test") { |
923 | 930 | file.close(); |
924 | 931 | fs::remove(fs::path(filename)); |
925 | 932 | } |
| 933 | + |
| 934 | +#if !defined(ASIO_WINDOWS) |
| 935 | +constexpr size_t DIRECT_IO_ALIGNMENT = 512; |
| 936 | + |
| 937 | +inline void* aligned_alloc_direct_io(size_t size) { |
| 938 | + void* ptr = nullptr; |
| 939 | + if (posix_memalign(&ptr, DIRECT_IO_ALIGNMENT, size) != 0) { |
| 940 | + return nullptr; |
| 941 | + } |
| 942 | + return ptr; |
| 943 | +} |
| 944 | + |
| 945 | +inline void aligned_free_direct_io(void* ptr) { |
| 946 | + free(ptr); |
| 947 | +} |
| 948 | + |
| 949 | +inline size_t align_offset_for_direct_io(size_t offset) { |
| 950 | + return (offset / DIRECT_IO_ALIGNMENT) * DIRECT_IO_ALIGNMENT; |
| 951 | +} |
| 952 | + |
| 953 | +inline size_t align_size_for_direct_io(size_t size) { |
| 954 | + return ((size + DIRECT_IO_ALIGNMENT - 1) / DIRECT_IO_ALIGNMENT) * DIRECT_IO_ALIGNMENT; |
| 955 | +} |
| 956 | + |
| 957 | +template <coro_io::execution_type execute_type> |
| 958 | +void test_direct_io_read_write(std::string_view filename) { |
| 959 | + size_t file_size = 8 * KB; |
| 960 | + create_files({std::string(filename)}, file_size); |
| 961 | + |
| 962 | + auto executor = coro_io::get_global_block_executor(); |
| 963 | + coro_io::basic_random_coro_file<execute_type> file(executor); |
| 964 | + |
| 965 | + bool opened = file.open(filename, std::ios::in | std::ios::out, true); |
| 966 | + if (!opened) { |
| 967 | + WARN("Direct I/O not supported on this filesystem, skipping test"); |
| 968 | + return; |
| 969 | + } |
| 970 | + |
| 971 | + CHECK(file.is_open()); |
| 972 | + |
| 973 | + size_t aligned_size = align_size_for_direct_io(block_size); |
| 974 | + void* aligned_buf = aligned_alloc_direct_io(aligned_size); |
| 975 | + REQUIRE(aligned_buf != nullptr); |
| 976 | + |
| 977 | + struct BufferGuard { |
| 978 | + void* ptr; |
| 979 | + ~BufferGuard() { |
| 980 | + if (ptr) { |
| 981 | + aligned_free_direct_io(ptr); |
| 982 | + } |
| 983 | + } |
| 984 | + } guard{aligned_buf}; |
| 985 | + |
| 986 | + char* buf = static_cast<char*>(aligned_buf); |
| 987 | + |
| 988 | + size_t aligned_offset = align_offset_for_direct_io(0); |
| 989 | + size_t aligned_read_size = align_size_for_direct_io(block_size); |
| 990 | + |
| 991 | + auto [ec, bytes_read] = async_simple::coro::syncAwait( |
| 992 | + file.async_read_at(aligned_offset, buf, aligned_read_size)); |
| 993 | + |
| 994 | + CHECK(!ec); |
| 995 | + CHECK(bytes_read == aligned_read_size); |
| 996 | + CHECK(!file.eof()); |
| 997 | + |
| 998 | + std::memset(buf, 'X', aligned_read_size); |
| 999 | + |
| 1000 | + auto [write_ec, bytes_written] = async_simple::coro::syncAwait( |
| 1001 | + file.async_write_at(aligned_offset, std::string_view(buf, aligned_read_size))); |
| 1002 | + |
| 1003 | + CHECK(!write_ec); |
| 1004 | + CHECK(bytes_written == aligned_read_size); |
| 1005 | + |
| 1006 | + std::memset(buf, 0, aligned_read_size); |
| 1007 | + auto [read_ec, read_bytes] = async_simple::coro::syncAwait( |
| 1008 | + file.async_read_at(aligned_offset, buf, aligned_read_size)); |
| 1009 | + |
| 1010 | + CHECK(!read_ec); |
| 1011 | + CHECK(read_bytes == aligned_read_size); |
| 1012 | + |
| 1013 | + for (size_t i = 0; i < aligned_read_size; ++i) { |
| 1014 | + CHECK(buf[i] == 'X'); |
| 1015 | + } |
| 1016 | + |
| 1017 | + for (size_t offset = 0; offset < file_size - aligned_read_size; offset += aligned_read_size) { |
| 1018 | + size_t aligned_off = align_offset_for_direct_io(offset); |
| 1019 | + |
| 1020 | + std::memset(buf, 'A' + (offset / aligned_read_size) % 26, aligned_read_size); |
| 1021 | + auto [w_ec, w_bytes] = async_simple::coro::syncAwait( |
| 1022 | + file.async_write_at(aligned_off, std::string_view(buf, aligned_read_size))); |
| 1023 | + CHECK(!w_ec); |
| 1024 | + CHECK(w_bytes == aligned_read_size); |
| 1025 | + |
| 1026 | + std::memset(buf, 0, aligned_read_size); |
| 1027 | + auto [r_ec, r_bytes] = async_simple::coro::syncAwait( |
| 1028 | + file.async_read_at(aligned_off, buf, aligned_read_size)); |
| 1029 | + CHECK(!r_ec); |
| 1030 | + CHECK(r_bytes == aligned_read_size); |
| 1031 | + |
| 1032 | + char expected = 'A' + (offset / aligned_read_size) % 26; |
| 1033 | + for (size_t i = 0; i < aligned_read_size; ++i) { |
| 1034 | + CHECK(buf[i] == expected); |
| 1035 | + } |
| 1036 | + } |
| 1037 | + |
| 1038 | + file.close(); |
| 1039 | +} |
| 1040 | +#endif |
| 1041 | + |
| 1042 | +#if !defined(ASIO_WINDOWS) |
| 1043 | +TEST_CASE("direct io read write test - thread_pool") { |
| 1044 | + std::string filename = "direct_io_test_thread_pool.tmp"; |
| 1045 | + test_direct_io_read_write<coro_io::execution_type::thread_pool>(filename); |
| 1046 | + fs::remove(fs::path(filename)); |
| 1047 | +} |
| 1048 | +#endif |
| 1049 | + |
| 1050 | +#if defined(ASIO_HAS_FILE) && !defined(ASIO_WINDOWS) |
| 1051 | +TEST_CASE("direct io read write test - native_async") { |
| 1052 | + std::string filename = "direct_io_test_native_async.tmp"; |
| 1053 | + test_direct_io_read_write<coro_io::execution_type::native_async>(filename); |
| 1054 | + fs::remove(fs::path(filename)); |
| 1055 | +} |
| 1056 | +#endif |
| 1057 | + |
| 1058 | +TEST_CASE("direct io alignment test") { |
| 1059 | +#if !defined(ASIO_WINDOWS) |
| 1060 | + CHECK(align_offset_for_direct_io(0) == 0); |
| 1061 | + CHECK(align_offset_for_direct_io(100) == 0); |
| 1062 | + CHECK(align_offset_for_direct_io(512) == 512); |
| 1063 | + CHECK(align_offset_for_direct_io(1000) == 512); |
| 1064 | + CHECK(align_offset_for_direct_io(1024) == 1024); |
| 1065 | + |
| 1066 | + CHECK(align_size_for_direct_io(0) == 0); |
| 1067 | + CHECK(align_size_for_direct_io(100) == 512); |
| 1068 | + CHECK(align_size_for_direct_io(512) == 512); |
| 1069 | + CHECK(align_size_for_direct_io(1000) == 1024); |
| 1070 | + CHECK(align_size_for_direct_io(1024) == 1024); |
| 1071 | + |
| 1072 | + void* ptr1 = aligned_alloc_direct_io(1024); |
| 1073 | + REQUIRE(ptr1 != nullptr); |
| 1074 | + CHECK(reinterpret_cast<uintptr_t>(ptr1) % DIRECT_IO_ALIGNMENT == 0); |
| 1075 | + aligned_free_direct_io(ptr1); |
| 1076 | + |
| 1077 | + void* ptr2 = aligned_alloc_direct_io(4096); |
| 1078 | + REQUIRE(ptr2 != nullptr); |
| 1079 | + CHECK(reinterpret_cast<uintptr_t>(ptr2) % DIRECT_IO_ALIGNMENT == 0); |
| 1080 | + aligned_free_direct_io(ptr2); |
| 1081 | +#endif |
| 1082 | +} |
0 commit comments