在下面的代码片段中,有办法处理ENOSPC
吗?
#include <fstream>
#include <iostream>
#include <boost/iostreams/filtering_streambuf.hpp>
#include <boost/iostreams/copy.hpp>
#include <boost/iostreams/filter/bzip2.hpp>
// open input file stream of the bzip2 file
std::ifstream ifs("file.bz2");
// open output stream to the "full" device
// full device is a "utility-device" to check how applications handle ENOSPC
// more details in "man full"
std::ofstream ofs("/dev/full");
// Setup the iostreams filter
boost::iostreams::filtering_streambuf<boost::iostreams::output> filters;
filters.push(boost::iostreams::bzip2_decompressor());
filters.push(ofs);
// "run" the filter
boost::iostreams::copy(ifs, filters);
如果我执行已编译二进制文件的strace
,则代码似乎无限地使用相同数据调用writev()
并返回ENOSPC
错误。
writev(4, [{NULL, 0}, {"DATA DATA "..., 4096}], 2) = -1 ENOSPC (No space left on device)
如何处理此错误或将其作为boost::iostreams::copy()
的错误抛出。
是否可以在exceptions()
对象上设置适当的ofstream
?我试过ofs.exceptions(std::ios::badbit | std::ios::failbit)
,但没有任何区别。
上面的代码是用GCC编译的,并在Linux上运行。提升版本1.55。
答案 0 :(得分:3)
它陷入non_blocking_adaptor<Device>::write(...)
的无限循环中:
std::streamsize result = 0;
while (result < n) {
std::streamsize amt =
iostreams::write(device_, s + result, n - result);
result += amt;
}
return result;
iostream::write(device_, ...
一直返回0(所以n保持n,amt,结果保持为0)。
这似乎是Boost IOstreams中的一个错误。也许是在添加了对非阻塞(同步)IO的初步支持时引入的。根据文档,这应该是一项正在进行的工作。
特别启发的是:http://www.boost.org/doc/libs/1_55_0/libs/iostreams/doc/guide/asynchronous.html
过滤器
允许过滤器传播临时失败通知:如果下游设备消耗或产生的字符数少于过滤器请求的字符数,并且因此过滤器无法满足读取或写入请求,则过滤器可能返回表示输入或输出暂时不可用的值。希望这种能力足以允许当前的Filter概念与异步和非阻塞i / o一起使用。 但是,为了对阻止i / o有用,过滤器必须永远不会返回临时失败通知,除非它已从下游设备收到此类通知。通过声明过滤器必须保持阻塞来总结此要求。见阻止。
( bold mine )通过将E_NOSPC条件转换为临时故障通知,看起来好像IOStreams违反了这个原则。
boost::iostreams::copy
特殊情况下源和目标间接设备的情况。在这种情况下,两者都是间接的。现在,特殊情况将接收器包裹在non_blocking_adaptor
中。我不知道为什么,这似乎与从同一文档页面中获得的以下一般建议相矛盾:
流和流缓冲区
虽然Boost.Iostreams过滤器和设备概念可以容纳非阻塞i / o,但C ++标准库流和流缓冲接口不能,因为它们缺乏区分临时和永久失败以满足读或写的手段请求。因此,非阻塞设备无法与模板流,stream_buffer,filtering_stream和filtering_streambuf一起正常工作。
我尝试用file_sink
和file_source
代替文件,但没有变化。 :(
这是我的简化测试用例仍然可以重现问题:
#include <boost/iostreams/copy.hpp>
#include <boost/iostreams/device/file.hpp>
#include <boost/iostreams/filtering_streambuf.hpp>
int main()
{
using namespace boost::iostreams;
file_source ifs("/dev/zero");
file_sink ofs("/dev/full");
filtering_streambuf<output> filters(ofs);
copy(ifs, filters);
}
也许您应该将此报告为开发人员/邮件列表中的错误。
答案 1 :(得分:1)
我想我找到了解决这个问题的方法。我所做的是根据documentation here创建自己的接收器。
基本上是sink
,它会处理写入,然后检查流的good
状态。
struct safe_ofstream_sink
{
typedef char char_type;
typedef boost::iostreams::sink_tag category;
std::ofstream& ofs;
safe_ofstream_sink(std::ofstream& ofs) :
ofs(ofs)
{
}
std::streamsize write(const char* s, std::streamsize n)
{
ofs.write(s, n);
if (!ofs)
throw std::runtime_error("Failed writing to fstream");
return n;
}
};
boost::iostreams::filtering_streambuf<boost::iostreams::output> filters;
filters.push(boost::iostreams::bzip2_decompressor());
filters.push(safe_ofstream_sink(ofs));
boost::iostreams::copy(ifs, filters);