多线程导致boost::remove_all 提前退出

起因

异地容灾项目遇到一个奇怪的问题, 清理文件目录的时候, 总会清理不干净,导致恢复失败

分析

  1. 日志确认,在调用boost::remove_all 的地方打日志, 发现文件和目录都删了一遍
  2. 可能是文件重复创建了,因为此时文件的ctime 很新, 排查了所有文件处理函数,都没有可能出现创建的效果
  3. 那就是remove_all 的问题

问题定位:

模拟remove_all删除文件,开始单线程一切正常,后来发现我们的项目是多线程的,于是多线程下,remove_all 同时删除,问题复现

原因分析

remove_all 删除目录时,会先迭代删除子目录和文件, 最后删除本身目录, 而多线程下,有子目录被其它线程删的时候, remove_all 异常退出, 不再继续删除, 这导致了目录遗留的情况。 至于文件目录ctime问题,是由于设置权限chmod 时, 会更新ctime。

解决办法

检查remove_all 的返回值,然后判断异常码, 如果返回异常,且异常码为 no_such_file_or_directory 则进行再次删除


-    if (boost::system::errc::success !=
-        fs::remove_all(fs::path(m_abs_path), ec)) {
-      if (ec && ec != boost::system::errc::no_such_file_or_directory) {

boost remove_all 源码

  1. while 循环递归删除
    const fs::directory_iterator end_dit;
    while(itr != end_dit)
    {
      fs::file_type tmp_type = query_file_type(itr->path(), ec);
      if (ec != 0 && *ec)
        return count;

      count += remove_all_aux(itr->path(), tmp_type, ec);
      if (ec != 0 && *ec)
        return count;

      fs::detail::directory_iterator_increment(itr, ec);
      if (ec != 0 && *ec)
        return count;
    }

  remove_file_or_directory(p, type, ec);
  if (ec != 0 && *ec)
    return count;

  return ++count;
  1. 执行删除, 由error 来判断是否抛异常
  if (type == fs::directory_file
#     ifdef BOOST_WINDOWS_API
      || type == fs::_detail_directory_symlink
#     endif
    )
  {
    if (error(!remove_directory(p) ? BOOST_ERRNO : 0, p, ec,
      "boost::filesystem::remove"))
        return false;
  }
  else
  {
    if (error(!remove_file(p) ? BOOST_ERRNO : 0, p, ec,
      "boost::filesystem::remove"))
        return false;
  }
  1. 抛异常的地方, 可见通过ec本身是否为0 来决定是抛异常,还是直接赋值 ec。 这是boost库常见的两种api调用
void emit_error(err_t error_num, const path& p, system::error_code* ec, const char* message)
{
  if (!ec)
    BOOST_FILESYSTEM_THROW(filesystem_error(message, p, system::error_code(error_num, system::system_category())));
  else
    ec->assign(error_num, system::system_category());
}
上一篇:bootstrap模态框的使用


下一篇:「BalticOI 2019 Day1」山谷