boost :: iostreams :: copy - sink - ENOSPC(设备上没有剩余空间)错误处理

时间:2013-12-06 02:01:50

标签: c++ boost stl boost-iostreams

在下面的代码片段中,有办法处理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。

2 个答案:

答案 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_sinkfile_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);