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


    起因

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

    分析

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

    问题定位:

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

    原因分析

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

    解决办法

    检查异常码, 如果异常码为 no_such_file_or_directory 则进行再次删除
    如果不是子目录被删的情况,即使指定目录不存在, ec 也不会为 no_such_file_or_directory

          fs::remove_all(fs::path(m_abs_path), ec)) {
          if (ec && ec == boost::system::errc::no_such_file_or_directory) {
              // 重复执行 remove_all  
          }
    
    

    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());
    }
    
  • 相关阅读:
    Markdown标签
    macbook使用
    git的使用
    HTTPS的原理
    javascript中的对象
    javascript中this的指向问题
    javascript中的闭包
    javaScript中的return、break和continue
    Promise对象
    ORACLE_11G归档空间满,由于数据库装完后使用的是默认空间是闪回区
  • 原文地址:https://www.cnblogs.com/hustcpp/p/12985693.html
Copyright © 2020-2023  润新知