-
-
Notifications
You must be signed in to change notification settings - Fork 46
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
file read EOF condition #29
Comments
You are completely right that for For For Does this make sense? |
That was my interpretation, too. I only quoted the
This is the part I wanted to be clarified and I think it is the only sane way to specify it. Which brings me to my actual point: The current llfio/include/llfio/v2.0/detail/impl/windows/io_handle.ipp Lines 91 to 95 in 14d5c7f
which in turn forces me to write an ugly wrapper with a win32 special case, because there is no portable eof inline auto readx(llfio::file_handle &src, std::size_t offset, rw_dynblob buffer) noexcept
-> result<rw_dynblob>
{
llfio::io_handle::buffer_type raw_buffer{buffer.data(), buffer.size()};
if (auto readrx = src.read({{&raw_buffer, 1}, offset}))
{
return rw_dynblob{readrx.assume_value()[0]};
}
else
{
auto ec = make_error_code(readrx.assume_error());
#if defined BOOST_OS_WINDOWS_AVAILABLE
if (ec == std::error_code{38, std::system_category()}) // ERROR_HANDLE_EOF
{
return rw_dynblob{};
}
#endif
return failure(ec);
}
} something I would rather avoid if possible.
Very much so, I think. Thanks for providing the insight. |
Surely that error only occurs if the read offset is wholly beyond the current maximum extent? If so, that exactly matches POSIX on this. Same behaviour. And totally system-introduced. LLFIO tries to avoid fiddling when we can pass through, unless there is an extremely good reason not to (e.g. that race in Win32
Yeah, you're kinda caught there. POSIX returns invalid parameter for when you try to wholly read past EOF. Microsoft do not map
You are totally free to subclass LLFIO classes and override anything you like. It was designed to allow that, so one can solve local weirdnesses like yours. But a more proper solution would be to subclass But fixing it for everybody globally, that's not wise. Microsoft like to change their Win32 error code mappings, as do all the standard library implementations. So LLFIO makes no attempt to fix the many mismappings in all the standard libraries. I do control status code however, and I am happy to fix it there. Raise a bug on https://github.com/ned14/status-code/issues if you'd like that. |
It also occurs for reads with offset = maximum extent, e.g. when I consume the file in 128kiB chunks and the file extent is a multiple of the chunk size. This means that the attached example prints
LLFIO_V2_NAMESPACE::result<void> repro()
{
namespace llfio = LLFIO_V2_NAMESPACE;
auto name = llfio::utils::random_string(8);
OUTCOME_TRY(
o, llfio::file({}, name, llfio::handle::mode::write, llfio::handle::creation::if_needed));
std::vector<std::byte> zeroes(128 * 1024, std::byte{});
OUTCOME_TRY(o.write(0, {{zeroes.data(), zeroes.size()}}));
OUTCOME_TRY(o.close());
OUTCOME_TRY(i, llfio::file({}, name));
llfio::file_handle::extent_type offset{0};
OUTCOME_TRY(num_read, i.read(0, {{zeroes.data(), zeroes.size()}}));
offset += num_read;
if (auto rx = i.read(offset, {{zeroes.data(), zeroes.size()}}))
{
std::cout << "successfully read " << rx.assume_value() << "B\n";
}
else
{
auto ec = std::move(rx).assume_error();
std::cout << "failed with code " << ec.value() << "; " << ec.message() << "\n";
}
OUTCOME_TRY(i.unlink());
return llfio::success();
} |
I did some more research into this, and what happens if you read at or beyond EOF varies between systems:
So Windows isn't being weird here. It chose to track SunOS, which makes sense given its vintage. I'm minded to leave in the platform-specific behaviour, but add in a note saying you're into implementation defined behaviour >= EOF. Is this okay with you? |
Well, I'm not particularly happy about specifying it as implementation defined, but I can deal with it. In this case I would really appreciate the ability to suppress the EOF errors from the llfio log, because currently a myriad of EOF errors are cluttering my logs which is quite unpleasant to work with. However, I would argue that all platforms provide a way to cleanly detect the EOF condition, therefore there ought to be a portable interface for it in the platform abstraction layer. Maybe we could add an API to |
Firstly, that's a great idea about filtering certain errors from the log. I too have suffered from too much log detail. I'll honestly admit that it hadn't occurred to me to simply add a filter to the log. Thank you. As you have correctly observed, LLFIO avoids mentioning EOF. This is because it's racy. One moment you might experience EOF, a microsecond later you might not. You may have noticed that i/o gets returned using io_result rather than result. This was to enable adding member functions to query an io_result for certain bespoke things. Right not, there is only I can't give an ETA on this work, unfortunately. It's likely after the Cologne meeting, where I hope to get the first tranche of official WG21 feedback on this library. Also, I'm currently working on gutting and replace the signal handling infrastructure in LLFIO based on WG21 feedback, which has the side effect of a major refactor of how completion handlers are stored and invoked. So, I am technically working on LLFIO, just indirectly for now. This choice of priority is because there was surprising warmth for a complete replacement for signal handling on all three of WG21, WG14 and POSIX. Literally everybody was "bring it on already", which surprised me. |
Yeah, that would be perfectly fine with me.
I'm not in a hurry and just using my current patch set will suffice for some time. Signal handlers are a sore point which I always avoid to touch if possible; very kind of you to improve that situation. |
I am unsure how to portably detect an EOF condition for a
file_handle
in cases where I sequentially consume the whole file, e.g. for computing its hash or copying its content. My usual approach is to open the file and just loop until the read indicates the EOF condition (e.g.pread()
returning 0 orReadFile()
failing withERROR_HANDLE_EOF
).P1031r2 states in its
io_handle::read()
specification, and I quote:So I would expect that a
read()
call at the file end either (waits until new data is appended or the deadline expires) or (fails with an error which can be portably distinguished). However, the specification also states:Which I assume to loosen the specification to implementation defined behavior in my case. In order to stay portable I would need to check the maximum file extent beforehand which leads me into TOCTOU land.
The current reference implementation just passes through the behavior described in the first paragraph.
All in all it seems to me either that the case is underspecified for plain files or that I missed something. Maybe specifying the posix
pread()
behavior and aligning the windows implementation is a reasonable solution?The text was updated successfully, but these errors were encountered: