diff options
-rw-r--r-- | netfs/fuse/fuseFiles.cpp | 151 | ||||
-rw-r--r-- | netfs/fuse/fuseFiles.h | 15 | ||||
-rw-r--r-- | netfs/unittests/testEdgeCases.cpp | 65 |
3 files changed, 56 insertions, 175 deletions
diff --git a/netfs/fuse/fuseFiles.cpp b/netfs/fuse/fuseFiles.cpp index f47138d..fd313e5 100644 --- a/netfs/fuse/fuseFiles.cpp +++ b/netfs/fuse/fuseFiles.cpp @@ -5,13 +5,13 @@ #include <algorithm> #include <cstring> #include <entCache.h> -#include <functional> #include <future> -#include <iterator> #include <memory> #include <mutex> #include <numeric.h> #include <numeric> +#include <ranges> +#include <span> #include <utility> #include <vector> @@ -164,74 +164,61 @@ namespace NetFS { } } - namespace { - void - blockSizeIterate(FuseApp::OpenFile::BlockSizes blockSizes, auto && callback) - { - if (blockSizes.size1) { - callback(blockSizes.size1); - } - while (blockSizes.countN--) { - callback(blockSizes.sizeN); - } - } - } - int FuseApp::read(const char *, char * buf, size_t size, off_t offset, struct fuse_file_info * fileInfo) { try { + const std::span out(buf, size); using BackgroundOps = std::vector<std::promise<int>>; - auto cpy = [buf](off_t blockOffset, const auto && data) -> int { - std::copy(data.begin(), data.end(), buf + blockOffset); + const auto cpy = [out](off_t blockOffset, const auto && data) -> int { + std::ranges::copy(data, out.begin() + blockOffset); return safe {data.size()}; }; - auto collateTotal = [](auto && ops) { + const auto collateTotal = [](auto && ops) { return std::accumulate(ops.begin(), ops.end(), 0, [](auto && total, auto & operation) { return total += operation.get_future().get(); }); }; auto openFile = getProxy<OpenFilePtr>(fileInfo->fh); auto remote = openFile->remote; - auto blockSizes = openFile->blockSizes(size); if (fcr->Async) { - BackgroundOps ops(blockSizes.count()); - blockSizeIterate(blockSizes, - [&offset, &openFile, &remote, blockOffset = 0U, &cpy, opIter = ops.begin()]( - safe<size_t> blockSize) mutable { - waitOnWriteRangeAndThen(blockSize, offset, openFile, - [thisOp = opIter++, offset, blockOffset, blockSize, &remote, &cpy](const auto &) { + const auto blocks = out | std::views::chunk(openFile->bodyMaxSize); + BackgroundOps ops(blocks.size()); + std::ranges::for_each( + blocks, [offset, &openFile, &remote, &cpy, opIter = ops.begin(), out](auto && block) mutable { + waitOnWriteRangeAndThen(block.size(), offset, openFile, + [thisOp = opIter++, offset, &remote, &cpy, block, out](const auto &) { + const auto outPosition = block.begin() - out.begin(); + const auto position = offset + outPosition; return remote->readAsync( - offset, blockSize, - [cpy, thisOp, blockOffset](auto && resultBuf) { + position, safe(block.size()), + [cpy, thisOp, outPosition](auto && resultBuf) { thisOp->set_value( - cpy(blockOffset, std::forward<Ice::ByteSeq>(resultBuf))); + cpy(outPosition, std::forward<Ice::ByteSeq>(resultBuf))); }, [thisOp](auto error) { thisOp->set_exception(std::move(error)); }); }); - offset += blockSize; - blockOffset += blockSize; }); return collateTotal(ops); } if (openFile->bodyMaxSize < size) { - BackgroundOps ops(blockSizes.count()); - blockSizeIterate(blockSizes, - [&offset, &remote, blockOffset = 0U, &cpy, opIter = ops.begin()]( - safe<size_t> blockSize) mutable { + const auto blocks = out | std::views::chunk(openFile->bodyMaxSize); + BackgroundOps ops(blocks.size()); + std::ranges::for_each( + blocks, [offset, &remote, out, &cpy, opIter = ops.begin()](auto && block) mutable { auto thisOp = opIter++; + const auto outPosition = block.begin() - out.begin(); + const auto position = offset + outPosition; remote->readAsync( - offset, blockSize, - [cpy, thisOp, blockOffset](auto && resultBuf) { - thisOp->set_value(cpy(blockOffset, std::forward<Ice::ByteSeq>(resultBuf))); + position, safe(block.size()), + [cpy, thisOp, outPosition](auto && resultBuf) { + thisOp->set_value(cpy(outPosition, std::forward<Ice::ByteSeq>(resultBuf))); }, [thisOp](auto error) { thisOp->set_exception(std::move(error)); }); - offset += blockSize; - blockOffset += blockSize; }); return collateTotal(ops); } @@ -245,44 +232,47 @@ namespace NetFS { int FuseApp::write(const char *, const char * buf, size_t size, off_t offset, struct fuse_file_info * fileInfo) { + static auto toBuffer = [](auto block) { + return std::make_pair(std::to_address(block.begin()), std::to_address(block.end())); + }; try { auto openFile = getProxy<OpenFilePtr>(fileInfo->fh); auto remote = openFile->remote; - auto bytes = reinterpret_cast<const ::Ice::Byte *>(buf); + const std::span bytes {reinterpret_cast<const ::Ice::Byte *>(buf), size}; if (fcr->Async) { - blockSizeIterate( - openFile->blockSizes(size), [&bytes, &offset, &remote, &openFile](safe<size_t> blockSize) { - waitOnWriteRangeAndThen(blockSize, offset, openFile, - [offset, blockSize, bytes, &openFile, remote](const auto & key) { - auto p = std::make_shared<OpenFile::WriteState>(); - openFile->bg.insert({key, p}); - remote->writeAsync( - offset, blockSize, std::make_pair(bytes, bytes + blockSize), - [p, openFile, key]() { - p->promise.set_value(); - ScopeLock(openFile->mutex) { - openFile->bg.erase(key); - } - }, - [p, openFile](auto error) { - p->promise.set_exception(std::move(error)); - }); - }); - bytes += blockSize; - offset += blockSize; - }); + const auto blocks = bytes | std::views::chunk(openFile->bodyMaxSize); + std::ranges::for_each(blocks, [&remote, bytes, offset, &openFile](auto && block) { + const auto position = offset + (block.begin() - bytes.begin()); + waitOnWriteRangeAndThen( + block.size(), position, openFile, [position, block, &openFile, remote](const auto & key) { + auto pendingWrite = std::make_shared<OpenFile::WriteState>(); + openFile->bg.insert({key, pendingWrite}); + remote->writeAsync( + position, safe(block.size()), toBuffer(block), + [pendingWrite, openFile, key]() { + pendingWrite->promise.set_value(); + ScopeLock(openFile->mutex) { + openFile->bg.erase(key); + } + }, + [pendingWrite, openFile](auto error) { + pendingWrite->promise.set_exception(std::move(error)); + }); + }); + }); } else if (openFile->bodyMaxSize < size) { + const auto blocks = bytes | std::views::chunk(openFile->bodyMaxSize); std::vector<std::future<void>> ops; - blockSizeIterate(openFile->blockSizes(size), [&ops, &bytes, &offset, &remote](safe<size_t> blockSize) { - ops.emplace_back(remote->writeAsync(offset, blockSize, std::make_pair(bytes, bytes + blockSize))); - bytes += blockSize; - offset += blockSize; + ops.reserve(blocks.size()); + std::ranges::transform(blocks, std::back_inserter(ops), [&remote, bytes, offset](auto && block) { + const auto position = offset + (block.begin() - bytes.begin()); + return remote->writeAsync(position, safe(block.size()), toBuffer(block)); }); std::ranges::for_each(ops, &std::future<void>::get); } else { - remote->write(offset, safe {size}, std::make_pair(bytes, bytes + size)); + remote->write(offset, safe {size}, toBuffer(bytes)); } return safe {size}; } @@ -291,35 +281,6 @@ namespace NetFS { } } - FuseApp::OpenFile::BlockSizes - FuseApp::OpenFile::blockSizes(size_t total, size_t max) noexcept - { - if (max >= total) { // Simple case, all in remainder block - return {.size1 = total, .sizeN = 0, .countN = 0}; - } - if (total % max == 0) { // Simplish case, multiples of max - return {.size1 = 0, .sizeN = max, .countN = total / max}; - } - const auto blocks = (total / max); - const auto blockSize = (total / (blocks + 1)) + 1; - const auto batchTotal = blockSize * blocks; - const auto remainder = total - batchTotal; - - return {.size1 = remainder, .sizeN = blockSize, .countN = blocks}; - } - - size_t - FuseApp::OpenFile::BlockSizes::total() const noexcept - { - return size1 + (sizeN * countN); - } - - size_t - FuseApp::OpenFile::BlockSizes::count() const noexcept - { - return countN + (size1 ? 1 : 0); - } - ssize_t FuseApp::copy_file_range(const char *, struct fuse_file_info * fileInfoIn, off_t offsetIn, const char *, struct fuse_file_info * fileInfoOut, off_t offsetOut, size_t size, int flags) diff --git a/netfs/fuse/fuseFiles.h b/netfs/fuse/fuseFiles.h index 9742236..672d30f 100644 --- a/netfs/fuse/fuseFiles.h +++ b/netfs/fuse/fuseFiles.h @@ -11,21 +11,6 @@ namespace NetFS { void flush(); void wait() const; - struct BlockSizes { - size_t size1, sizeN, countN; - auto operator<=>(const BlockSizes &) const = default; - [[nodiscard]] size_t total() const noexcept; - [[nodiscard]] size_t count() const noexcept; - }; - - [[nodiscard]] static BlockSizes blockSizes(size_t total, size_t max) noexcept; - - [[nodiscard]] auto - blockSizes(size_t total) const noexcept - { - return blockSizes(total, bodyMaxSize); - } - FilePrxPtr remote; const std::string path; const int flags; diff --git a/netfs/unittests/testEdgeCases.cpp b/netfs/unittests/testEdgeCases.cpp index 07f2c18..5eb8d5d 100644 --- a/netfs/unittests/testEdgeCases.cpp +++ b/netfs/unittests/testEdgeCases.cpp @@ -127,71 +127,6 @@ BOOST_AUTO_TEST_CASE(manyThreads) BOOST_CHECK_EQUAL(failure, 0); } -namespace std { - ostream & - operator<<(ostream & s, const FuseMock::OpenFile::BlockSizes & b) - { - return s << '(' << b.size1 << '+' << b.sizeN << '*' << b.countN << ')'; - } -} - -BOOST_AUTO_TEST_CASE(blockSizes) -{ - using BS = NetFS::FuseApp::OpenFile::BlockSizes; - BOOST_CHECK_EQUAL(NetFS::FuseApp::OpenFile::blockSizes(1, 1), (BS {1, 0, 0})); - BOOST_CHECK_EQUAL(NetFS::FuseApp::OpenFile::blockSizes(1, 5), (BS {1, 0, 0})); - BOOST_CHECK_EQUAL(NetFS::FuseApp::OpenFile::blockSizes(5, 5), (BS {5, 0, 0})); - BOOST_CHECK_EQUAL(NetFS::FuseApp::OpenFile::blockSizes(0, 1), (BS {0, 0, 0})); - BOOST_CHECK_EQUAL(NetFS::FuseApp::OpenFile::blockSizes(5, 2), (BS {1, 2, 2})); - BOOST_CHECK_EQUAL(NetFS::FuseApp::OpenFile::blockSizes(5, 3), (BS {2, 3, 1})); - BOOST_CHECK_EQUAL(NetFS::FuseApp::OpenFile::blockSizes(29, 10), (BS {9, 10, 2})); - BOOST_CHECK_EQUAL(NetFS::FuseApp::OpenFile::blockSizes(30, 10), (BS {0, 10, 3})); - BOOST_CHECK_EQUAL(NetFS::FuseApp::OpenFile::blockSizes(31, 10), (BS {7, 8, 3})); - BOOST_CHECK_EQUAL(NetFS::FuseApp::OpenFile::blockSizes(32, 10), (BS {5, 9, 3})); - BOOST_CHECK_EQUAL(NetFS::FuseApp::OpenFile::blockSizes(33, 10), (BS {6, 9, 3})); - BOOST_CHECK_EQUAL(NetFS::FuseApp::OpenFile::blockSizes(34, 10), (BS {7, 9, 3})); - BOOST_CHECK_EQUAL(NetFS::FuseApp::OpenFile::blockSizes(35, 10), (BS {8, 9, 3})); - BOOST_CHECK_EQUAL(NetFS::FuseApp::OpenFile::blockSizes(36, 10), (BS {6, 10, 3})); - BOOST_CHECK_EQUAL(NetFS::FuseApp::OpenFile::blockSizes(37, 10), (BS {7, 10, 3})); - BOOST_CHECK_EQUAL(NetFS::FuseApp::OpenFile::blockSizes(38, 10), (BS {8, 10, 3})); - BOOST_CHECK_EQUAL(NetFS::FuseApp::OpenFile::blockSizes(39, 10), (BS {9, 10, 3})); - BOOST_CHECK_EQUAL(NetFS::FuseApp::OpenFile::blockSizes(40, 10), (BS {0, 10, 4})); - BOOST_CHECK_EQUAL(NetFS::FuseApp::OpenFile::blockSizes(41, 10), (BS {5, 9, 4})); - BOOST_CHECK_EQUAL(NetFS::FuseApp::OpenFile::blockSizes(1234, 100), (BS {94, 95, 12})); - BOOST_CHECK_EQUAL(NetFS::FuseApp::OpenFile::blockSizes(1235, 100), (BS {83, 96, 12})); - BOOST_CHECK_EQUAL(NetFS::FuseApp::OpenFile::blockSizes(1236, 100), (BS {84, 96, 12})); - BOOST_CHECK_EQUAL(NetFS::FuseApp::OpenFile::blockSizes(1237, 100), (BS {85, 96, 12})); - BOOST_CHECK_EQUAL(NetFS::FuseApp::OpenFile::blockSizes(1238, 100), (BS {86, 96, 12})); - BOOST_CHECK_EQUAL(NetFS::FuseApp::OpenFile::blockSizes(1239, 100), (BS {87, 96, 12})); - BOOST_CHECK_EQUAL(NetFS::FuseApp::OpenFile::blockSizes(1240, 100), (BS {88, 96, 12})); - BOOST_CHECK_EQUAL(NetFS::FuseApp::OpenFile::blockSizes(1241, 100), (BS {89, 96, 12})); -} - -BOOST_DATA_TEST_CASE(blockSizes_range, - boost::unit_test::data::xrange<size_t>(1, 200) - * boost::unit_test::data::make<size_t>({1, 5, 10, 16, 32, 256, 1024}), - total, max) -{ - const auto bs = NetFS::FuseApp::OpenFile::blockSizes(total, max); - BOOST_CHECK_LE(bs.size1, max); - BOOST_CHECK_LE(bs.sizeN, max); - BOOST_CHECK_EQUAL(bs.total(), total); - BOOST_CHECK_NE(bs.size1, bs.sizeN); -} - -BOOST_DATA_TEST_CASE(blockSizes_scatter, - (boost::unit_test::data::xrange<size_t>(1, 200, 17LU) + boost::unit_test::data::xrange<size_t>(1200, 1300, 3LU)) - * (boost::unit_test::data::xrange<size_t>(1, 100) - + boost::unit_test::data::xrange<size_t>(256, 4096, 256LU)), - total, max) -{ - const auto bs = NetFS::FuseApp::OpenFile::blockSizes(total, max); - BOOST_CHECK_LE(bs.size1, max); - BOOST_CHECK_LE(bs.sizeN, max); - BOOST_CHECK_EQUAL(bs.total(), total); - BOOST_CHECK_NE(bs.size1, bs.sizeN); -} - BOOST_DATA_TEST_CASE(bigWritesAndReads, boost::unit_test::data::make({"defaultFuseNoAsync.xml", "defaultFuse.xml"}) * boost::unit_test::data::xrange<size_t>(1024UL * 100UL, 4096UL * 1024UL, 512UL * 1024UL) |