diff --git a/src/cache_node.cpp b/src/cache_node.cpp index f1410ad..318176f 100644 --- a/src/cache_node.cpp +++ b/src/cache_node.cpp @@ -871,12 +871,42 @@ bool DirStatCache::isRemovableHasLock() } std::lock_guard dircachelock(dir_cache_lock); - if(!children.empty()){ + if(HasExistedChildHasLock()){ return false; } return true; } +bool DirStatCache::HasExistedChildHasLock() +{ + // [FIXME] + // This for statement will result in an error saying that it can be + // replaced with std::any_of using clang-tidy. + // However, if we replace this loop with std::any_of as shown below, + // an error will occur in thread_safety. + // + // std::any_of(children.begin(), children.end(), [](const auto& pair){ return !pair.second->isNegativeHasLock(); }); + // + // This is because that when using as std::any_of algorithms with + // lambda expressions, static analysis assumes that "the lambda may + // be called outside the scope of the caller." + // As a result, it concludes that there is no guarantee that the + // mutex is held within the lambda (a limitation of the analysis). + // + // Therefore, until this false positive is resolved, we use + // NOLINTNEXTLINE(readability-use-anyofallof) to avoid clang-tidy + // errors. + // + + // NOLINTNEXTLINE(readability-use-anyofallof) + for(const auto& pair: children){ + if(!pair.second->isNegativeHasLock()){ + return true; + } + } + return false; +} + bool DirStatCache::AddHasLock(const std::string& strpath, const struct stat* pstat, const headers_t* pmeta, objtype_t type, bool is_notruncate) { // Check size @@ -1168,7 +1198,7 @@ bool DirStatCache::IsExpiredHasLock() } std::lock_guard dircachelock(dir_cache_lock); - if(!HasStatHasLock() && !HasMetaHasLock() && children.empty()){ + if(!HasStatHasLock() && !HasMetaHasLock() && !HasExistedChildHasLock()){ // this cache is empty return true; } diff --git a/src/cache_node.h b/src/cache_node.h index a2bd9be..0eeeaaf 100644 --- a/src/cache_node.h +++ b/src/cache_node.h @@ -253,6 +253,7 @@ class DirStatCache : public StatCacheNode bool ClearHasLock() override REQUIRES(StatCacheNode::cache_lock); bool RemoveChildHasLock(const std::string& strpath) override REQUIRES(StatCacheNode::cache_lock); bool isRemovableHasLock() override REQUIRES(StatCacheNode::cache_lock); + bool HasExistedChildHasLock() REQUIRES(StatCacheNode::cache_lock, dir_cache_lock); bool AddHasLock(const std::string& strpath, const struct stat* pstat, const headers_t* pmeta, objtype_t type, bool is_notruncate) override REQUIRES(StatCacheNode::cache_lock);