Skip to content

Commit

Permalink
handle files and directories locks
Browse files Browse the repository at this point in the history
handle multiple locks acquired from the same process
improve logs
add tests
refactor use of LockFile
forbid locking twice same path from same PID
improve windows lockfile to match unix one
split Lock and LockFile
use byte 21 to set lock status
enable reading locked file on windows
ensure closing of file descriptor in Lock and LockFile destructors
add timeout implementation for unix
add testing lock cli for subprocess tests
add subprocess tests
add --lock-timeout to umamba
add clean lock files
add a clean lock files flag -l,--locks to umamba clean command
add cpp-filesystem pins to enforce correct version on dev mode
  • Loading branch information
adriendelsalle committed Oct 6, 2021
1 parent bc00ce1 commit ddee02f
Show file tree
Hide file tree
Showing 14 changed files with 812 additions and 115 deletions.
21 changes: 11 additions & 10 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ jobs:
run: |
conda config --add channels conda-forge
conda config --set channel_priority strict
conda install -n base python=$PYTHON_VERSION pip pybind11 libsolv libsodium libarchive "libcurl=7.76.1=*_0" nlohmann_json cpp-filesystem conda cxx-compiler ccache cmake gtest gmock reproc-cpp yaml-cpp
conda install -n base python=$PYTHON_VERSION pip pybind11 libsolv libsodium libarchive "libcurl=7.76.1=*_0" nlohmann_json "cpp-filesystem>=1.5.8" conda cxx-compiler ccache cmake gtest gmock reproc-cpp yaml-cpp
env:
PYTHON_VERSION: ${{ matrix.python-version }}
- name: Install dependencies
Expand Down Expand Up @@ -147,7 +147,7 @@ jobs:
export MAMBA_ROOT_PREFIX=~/mambaroot
export MAMBA_EXE=$(pwd)/micromamba
. $MAMBA_ROOT_PREFIX/etc/profile.d/mamba.sh
micromamba create -y -p ~/build_env pybind11 libsolv libsodium libarchive "libcurl=7.76.1=*_0" nlohmann_json cxx-compiler ccache cmake gtest gmock cpp-filesystem reproc-cpp yaml-cpp cli11 -c conda-forge
micromamba create -y -p ~/build_env pybind11 libsolv libsodium libarchive "libcurl=7.76.1=*_0" nlohmann_json cxx-compiler ccache cmake gtest gmock "cpp-filesystem>=1.5.8" reproc-cpp yaml-cpp cli11 -c conda-forge
env:
PYTHON_VERSION: ${{ matrix.python-version }}
- name: build tests
Expand All @@ -169,6 +169,7 @@ jobs:
-DBUILD_BINDINGS=OFF \
-DCMAKE_CXX_COMPILER_LAUNCHER=ccache \
-DCMAKE_C_COMPILER_LAUNCHER=ccache
make test_mamba_lock -j2
make test -j2
umamba_tests:
Expand Down Expand Up @@ -208,7 +209,7 @@ jobs:
export MAMBA_ROOT_PREFIX=~/mambaroot
export MAMBA_EXE=$(pwd)/micromamba
. $MAMBA_ROOT_PREFIX/etc/profile.d/mamba.sh
micromamba create -y -p ~/build_env pybind11 libsolv libsodium libarchive "libcurl=7.76.1=*_0" nlohmann_json cxx-compiler ccache cmake gtest gmock cpp-filesystem reproc-cpp yaml-cpp pyyaml cli11 -c conda-forge
micromamba create -y -p ~/build_env pybind11 libsolv libsodium libarchive "libcurl=7.76.1=*_0" nlohmann_json cxx-compiler ccache cmake gtest gmock "cpp-filesystem>=1.5.8" reproc-cpp yaml-cpp pyyaml cli11 -c conda-forge
env:
PYTHON_VERSION: ${{ matrix.python-version }}
- name: build micromamba
Expand Down Expand Up @@ -323,7 +324,7 @@ jobs:
run: |
conda config --add channels conda-forge
conda config --set channel_priority strict
conda install -n base -q -y vs2017_win-64 ccache python=$PYTHON_VERSION pip pybind11 libsolv libsodium libarchive "libcurl=7.76.1=*_0" nlohmann_json cpp-filesystem conda cmake gtest gmock ninja reproc-cpp yaml-cpp cli11
conda install -n base -q -y vs2017_win-64 ccache python=$PYTHON_VERSION pip pybind11 libsolv libsodium libarchive "libcurl=7.76.1=*_0" nlohmann_json "cpp-filesystem>=1.5.8" conda cmake gtest gmock ninja reproc-cpp yaml-cpp cli11
env:
PYTHON_VERSION: ${{ matrix.python-version }}
- name: Install dependencies
Expand Down Expand Up @@ -398,7 +399,7 @@ jobs:
run: |
conda config --add channels conda-forge
conda config --set channel_priority strict
conda create -q -y -n mamba-tests vs2017_win-64 ccache python=$PYTHON_VERSION pip pybind11 libsolv libsodium libarchive "libcurl=7.76.1=*_0" nlohmann_json cpp-filesystem conda cmake gtest gmock ninja reproc-cpp yaml-cpp cli11 pytest
conda create -q -y -n mamba-tests vs2017_win-64 ccache python=$PYTHON_VERSION pip pybind11 libsolv libsodium libarchive "libcurl=7.76.1=*_0" nlohmann_json "cpp-filesystem>=1.5.8" conda cmake gtest gmock ninja reproc-cpp yaml-cpp cli11 pytest
env:
PYTHON_VERSION: ${{ matrix.python-version }}
- name: Run C++ tests Windows
Expand All @@ -412,8 +413,8 @@ jobs:
-G "Ninja" ^
-DCMAKE_CXX_COMPILER_LAUNCHER=ccache ^
-DCMAKE_C_COMPILER_LAUNCHER=ccache
ninja test
ninja test_mamba_lock -j2
ninja test -j2
umamba_tests_win:
runs-on: ${{ matrix.os }}
Expand Down Expand Up @@ -508,7 +509,7 @@ jobs:
run: |
conda config --add channels conda-forge
conda config --set channel_priority strict
conda create -q -y -n mamba-tests vs2017_win-64 python=$PYTHON_VERSION pip pybind11 libsolv libsodium libarchive "libcurl=7.76.1=*_0" nlohmann_json cpp-filesystem conda cmake gtest gmock ninja reproc-cpp yaml-cpp pyyaml cli11 pytest pytest-lazy-fixture menuinst
conda create -q -y -n mamba-tests vs2017_win-64 python=$PYTHON_VERSION pip pybind11 libsolv libsodium libarchive "libcurl=7.76.1=*_0" nlohmann_json "cpp-filesystem>=1.5.8" conda cmake gtest gmock ninja reproc-cpp yaml-cpp pyyaml cli11 pytest pytest-lazy-fixture menuinst
env:
PYTHON_VERSION: ${{ matrix.python-version }}
- name: micromamba python based tests
Expand Down Expand Up @@ -549,7 +550,7 @@ jobs:
run: |
conda config --add channels conda-forge
conda config --set channel_priority strict
conda create -q -y -n mamba-tests vs2017_win-64 python=$PYTHON_VERSION pip pybind11 libsolv libsodium libarchive "libcurl=7.76.1=*_0" nlohmann_json cpp-filesystem conda cmake gtest gmock ninja reproc-cpp yaml-cpp pyyaml cli11 pytest pytest-lazy-fixture menuinst
conda create -q -y -n mamba-tests vs2017_win-64 python=$PYTHON_VERSION pip pybind11 libsolv libsodium libarchive "libcurl=7.76.1=*_0" nlohmann_json "cpp-filesystem>=1.5.8" conda cmake gtest gmock ninja reproc-cpp yaml-cpp pyyaml cli11 pytest pytest-lazy-fixture menuinst
env:
PYTHON_VERSION: ${{ matrix.python-version }}
- name: micromamba python based tests with pwsh
Expand Down Expand Up @@ -588,7 +589,7 @@ jobs:
run: |
conda config --add channels conda-forge
conda config --set channel_priority strict
conda create -q -y -n mamba-tests vs2017_win-64 python=$PYTHON_VERSION pip pybind11 libsolv libsodium libarchive "libcurl=7.76.1=*_0" nlohmann_json cpp-filesystem conda cmake gtest gmock ninja reproc-cpp yaml-cpp pyyaml cli11 pytest pytest-lazy-fixture menuinst
conda create -q -y -n mamba-tests vs2017_win-64 python=$PYTHON_VERSION pip pybind11 libsolv libsodium libarchive "libcurl=7.76.1=*_0" nlohmann_json "cpp-filesystem>=1.5.8" conda cmake gtest gmock ninja reproc-cpp yaml-cpp pyyaml cli11 pytest pytest-lazy-fixture menuinst
env:
PYTHON_VERSION: ${{ matrix.python-version }}
- name: micromamba python based tests
Expand Down
2 changes: 1 addition & 1 deletion environment-dev.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ dependencies:
- cxx-compiler
- gtest
- gmock
- cpp-filesystem
- cpp-filesystem >=1.5.8
- reproc-cpp
- yaml-cpp
- pyyaml
Expand Down
1 change: 1 addition & 0 deletions include/mamba/api/clean.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ namespace mamba
const int MAMBA_CLEAN_INDEX = 1 << 1;
const int MAMBA_CLEAN_PKGS = 1 << 2;
const int MAMBA_CLEAN_TARBALLS = 1 << 3;
const int MAMBA_CLEAN_LOCKS = 1 << 4;

void clean(int options);
}
Expand Down
2 changes: 2 additions & 0 deletions include/mamba/core/context.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,8 @@ namespace mamba
bool no_rc = false;
bool no_env = false;

std::size_t lock_timeout = 0;

// Conda compat
bool add_pip_as_python_dependency = true;

Expand Down
69 changes: 57 additions & 12 deletions include/mamba/core/util.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@
#ifndef MAMBA_CORE_UTIL_HPP
#define MAMBA_CORE_UTIL_HPP

#include "mamba/core/mamba_fs.hpp"
#include "mamba/core/output.hpp"

#include "nlohmann/json.hpp"

#include <array>
#include <iomanip>
#include <limits>
Expand All @@ -18,11 +23,7 @@
#include <time.h>
#include <vector>
#include <regex>

#include "nlohmann/json.hpp"

#include "mamba_fs.hpp"
#include "output.hpp"
#include <chrono>


namespace mamba
Expand Down Expand Up @@ -132,28 +133,72 @@ namespace mamba
fs::path m_path;
};

class Lock;

class LockFile
{
public:
LockFile(const fs::path& path);
// LockFile(const fs::path& path);
LockFile(const fs::path& path, const std::chrono::seconds& timeout);
~LockFile();

LockFile(const LockFile&) = delete;
LockFile& operator=(const LockFile&) = delete;
LockFile& operator=(LockFile&&) = default;
int fd() const;

fs::path& path();
operator fs::path();
#ifdef _WIN32
// Using file descriptor on Windows may cause false negative
static bool is_locked(const fs::path& path);
#else
// Opening a new file descriptor on Unix would clear locks
static bool is_locked(int fd);
#endif
static int read_pid(int fd);

private:
fs::path m_path;
std::chrono::seconds m_timeout;
int m_fd = -1;

bool set_lock(bool blocking) const;

int read_pid() const;
bool write_pid(int pid) const;
bool locked() const;
bool lock(int pid, bool blocking) const;
bool unlock() const;
int close_fd();
void remove() noexcept;

friend class Lock;
};

class Lock
{
public:
Lock(const fs::path& path);
~Lock();

Lock(const Lock&) = delete;
Lock& operator=(const Lock&) = delete;
Lock& operator=(Lock&&) = default;

bool locked() const;
int fd() const;
fs::path path() const;

private:
fs::path m_path;
int m_fd;
fs::path m_lock;
std::unique_ptr<LockFile> p_lock_file;
bool m_locked;

#if defined(__APPLE__) or defined(__linux__)
pid_t m_pid;
#else
int m_pid;
#endif

bool try_lock();
bool lock();
};

/*************************
Expand Down
54 changes: 50 additions & 4 deletions src/api/clean.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include "mamba/api/configuration.hpp"

#include "mamba/core/context.hpp"
#include "mamba/core/mamba_fs.hpp"
#include "mamba/core/package_cache.hpp"


Expand All @@ -25,21 +26,22 @@ namespace mamba
bool clean_index = options & MAMBA_CLEAN_INDEX;
bool clean_pkgs = options & MAMBA_CLEAN_PKGS;
bool clean_tarballs = options & MAMBA_CLEAN_TARBALLS;
bool clean_locks = options & MAMBA_CLEAN_LOCKS;

if (!(clean_all || clean_index || clean_pkgs || clean_tarballs))
if (!(clean_all || clean_index || clean_pkgs || clean_tarballs || clean_locks))
{
std::cout << "Nothing to do." << std::endl;
Console::stream() << "Nothing to do." << std::endl;
return;
}

Console::print("Collect information..");
Console::stream() << "Collect information..";

std::vector<fs::path> envs;

MultiPackageCache caches(ctx.pkgs_dirs);
if (!ctx.dry_run && (clean_index || clean_all))
{
Console::print("Cleaning index cache..");
Console::stream() << "Cleaning index cache..";

for (auto* pkg_cache : caches.writable_caches())
if (fs::exists(pkg_cache->get_pkgs_dir() / "cache"))
Expand All @@ -55,6 +57,50 @@ namespace mamba
}
}

if (!ctx.dry_run && (clean_locks || clean_all))
{
Console::stream() << "Cleaning lock files..";

for (auto* pkg_cache : caches.writable_caches())
{
if (fs::exists(pkg_cache->get_pkgs_dir()))
for (auto& p : fs::directory_iterator(pkg_cache->get_pkgs_dir()))
{
if (p.exists() && ends_with(p.path().string(), ".lock")
&& fs::exists(rstrip(p.path().string(), ".lock")))
{
try
{
fs::remove(p);
}
catch (...)
{
LOG_WARNING << "Could not clean lock file '" << p.path().string()
<< "'";
}
}
}

if (fs::exists(pkg_cache->get_pkgs_dir() / "cache"))
for (auto& p :
fs::recursive_directory_iterator(pkg_cache->get_pkgs_dir() / "cache"))
{
if (p.exists() && ends_with(p.path().string(), ".lock"))
{
try
{
fs::remove(p);
}
catch (...)
{
LOG_WARNING << "Could not clean lock file '" << p.path().string()
<< "'";
}
}
}
}
}

if (fs::exists(ctx.root_prefix / "conda-meta"))
{
envs.push_back(ctx.root_prefix);
Expand Down
10 changes: 10 additions & 0 deletions src/api/configuration.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -814,6 +814,16 @@ namespace mamba
Spend extra time validating package contents. It consists of running
cryptographic verifications on channels and packages metadata.)")));

insert(Configurable("lock_timeout", &ctx.lock_timeout)
.group("Link & Install")
.set_rc_configurable()
.set_env_var_names()
.description("Lock timeout")
.long_description(unindent(R"(
Lock timeout for blocking mode when waiting for another process
to release the path.
Not configurable on Windows: set to 10s by WinAPI)")));

// Output, Prompt and Flow
insert(Configurable("always_yes", &ctx.always_yes)
.group("Output, Prompt and Flow Control")
Expand Down
8 changes: 4 additions & 4 deletions src/api/install.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -339,10 +339,10 @@ namespace mamba

std::vector<std::shared_ptr<MSubdirData>> subdirs;
MultiDownloadTarget multi_dl;
std::unique_ptr<LockFile> subdir_download_lock;
std::unique_ptr<Lock> subdir_download_lock;
if (!ctx.offline)
{
subdir_download_lock = std::make_unique<LockFile>(cache_dir / "mamba.lock");
subdir_download_lock = std::make_unique<Lock>(cache_dir);
}

std::vector<std::pair<int, int>> priorities;
Expand Down Expand Up @@ -535,7 +535,7 @@ namespace mamba
detail::create_target_directory(ctx.target_prefix);
}
{
LockFile(pkgs_dirs / "mamba.lock");
Lock pkgs_dirs_lock(pkgs_dirs);
trans.execute(prefix_data);
}
for (auto other_spec : config.at("others_pkg_mgrs_specs")
Expand Down Expand Up @@ -774,7 +774,7 @@ namespace mamba
fs::create_directories(pkgs_dirs);
}

LockFile(pkgs_dirs / "mamba.lock");
Lock pkgs_dirs_lock(pkgs_dirs);

std::vector<std::unique_ptr<PackageDownloadExtractTarget>> targets;
MultiDownloadTarget multi_dl;
Expand Down
Loading

0 comments on commit ddee02f

Please sign in to comment.