-
Notifications
You must be signed in to change notification settings - Fork 14.3k
[libcxx] Cache file attributes during directory iteration. #93316
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
7 commits
Select commit
Hold shift + click to select a range
1fea8e1
[libcxx] Cache file attributes during directory iteration.
ed-sat 6e6cb8e
Test, formatting and fixes.
ed-sat 69a00a0
[libcxx] Format code & fix test compilation for c++17.
ed-sat d8211d9
[libcxx] Add availability for populating cache from directory_iterator.
ed-sat 89c5d7e
[libcxx] Fixes after review, add & improve test.
ed-sat c86a8ae
[libcxx] Remove unnecessary availability for populating cache filesystem
ed-sat 57c6052
[libcxx] Remove "using namespace fs".
ed-sat File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
187 changes: 187 additions & 0 deletions
187
libcxx/test/std/input.output/filesystems/class.rec.dir.itr/cache_refresh_iter.pass.cpp
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,187 @@ | ||
//===----------------------------------------------------------------------===// | ||
// | ||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | ||
// See https://llvm.org/LICENSE.txt for license information. | ||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | ||
// | ||
//===----------------------------------------------------------------------===// | ||
|
||
// REQUIRES: can-create-symlinks | ||
// UNSUPPORTED: c++03, c++11, c++14 | ||
// UNSUPPORTED: no-filesystem | ||
// UNSUPPORTED: availability-filesystem-missing | ||
|
||
// <filesystem> | ||
|
||
// recursive_directory_iterator | ||
ed-sat marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
#include <filesystem> | ||
#include <type_traits> | ||
#include <set> | ||
#include <cassert> | ||
|
||
#include "test_macros.h" | ||
#include "filesystem_test_helper.h" | ||
namespace fs = std::filesystem; | ||
|
||
#if defined(_WIN32) | ||
static void set_last_write_time_in_iteration(const fs::path& dir) { | ||
// Windows can postpone updating last write time for file especially for | ||
// directory because last write time of directory depends of its childs. | ||
// See | ||
// https://learn.microsoft.com/en-us/windows/win32/sysinfo/file-times | ||
// To force updating file entries calls "last_write_time" with own value. | ||
const fs::recursive_directory_iterator end_it{}; | ||
|
||
std::error_code ec; | ||
fs::recursive_directory_iterator it(dir, ec); | ||
assert(!ec); | ||
|
||
fs::file_time_type now_time = fs::file_time_type::clock::now(); | ||
for (; it != end_it; ++it) { | ||
const fs::path entry = *it; | ||
fs::last_write_time(entry, now_time, ec); | ||
assert(!ec); | ||
} | ||
|
||
assert(it == end_it); | ||
} | ||
|
||
struct directory_entry_and_values { | ||
fs::directory_entry entry; | ||
|
||
fs::file_status symlink_status; | ||
fs::file_status status; | ||
std::uintmax_t file_size; | ||
fs::file_time_type last_write_time; | ||
}; | ||
|
||
std::vector<directory_entry_and_values> | ||
get_directory_entries_for(const fs::path& dir, const std::set<fs::path>& dir_contents) { | ||
const fs::recursive_directory_iterator end_it{}; | ||
|
||
std::error_code ec; | ||
fs::recursive_directory_iterator it(dir, ec); | ||
assert(!ec); | ||
|
||
std::vector<directory_entry_and_values> dir_entries; | ||
std::set<fs::path> unseen_entries = dir_contents; | ||
while (!unseen_entries.empty()) { | ||
assert(it != end_it); | ||
const fs::directory_entry& entry = *it; | ||
|
||
assert(unseen_entries.erase(entry.path()) == 1); | ||
|
||
dir_entries.push_back(directory_entry_and_values{ | ||
.entry = entry, | ||
.symlink_status = entry.symlink_status(), | ||
.status = entry.status(), | ||
.file_size = entry.is_regular_file() ? entry.file_size() : 0, | ||
.last_write_time = entry.last_write_time()}); | ||
|
||
fs::recursive_directory_iterator& it_ref = it.increment(ec); | ||
assert(!ec); | ||
assert(&it_ref == &it); | ||
} | ||
return dir_entries; | ||
} | ||
#endif // _WIN32 | ||
|
||
// Checks that the directory_entry properties will be the same before and after | ||
// calling "refresh" in case of iteration. | ||
// In case of Windows expects that directory_entry caches the properties during | ||
// iteration. | ||
static void test_cache_and_refresh_in_iteration() { | ||
static_test_env static_env; | ||
const fs::path test_dir = static_env.Dir; | ||
#if defined(_WIN32) | ||
set_last_write_time_in_iteration(test_dir); | ||
#endif | ||
const std::set<fs::path> dir_contents(static_env.RecDirIterationList.begin(), static_env.RecDirIterationList.end()); | ||
const fs::recursive_directory_iterator end_it{}; | ||
|
||
std::error_code ec; | ||
fs::recursive_directory_iterator it(test_dir, ec); | ||
assert(!ec); | ||
|
||
std::set<fs::path> unseen_entries = dir_contents; | ||
while (!unseen_entries.empty()) { | ||
assert(it != end_it); | ||
const fs::directory_entry& entry = *it; | ||
|
||
assert(unseen_entries.erase(entry.path()) == 1); | ||
|
||
fs::file_status symlink_status = entry.symlink_status(); | ||
fs::file_status status = entry.status(); | ||
std::uintmax_t file_size = entry.is_regular_file() ? entry.file_size() : 0; | ||
fs::file_time_type last_write_time = entry.last_write_time(); | ||
|
||
fs::directory_entry mutable_entry = *it; | ||
mutable_entry.refresh(); | ||
fs::file_status upd_symlink_status = mutable_entry.symlink_status(); | ||
fs::file_status upd_status = mutable_entry.status(); | ||
std::uintmax_t upd_file_size = mutable_entry.is_regular_file() ? mutable_entry.file_size() : 0; | ||
fs::file_time_type upd_last_write_time = mutable_entry.last_write_time(); | ||
assert(upd_symlink_status.type() == symlink_status.type() && | ||
upd_symlink_status.permissions() == symlink_status.permissions()); | ||
assert(upd_status.type() == status.type() && upd_status.permissions() == status.permissions()); | ||
assert(upd_file_size == file_size); | ||
assert(upd_last_write_time == last_write_time); | ||
|
||
fs::recursive_directory_iterator& it_ref = it.increment(ec); | ||
assert(!ec); | ||
assert(&it_ref == &it); | ||
} | ||
} | ||
|
||
#if defined(_WIN32) | ||
// In case of Windows expects that the directory_entry caches the properties | ||
// during iteration and the properties don't change after deleting folders | ||
// and files. | ||
static void test_cached_values_in_iteration() { | ||
std::vector<directory_entry_and_values> dir_entries; | ||
{ | ||
static_test_env static_env; | ||
const fs::path testDir = static_env.Dir; | ||
set_last_write_time_in_iteration(testDir); | ||
const std::set<fs::path> dir_contents(static_env.RecDirIterationList.begin(), static_env.RecDirIterationList.end()); | ||
dir_entries = get_directory_entries_for(testDir, dir_contents); | ||
} | ||
// Testing folder should be deleted after destoying static_test_env. | ||
|
||
for (const auto& dir_entry : dir_entries) { | ||
// During iteration Windows provides information only about symlink itself | ||
// not about file/folder which symlink points to. | ||
if (dir_entry.entry.is_symlink()) { | ||
// Check that symlink is not using cached value about existing file. | ||
assert(!dir_entry.entry.exists()); | ||
} else { | ||
// Check that entry uses cached value about existing file. | ||
assert(dir_entry.entry.exists()); | ||
} | ||
fs::file_status symlink_status = dir_entry.entry.symlink_status(); | ||
assert(dir_entry.symlink_status.type() == symlink_status.type() && | ||
dir_entry.symlink_status.permissions() == symlink_status.permissions()); | ||
|
||
if (!dir_entry.entry.is_symlink()) { | ||
fs::file_status status = dir_entry.entry.status(); | ||
assert(dir_entry.status.type() == status.type() && dir_entry.status.permissions() == status.permissions()); | ||
|
||
std::uintmax_t file_size = dir_entry.entry.is_regular_file() ? dir_entry.entry.file_size() : 0; | ||
assert(dir_entry.file_size == file_size); | ||
|
||
fs::file_time_type last_write_time = dir_entry.entry.last_write_time(); | ||
assert(dir_entry.last_write_time == last_write_time); | ||
} | ||
} | ||
} | ||
#endif // _WIN32 | ||
|
||
int main(int, char**) { | ||
test_cache_and_refresh_in_iteration(); | ||
#if defined(_WIN32) | ||
test_cached_values_in_iteration(); | ||
#endif | ||
|
||
return 0; | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.