Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
  • Loading branch information
strega-nil committed Jul 6, 2020
1 parent c2d062b commit 4607593
Show file tree
Hide file tree
Showing 5 changed files with 79 additions and 21 deletions.
3 changes: 3 additions & 0 deletions toolsrc/include/vcpkg/base/files.h
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,9 @@ namespace vcpkg::Files
virtual void current_path(const fs::path& path, std::error_code&) = 0;
void current_path(const fs::path& path, LineInfo li);

// waits forever for the file lock
virtual fs::SystemHandle take_exclusive_file_lock(const fs::path& path, std::error_code&) = 0;
// waits, at most, 1.5 seconds, for the file lock
virtual fs::SystemHandle try_take_exclusive_file_lock(const fs::path& path, std::error_code&) = 0;
virtual void unlock_file_lock(fs::SystemHandle handle, std::error_code&) = 0;

Expand Down
3 changes: 3 additions & 0 deletions toolsrc/include/vcpkg/vcpkgcmdarguments.h
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,9 @@ namespace vcpkg
constexpr static StringLiteral PRINT_METRICS_SWITCH = "printmetrics";
Optional<bool> print_metrics = nullopt;

constexpr static StringLiteral WAIT_FOR_LOCK_SWITCH = "x-wait-for-lock";
Optional<bool> wait_for_lock = nullopt;

// feature flags
constexpr static StringLiteral FEATURE_FLAGS_ENV = "VCPKG_FEATURE_FLAGS";
constexpr static StringLiteral FEATURE_FLAGS_ARG = "feature-flags";
Expand Down
84 changes: 64 additions & 20 deletions toolsrc/src/vcpkg/base/files.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -848,15 +848,18 @@ namespace vcpkg::Files
fs::stdfs::current_path(path, ec);
}

virtual fs::SystemHandle try_take_exclusive_file_lock(const fs::path& path, std::error_code& ec) override
struct TakeExclusiveFileLockHelper
{
fs::SystemHandle res;
fs::SystemHandle& res;
fs::path::string_type native;
TakeExclusiveFileLockHelper(fs::SystemHandle& res, const fs::path::string_type& native)
: res(res), native(native)
{ }

const auto system_file_name = path.native();
#if defined(WIN32)
constexpr static auto busy_error = ERROR_BUSY;
const auto system_try_take_file_lock = [&] {
auto handle = CreateFileW(system_file_name.c_str(),
bool operator()(std::error_code& ec) {
auto handle = CreateFileW(native.c_str(),
GENERIC_READ,
0 /* no sharing */,
nullptr /* no security attributes */,
Expand All @@ -875,17 +878,23 @@ namespace vcpkg::Files

res.system_handle = reinterpret_cast<intptr_t>(handle);
return true;
};
}
#else // ^^^ WIN32 / !WIN32 vvv
constexpr static auto busy_error = EBUSY;
int fd = open(system_file_name.c_str(), 0);
if (fd < 0)
{
ec.assign(errno, std::system_category());
return res;
}
const auto system_try_take_file_lock = [&] {
if (flock(fd, LOCK_EX | LOCK_NB) != 0)
int fd = -1;

bool operator()(std::error_code& ec) {
if (fd == -1)
{
fd = ::open(native.c_str(), 0);
if (fd < 0)
{
ec.assign(errno, std::system_category());
return res;
}
}

if (::flock(fd, LOCK_EX | LOCK_NB) != 0)
{
if (errno != EWOULDBLOCK)
{
Expand All @@ -895,11 +904,48 @@ namespace vcpkg::Files
}

res.system_handle = fd;
fd = -1;
return true;
};

~TakeExclusiveFileLockHelper() {
if (fd != -1)
{
::close(fd);
}
}
#endif
};

virtual fs::SystemHandle take_exclusive_file_lock(const fs::path& path, std::error_code& ec) override
{
fs::SystemHandle res;
TakeExclusiveFileLockHelper helper(res, path.native());

if (system_try_take_file_lock() || ec)
if (helper(ec) || ec)
{
return res;
}

System::printf("Waiting to take filesystem lock on %s...\n", path.u8string());
const auto wait = std::chrono::milliseconds(1000);
// infinite loop
for (;;)
{
std::this_thread::sleep_for(wait);
if (helper(ec) || ec)
{
return res;
}
}
}

virtual fs::SystemHandle try_take_exclusive_file_lock(const fs::path& path, std::error_code& ec) override
{
fs::SystemHandle res;
TakeExclusiveFileLockHelper helper(res, path.native());

if (helper(ec) || ec)
{
return res;
}
Expand All @@ -910,19 +956,17 @@ namespace vcpkg::Files
while (wait < std::chrono::milliseconds(1000))
{
std::this_thread::sleep_for(wait);
if (system_try_take_file_lock() || ec)
if (helper(ec) || ec)
{
return res;
}
wait *= 2;
}

#if !defined(WIN32)
close(fd);
#endif
ec.assign(busy_error, std::system_category());
ec.assign(helper.busy_error, std::system_category());
return res;
}

virtual void unlock_file_lock(fs::SystemHandle handle, std::error_code& ec) override
{
#if defined(WIN32)
Expand Down
1 change: 1 addition & 0 deletions toolsrc/src/vcpkg/vcpkgcmdarguments.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -297,6 +297,7 @@ namespace vcpkg
{PRINT_METRICS_SWITCH, &VcpkgCmdArguments::print_metrics},
{FEATURE_PACKAGES_SWITCH, &VcpkgCmdArguments::feature_packages},
{BINARY_CACHING_SWITCH, &VcpkgCmdArguments::binary_caching},
{WAIT_FOR_LOCK_SWITCH, &VcpkgCmdArguments::wait_for_lock},
};

bool found = false;
Expand Down
9 changes: 8 additions & 1 deletion toolsrc/src/vcpkg/vcpkgpaths.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,14 @@ namespace vcpkg

std::error_code ec;
const auto vcpkg_lock = root / ".vcpkg-root";
m_pimpl->file_lock_handle = filesystem.try_take_exclusive_file_lock(vcpkg_lock, ec);
if (args.wait_for_lock.value_or(false))
{
m_pimpl->file_lock_handle = filesystem.take_exclusive_file_lock(vcpkg_lock, ec);
}
else
{
m_pimpl->file_lock_handle = filesystem.try_take_exclusive_file_lock(vcpkg_lock, ec);
}
if (ec)
{
System::printf(System::Color::error, "Failed to take the filesystem lock on %s:\n", vcpkg_lock.u8string());
Expand Down

0 comments on commit 4607593

Please sign in to comment.