Merge branch 'hotfix/threadname' into release/0.10
This commit is contained in:
commit
1d45833f90
32
appveyor.yml
32
appveyor.yml
@ -20,13 +20,11 @@ init:
|
||||
- echo %NUMBER_OF_PROCESSORS%
|
||||
- echo %PLATFORM%
|
||||
- echo %APPVEYOR_BUILD_WORKER_IMAGE%
|
||||
- set arch=
|
||||
- if "%PLATFORM%"=="x64" ( set arch= Win64)
|
||||
- if "%APPVEYOR_BUILD_WORKER_IMAGE%"=="Visual Studio 2013" (set generator=Visual Studio 12 2013%arch%)
|
||||
- if "%APPVEYOR_BUILD_WORKER_IMAGE%"=="Visual Studio 2015" (set generator=Visual Studio 14 2015%arch%)
|
||||
- if "%APPVEYOR_BUILD_WORKER_IMAGE%"=="Visual Studio 2017" (set generator=Visual Studio 15 2017%arch%)
|
||||
- if "%APPVEYOR_BUILD_WORKER_IMAGE%"=="Visual Studio 2017 Preview" (set generator=Visual Studio 15 2017%arch%)
|
||||
- echo %generator%
|
||||
- set arch=32
|
||||
- if "%PLATFORM%"=="x64" ( set arch=64)
|
||||
- set VisualStudioVersion=2017
|
||||
- if "%APPVEYOR_BUILD_WORKER_IMAGE%" == "Visual Studio 2017 Preview" ( set VisualStudioVersion=Preview)
|
||||
- cmd: call "C:\Program Files (x86)\Microsoft Visual Studio\%VisualStudioVersion%\Community\VC\Auxiliary\Build\vcvars%arch%.bat"
|
||||
|
||||
install:
|
||||
- choco install -y dokany --version 1.1.0.2000 --installargs INSTALLDEVFILES=1
|
||||
@ -35,17 +33,17 @@ install:
|
||||
build_script:
|
||||
- cmd: mkdir build
|
||||
- cmd: cd build
|
||||
- cmd: cmake .. -G "%generator%" -DBUILD_TESTING=on -DBOOST_ROOT="C:/Libraries/boost_1_65_1" -DDOKAN_PATH="C:/Program Files/Dokan/DokanLibrary-1.1.0"
|
||||
# TODO Make build parallel
|
||||
- cmd: cmake .. -G "Ninja" -DBUILD_TESTING=on -DBOOST_ROOT="C:/Libraries/boost_1_65_1" -DDOKAN_PATH="C:/Program Files/Dokan/DokanLibrary-1.1.0"
|
||||
- cmd: cmake --build . --config %CONFIGURATION%
|
||||
- cmd: .\test\gitversion\%CONFIGURATION%\gitversion-test.exe
|
||||
- cmd: cd .\test\cpp-utils\%CONFIGURATION%\ && .\cpp-utils-test.exe && cd ..\..\..
|
||||
#- cmd: .\test\fspp\%CONFIGURATION%\fspp-test.exe
|
||||
- cmd: .\test\parallelaccessstore\%CONFIGURATION%\parallelaccessstore-test.exe
|
||||
- cmd: .\test\blockstore\%CONFIGURATION%\blockstore-test.exe
|
||||
- cmd: .\test\blobstore\%CONFIGURATION%\blobstore-test.exe
|
||||
- cmd: .\test\cryfs\%CONFIGURATION%\cryfs-test.exe
|
||||
#- cmd: .\test\cryfs-cli\%CONFIGURATION%\cryfs-cli-test.exe
|
||||
- cmd: .\test\gitversion\gitversion-test.exe
|
||||
# cpp-utils-test disables ThreadDebuggingTest_ThreadName.*_thenIsCorrect because the appveyor image is too old to support the API needed for that
|
||||
- cmd: cd .\test\cpp-utils\ && .\cpp-utils-test.exe --gtest_filter=-ThreadDebuggingTest_ThreadName.*_thenIsCorrect && cd ..\..
|
||||
#- cmd: .\test\fspp\fspp-test.exe
|
||||
- cmd: .\test\parallelaccessstore\parallelaccessstore-test.exe
|
||||
- cmd: .\test\blockstore\blockstore-test.exe
|
||||
- cmd: .\test\blobstore\blobstore-test.exe
|
||||
- cmd: .\test\cryfs\cryfs-test.exe
|
||||
#- cmd: .\test\cryfs-cli\cryfs-cli-test.exe
|
||||
|
||||
- cmd: cpack -C %CONFIGURATION% --verbose -G WIX
|
||||
|
||||
|
@ -45,7 +45,7 @@ void CachingBlockStore2::CachedBlock::write(Data data) {
|
||||
}
|
||||
|
||||
CachingBlockStore2::CachingBlockStore2(cpputils::unique_ref<BlockStore2> baseBlockStore)
|
||||
: _baseBlockStore(std::move(baseBlockStore)), _cachedBlocksNotInBaseStoreMutex(), _cachedBlocksNotInBaseStore(), _cache() {
|
||||
: _baseBlockStore(std::move(baseBlockStore)), _cachedBlocksNotInBaseStoreMutex(), _cachedBlocksNotInBaseStore(), _cache("blockstore") {
|
||||
}
|
||||
|
||||
bool CachingBlockStore2::tryCreate(const BlockId &blockId, const Data &data) {
|
||||
|
@ -24,7 +24,7 @@ public:
|
||||
static constexpr double PURGE_INTERVAL = 0.5; // With this interval, we check for entries to purge
|
||||
static constexpr double MAX_LIFETIME_SEC = PURGE_LIFETIME_SEC + PURGE_INTERVAL; // This is the oldest age an entry can reach (given purging works in an ideal world, i.e. with the ideal interval and in zero time)
|
||||
|
||||
Cache();
|
||||
Cache(const std::string& cacheName);
|
||||
~Cache();
|
||||
|
||||
uint32_t size() const;
|
||||
@ -56,10 +56,10 @@ template<class Key, class Value, uint32_t MAX_ENTRIES> constexpr double Cache<Ke
|
||||
template<class Key, class Value, uint32_t MAX_ENTRIES> constexpr double Cache<Key, Value, MAX_ENTRIES>::MAX_LIFETIME_SEC;
|
||||
|
||||
template<class Key, class Value, uint32_t MAX_ENTRIES>
|
||||
Cache<Key, Value, MAX_ENTRIES>::Cache(): _mutex(), _currentlyFlushingEntries(), _cachedBlocks(), _timeoutFlusher(nullptr) {
|
||||
Cache<Key, Value, MAX_ENTRIES>::Cache(const std::string& cacheName): _mutex(), _currentlyFlushingEntries(), _cachedBlocks(), _timeoutFlusher(nullptr) {
|
||||
//Don't initialize timeoutFlusher in the initializer list,
|
||||
//because it then might already call Cache::popOldEntries() before Cache is done constructing.
|
||||
_timeoutFlusher = std::make_unique<PeriodicTask>(std::bind(&Cache::_deleteOldEntriesParallel, this), PURGE_INTERVAL);
|
||||
_timeoutFlusher = std::make_unique<PeriodicTask>(std::bind(&Cache::_deleteOldEntriesParallel, this), PURGE_INTERVAL, "flush_" + cacheName);
|
||||
}
|
||||
|
||||
template<class Key, class Value, uint32_t MAX_ENTRIES>
|
||||
|
@ -7,10 +7,10 @@ using namespace cpputils::logging;
|
||||
namespace blockstore {
|
||||
namespace caching {
|
||||
|
||||
PeriodicTask::PeriodicTask(function<void ()> task, double intervalSec) :
|
||||
PeriodicTask::PeriodicTask(function<void ()> task, double intervalSec, std::string threadName) :
|
||||
_task(task),
|
||||
_interval(static_cast<uint64_t>(UINT64_C(1000000000) * intervalSec)),
|
||||
_thread(std::bind(&PeriodicTask::_loopIteration, this)) {
|
||||
_thread(std::bind(&PeriodicTask::_loopIteration, this), std::move(threadName)) {
|
||||
_thread.start();
|
||||
}
|
||||
|
||||
|
@ -11,7 +11,7 @@ namespace caching {
|
||||
|
||||
class PeriodicTask final {
|
||||
public:
|
||||
PeriodicTask(std::function<void ()> task, double intervalSec);
|
||||
PeriodicTask(std::function<void ()> task, double intervalSec, std::string threadName);
|
||||
|
||||
private:
|
||||
bool _loopIteration();
|
||||
|
@ -24,6 +24,8 @@ set(SOURCES
|
||||
io/pipestream.cpp
|
||||
thread/LoopThread.cpp
|
||||
thread/ThreadSystem.cpp
|
||||
thread/debugging_nonwindows.cpp
|
||||
thread/debugging_windows.cpp
|
||||
random/Random.cpp
|
||||
random/RandomGeneratorThread.cpp
|
||||
random/OSRandomGenerator.cpp
|
||||
|
@ -8,7 +8,7 @@ namespace cpputils {
|
||||
_buffer(buffer),
|
||||
_minSize(minSize),
|
||||
_maxSize(maxSize),
|
||||
_thread(std::bind(&RandomGeneratorThread::_loopIteration, this)) {
|
||||
_thread(std::bind(&RandomGeneratorThread::_loopIteration, this), "RandomGeneratorThread") {
|
||||
ASSERT(_maxSize >= _minSize, "Invalid parameters");
|
||||
}
|
||||
|
||||
|
@ -6,7 +6,8 @@ using boost::none;
|
||||
|
||||
namespace cpputils {
|
||||
|
||||
LoopThread::LoopThread(function<bool()> loopIteration): _loopIteration(std::move(loopIteration)), _runningHandle(none) {
|
||||
LoopThread::LoopThread(function<bool()> loopIteration, std::string threadName)
|
||||
: _loopIteration(std::move(loopIteration)), _runningHandle(none), _threadName(std::move(threadName)) {
|
||||
}
|
||||
|
||||
LoopThread::~LoopThread() {
|
||||
@ -16,7 +17,7 @@ namespace cpputils {
|
||||
}
|
||||
|
||||
void LoopThread::start() {
|
||||
_runningHandle = ThreadSystem::singleton().start(_loopIteration);
|
||||
_runningHandle = ThreadSystem::singleton().start(_loopIteration, _threadName);
|
||||
}
|
||||
|
||||
void LoopThread::stop() {
|
||||
|
@ -14,7 +14,7 @@ namespace cpputils {
|
||||
class LoopThread final {
|
||||
public:
|
||||
// The loopIteration callback returns true, if more iterations should be run, and false, if the thread should be terminated.
|
||||
LoopThread(std::function<bool()> loopIteration);
|
||||
LoopThread(std::function<bool()> loopIteration, std::string threadName);
|
||||
~LoopThread();
|
||||
void start();
|
||||
void stop();
|
||||
@ -22,6 +22,7 @@ namespace cpputils {
|
||||
private:
|
||||
std::function<bool()> _loopIteration;
|
||||
boost::optional<ThreadSystem::Handle> _runningHandle;
|
||||
std::string _threadName;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(LoopThread);
|
||||
};
|
||||
|
@ -1,7 +1,9 @@
|
||||
#include "ThreadSystem.h"
|
||||
#include "../logging/logging.h"
|
||||
#include "debugging.h"
|
||||
|
||||
using std::function;
|
||||
using std::string;
|
||||
using namespace cpputils::logging;
|
||||
|
||||
namespace cpputils {
|
||||
@ -21,10 +23,10 @@ namespace cpputils {
|
||||
#endif
|
||||
}
|
||||
|
||||
ThreadSystem::Handle ThreadSystem::start(function<bool()> loopIteration) {
|
||||
ThreadSystem::Handle ThreadSystem::start(function<bool()> loopIteration, string threadName) {
|
||||
boost::unique_lock<boost::mutex> lock(_mutex);
|
||||
auto thread = _startThread(loopIteration);
|
||||
_runningThreads.push_back(RunningThread{std::move(loopIteration), std::move(thread)});
|
||||
auto thread = _startThread(loopIteration, threadName);
|
||||
_runningThreads.push_back(RunningThread{std::move(threadName), std::move(loopIteration), std::move(thread)});
|
||||
return std::prev(_runningThreads.end());
|
||||
}
|
||||
|
||||
@ -59,13 +61,14 @@ namespace cpputils {
|
||||
|
||||
void ThreadSystem::_restartAllThreads() {
|
||||
for (RunningThread &thread : _runningThreads) {
|
||||
thread.thread = _startThread(thread.loopIteration);
|
||||
thread.thread = _startThread(thread.loopIteration, thread.threadName);
|
||||
}
|
||||
_mutex.unlock(); // Was locked in the before-fork handler
|
||||
}
|
||||
|
||||
boost::thread ThreadSystem::_startThread(function<bool()> loopIteration) {
|
||||
return boost::thread([loopIteration = std::move(loopIteration)] {
|
||||
boost::thread ThreadSystem::_startThread(function<bool()> loopIteration, const string& threadName) {
|
||||
return boost::thread([loopIteration = std::move(loopIteration), threadName] {
|
||||
cpputils::set_thread_name(threadName.c_str());
|
||||
ThreadSystem::_runThread(loopIteration);
|
||||
});
|
||||
}
|
||||
|
@ -13,6 +13,7 @@ namespace cpputils {
|
||||
class ThreadSystem final {
|
||||
private:
|
||||
struct RunningThread {
|
||||
std::string threadName;
|
||||
std::function<bool()> loopIteration; // The loopIteration callback returns true, if more iterations should be run, and false, if the thread should be terminated.
|
||||
boost::thread thread; // boost::thread because we need it to be interruptible.
|
||||
};
|
||||
@ -21,7 +22,7 @@ namespace cpputils {
|
||||
|
||||
static ThreadSystem &singleton();
|
||||
|
||||
Handle start(std::function<bool()> loopIteration);
|
||||
Handle start(std::function<bool()> loopIteration, std::string threadName);
|
||||
void stop(Handle handle);
|
||||
|
||||
private:
|
||||
@ -34,7 +35,7 @@ namespace cpputils {
|
||||
//TODO Rename to _doOnBeforeFork and _doAfterFork or similar, because they also handle locking _mutex for fork().
|
||||
void _stopAllThreadsForRestart();
|
||||
void _restartAllThreads();
|
||||
boost::thread _startThread(std::function<bool()> loopIteration);
|
||||
boost::thread _startThread(std::function<bool()> loopIteration, const std::string& threadName);
|
||||
|
||||
std::list<RunningThread> _runningThreads; // std::list, because we give out iterators as handles
|
||||
boost::mutex _mutex;
|
||||
|
16
src/cpp-utils/thread/debugging.h
Normal file
16
src/cpp-utils/thread/debugging.h
Normal file
@ -0,0 +1,16 @@
|
||||
#pragma once
|
||||
#ifndef MESSMER_CPPUTILS_DEBUGGING_H
|
||||
#define MESSMER_CPPUTILS_DEBUGGING_H
|
||||
|
||||
#include <string>
|
||||
#include <thread>
|
||||
|
||||
namespace cpputils {
|
||||
|
||||
void set_thread_name(const char* name);
|
||||
std::string get_thread_name();
|
||||
std::string get_thread_name(std::thread* thread);
|
||||
|
||||
}
|
||||
|
||||
#endif
|
55
src/cpp-utils/thread/debugging_nonwindows.cpp
Normal file
55
src/cpp-utils/thread/debugging_nonwindows.cpp
Normal file
@ -0,0 +1,55 @@
|
||||
#if !defined(_MSC_VER)
|
||||
|
||||
#include "debugging.h"
|
||||
#include <stdexcept>
|
||||
#include <thread>
|
||||
#include <pthread.h>
|
||||
#include <cpp-utils/assert/assert.h>
|
||||
|
||||
namespace cpputils {
|
||||
|
||||
namespace {
|
||||
constexpr size_t MAX_NAME_LEN = 16; // this length includes the terminating null character at the end
|
||||
}
|
||||
|
||||
void set_thread_name(const char* name) {
|
||||
std::string name_(name);
|
||||
if (name_.size() > MAX_NAME_LEN - 1) {
|
||||
name_.resize(MAX_NAME_LEN - 1);
|
||||
}
|
||||
#if defined(__APPLE__)
|
||||
int result = pthread_setname_np(name_.c_str());
|
||||
#else
|
||||
int result = pthread_setname_np(pthread_self(), name_.c_str());
|
||||
#endif
|
||||
if (0 != result) {
|
||||
throw std::runtime_error("Error setting thread name with pthread_setname_np. Code: " + std::to_string(result));
|
||||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
std::string get_thread_name(pthread_t thread) {
|
||||
char name[MAX_NAME_LEN];
|
||||
int result = pthread_getname_np(thread, name, MAX_NAME_LEN);
|
||||
if (0 != result) {
|
||||
throw std::runtime_error("Error getting thread name with pthread_getname_np. Code: " + std::to_string(result));
|
||||
}
|
||||
// pthread_getname_np returns a null terminated string with maximum 16 bytes.
|
||||
// but just to be safe against a buggy implementation, let's set the last byte to zero.
|
||||
name[MAX_NAME_LEN - 1] = '\0';
|
||||
return name;
|
||||
}
|
||||
}
|
||||
|
||||
std::string get_thread_name() {
|
||||
return get_thread_name(pthread_self());
|
||||
}
|
||||
|
||||
std::string get_thread_name(std::thread* thread) {
|
||||
ASSERT(thread->joinable(), "Thread not running");
|
||||
return get_thread_name(thread->native_handle());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
111
src/cpp-utils/thread/debugging_windows.cpp
Normal file
111
src/cpp-utils/thread/debugging_windows.cpp
Normal file
@ -0,0 +1,111 @@
|
||||
#if defined(_MSC_VER)
|
||||
|
||||
#include <Windows.h>
|
||||
#include "debugging.h"
|
||||
#include <codecvt>
|
||||
#include <cpp-utils/assert/assert.h>
|
||||
|
||||
using std::string;
|
||||
using std::wstring;
|
||||
using std::wstring_convert;
|
||||
|
||||
namespace cpputils {
|
||||
|
||||
namespace {
|
||||
struct NameData final {
|
||||
wchar_t *name = nullptr;
|
||||
|
||||
~NameData() {
|
||||
if (nullptr != LocalFree(name)) {
|
||||
throw std::runtime_error("Error releasing thread description memory. Error code: " + std::to_string(GetLastError()));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct ModuleHandle final {
|
||||
HMODULE module;
|
||||
|
||||
ModuleHandle(const char* dll) {
|
||||
bool success = GetModuleHandleExA(0, dll, &module);
|
||||
if (!success) {
|
||||
throw std::runtime_error(string() + "Error loading dll: " + dll + ". Error code: " + std::to_string(GetLastError()));
|
||||
}
|
||||
}
|
||||
|
||||
~ModuleHandle() {
|
||||
bool success = FreeLibrary(module);
|
||||
if (!success) {
|
||||
throw std::runtime_error("Error unloading dll. Error code: " + std::to_string(GetLastError()));
|
||||
}
|
||||
}
|
||||
};
|
||||
template<class Fn>
|
||||
class APIFunction final {
|
||||
private:
|
||||
ModuleHandle module_;
|
||||
Fn func_;
|
||||
|
||||
public:
|
||||
APIFunction(const char* dll, const char* function)
|
||||
: module_(dll), func_(reinterpret_cast<Fn>(GetProcAddress(module_.module, function))) {
|
||||
}
|
||||
|
||||
bool valid() const {
|
||||
return func_ != nullptr;
|
||||
}
|
||||
|
||||
Fn func() const {
|
||||
return func_;
|
||||
}
|
||||
};
|
||||
|
||||
std::string get_thread_name(HANDLE thread) {
|
||||
// The GetThreadDescription API was brought in version 1607 of Windows 10.
|
||||
typedef HRESULT(WINAPI* GetThreadDescriptionFn)(HANDLE hThread, PWSTR* ppszThreadDescription);
|
||||
static APIFunction<GetThreadDescriptionFn> get_thread_description_func("Kernel32.dll", "GetThreadDescription");
|
||||
|
||||
if (get_thread_description_func.valid()) {
|
||||
NameData name_data;
|
||||
|
||||
HRESULT status = get_thread_description_func.func()(thread, &name_data.name);
|
||||
if (FAILED(status)) {
|
||||
throw std::runtime_error("Error getting thread description. Error code: " + std::to_string(status));
|
||||
}
|
||||
return wstring_convert<std::codecvt_utf8_utf16<wchar_t>>().to_bytes(name_data.name);
|
||||
}
|
||||
else {
|
||||
// GetThreadDescription API is not available.
|
||||
return "";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void set_thread_name(const char* name) {
|
||||
// The GetThreadDescription API was brought in version 1607 of Windows 10.
|
||||
typedef HRESULT(WINAPI* SetThreadDescriptionFn)(HANDLE hThread, PCWSTR lpThreadDescription);
|
||||
static APIFunction<SetThreadDescriptionFn> set_thread_description_func("Kernel32.dll", "SetThreadDescription");
|
||||
|
||||
if (set_thread_description_func.valid()) {
|
||||
wstring wname = wstring_convert<std::codecvt_utf8_utf16<wchar_t>>().from_bytes(name);
|
||||
HRESULT status = set_thread_description_func.func()(GetCurrentThread(), wname.c_str());
|
||||
if (FAILED(status)) {
|
||||
throw std::runtime_error("Error setting thread description. Error code: " + std::to_string(status));
|
||||
}
|
||||
}
|
||||
else {
|
||||
// intentionally empty. SetThreadDescription API is not available.
|
||||
}
|
||||
}
|
||||
|
||||
std::string get_thread_name() {
|
||||
return get_thread_name(GetCurrentThread());
|
||||
}
|
||||
|
||||
std::string get_thread_name(std::thread* thread) {
|
||||
ASSERT(thread->joinable(), "Thread not running");
|
||||
return get_thread_name(static_cast<HANDLE>(thread->native_handle()));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
@ -9,7 +9,7 @@
|
||||
namespace cryfs_cli {
|
||||
class CallAfterTimeout final {
|
||||
public:
|
||||
CallAfterTimeout(boost::chrono::milliseconds timeout, std::function<void()> callback);
|
||||
CallAfterTimeout(boost::chrono::milliseconds timeout, std::function<void()> callback, const std::string& timeoutName);
|
||||
void resetTimer();
|
||||
private:
|
||||
bool _checkTimeoutThreadIteration();
|
||||
@ -25,8 +25,8 @@ namespace cryfs_cli {
|
||||
DISALLOW_COPY_AND_ASSIGN(CallAfterTimeout);
|
||||
};
|
||||
|
||||
inline CallAfterTimeout::CallAfterTimeout(boost::chrono::milliseconds timeout, std::function<void()> callback)
|
||||
:_callback(std::move(callback)), _timeout(timeout), _start(), _checkTimeoutThread(std::bind(&CallAfterTimeout::_checkTimeoutThreadIteration, this)) {
|
||||
inline CallAfterTimeout::CallAfterTimeout(boost::chrono::milliseconds timeout, std::function<void()> callback, const std::string& timeoutName)
|
||||
:_callback(std::move(callback)), _timeout(timeout), _start(), _checkTimeoutThread(std::bind(&CallAfterTimeout::_checkTimeoutThreadIteration, this), "timeout_" + timeoutName) {
|
||||
resetTimer();
|
||||
_checkTimeoutThread.start();
|
||||
}
|
||||
|
@ -26,6 +26,7 @@
|
||||
#include <cryfs/localstate/BasedirMetadata.h>
|
||||
#include "Environment.h"
|
||||
#include <cryfs/CryfsException.h>
|
||||
#include <cpp-utils/thread/debugging.h>
|
||||
|
||||
//TODO Many functions accessing the ProgramOptions object. Factor out into class that stores it as a member.
|
||||
//TODO Factor out class handling askPassword
|
||||
@ -297,7 +298,7 @@ namespace cryfs_cli {
|
||||
return none;
|
||||
}
|
||||
uint64_t millis = std::llround(60000 * (*minutes));
|
||||
return make_unique_ref<CallAfterTimeout>(milliseconds(millis), callback);
|
||||
return make_unique_ref<CallAfterTimeout>(milliseconds(millis), callback, "idlecallback");
|
||||
}
|
||||
|
||||
void Cli::_initLogfile(const ProgramOptions &options) {
|
||||
@ -396,6 +397,7 @@ namespace cryfs_cli {
|
||||
|
||||
int Cli::main(int argc, const char *argv[], unique_ref<HttpClient> httpClient, std::function<void()> onMounted) {
|
||||
cpputils::showBacktraceOnCrash();
|
||||
cpputils::set_thread_name("cryfs");
|
||||
|
||||
try {
|
||||
_showVersion(std::move(httpClient));
|
||||
|
@ -50,7 +50,7 @@ namespace cryfs {
|
||||
|
||||
|
||||
inline CachingFsBlobStore::CachingFsBlobStore(cpputils::unique_ref<fsblobstore::FsBlobStore> baseBlobStore)
|
||||
: _baseBlobStore(std::move(baseBlobStore)), _cache() {
|
||||
: _baseBlobStore(std::move(baseBlobStore)), _cache("fsblobstore") {
|
||||
}
|
||||
|
||||
inline CachingFsBlobStore::~CachingFsBlobStore() {
|
||||
|
@ -8,6 +8,7 @@
|
||||
#include <cpp-utils/assert/assert.h>
|
||||
#include <cpp-utils/logging/logging.h>
|
||||
#include <cpp-utils/process/subprocess.h>
|
||||
#include <cpp-utils/thread/debugging.h>
|
||||
#include <csignal>
|
||||
#include "InvalidFilesystem.h"
|
||||
|
||||
@ -23,7 +24,9 @@ namespace bf = boost::filesystem;
|
||||
using namespace cpputils::logging;
|
||||
using std::make_shared;
|
||||
using std::shared_ptr;
|
||||
using std::string;
|
||||
using namespace fspp::fuse;
|
||||
using cpputils::set_thread_name;
|
||||
|
||||
namespace {
|
||||
bool is_valid_fspp_path(const bf::path& path) {
|
||||
@ -32,6 +35,18 @@ bool is_valid_fspp_path(const bf::path& path) {
|
||||
&& !path.has_root_name() // on Windows, it shouldn't have a device specifier (i.e. no "C:")
|
||||
&& (path.string() == path.generic_string()); // must use portable '/' as directory separator
|
||||
}
|
||||
|
||||
class ThreadNameForDebugging final {
|
||||
public:
|
||||
ThreadNameForDebugging(const string& threadName) {
|
||||
std::string name = "fspp_" + threadName;
|
||||
set_thread_name(name.c_str());
|
||||
}
|
||||
|
||||
~ThreadNameForDebugging() {
|
||||
set_thread_name("fspp_idle");
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#define FUSE_OBJ (static_cast<Fuse *>(fuse_get_context()->private_data))
|
||||
@ -335,6 +350,7 @@ void Fuse::unmount(const bf::path& mountdir, bool force) {
|
||||
}
|
||||
|
||||
int Fuse::getattr(const bf::path &path, fspp::fuse::STAT *stbuf) {
|
||||
ThreadNameForDebugging _threadName("getattr");
|
||||
#ifdef FSPP_LOG
|
||||
LOG(DEBUG, "getattr({}, _, _)", path);
|
||||
#endif
|
||||
@ -357,6 +373,7 @@ int Fuse::getattr(const bf::path &path, fspp::fuse::STAT *stbuf) {
|
||||
}
|
||||
|
||||
int Fuse::fgetattr(const bf::path &path, fspp::fuse::STAT *stbuf, fuse_file_info *fileinfo) {
|
||||
ThreadNameForDebugging _threadName("fgetattr");
|
||||
#ifdef FSPP_LOG
|
||||
LOG(DEBUG, "fgetattr({}, _, _)\n", path);
|
||||
#endif
|
||||
@ -389,6 +406,7 @@ int Fuse::fgetattr(const bf::path &path, fspp::fuse::STAT *stbuf, fuse_file_info
|
||||
}
|
||||
|
||||
int Fuse::readlink(const bf::path &path, char *buf, size_t size) {
|
||||
ThreadNameForDebugging _threadName("readlink");
|
||||
#ifdef FSPP_LOG
|
||||
LOG(DEBUG, "readlink({}, _, {})", path, size);
|
||||
#endif
|
||||
@ -414,11 +432,13 @@ int Fuse::mknod(const bf::path &path, ::mode_t mode, dev_t rdev) {
|
||||
UNUSED(rdev);
|
||||
UNUSED(mode);
|
||||
UNUSED(path);
|
||||
ThreadNameForDebugging _threadName("mknod");
|
||||
LOG(WARN, "Called non-implemented mknod({}, {}, _)", path, mode);
|
||||
return ENOSYS;
|
||||
}
|
||||
|
||||
int Fuse::mkdir(const bf::path &path, ::mode_t mode) {
|
||||
ThreadNameForDebugging _threadName("mkdir");
|
||||
#ifdef FSPP_LOG
|
||||
LOG(DEBUG, "mkdir({}, {})", path, mode);
|
||||
#endif
|
||||
@ -442,6 +462,7 @@ int Fuse::mkdir(const bf::path &path, ::mode_t mode) {
|
||||
}
|
||||
|
||||
int Fuse::unlink(const bf::path &path) {
|
||||
ThreadNameForDebugging _threadName("unlink");
|
||||
#ifdef FSPP_LOG
|
||||
LOG(DEBUG, "unlink({})", path);
|
||||
#endif
|
||||
@ -464,6 +485,7 @@ int Fuse::unlink(const bf::path &path) {
|
||||
}
|
||||
|
||||
int Fuse::rmdir(const bf::path &path) {
|
||||
ThreadNameForDebugging _threadName("rmdir");
|
||||
#ifdef FSPP_LOG
|
||||
LOG(DEBUG, "rmdir({})", path);
|
||||
#endif
|
||||
@ -486,6 +508,7 @@ int Fuse::rmdir(const bf::path &path) {
|
||||
}
|
||||
|
||||
int Fuse::symlink(const bf::path &to, const bf::path &from) {
|
||||
ThreadNameForDebugging _threadName("symlink");
|
||||
#ifdef FSPP_LOG
|
||||
LOG(DEBUG, "symlink({}, {})", to, from);
|
||||
#endif
|
||||
@ -509,6 +532,7 @@ int Fuse::symlink(const bf::path &to, const bf::path &from) {
|
||||
}
|
||||
|
||||
int Fuse::rename(const bf::path &from, const bf::path &to) {
|
||||
ThreadNameForDebugging _threadName("rename");
|
||||
#ifdef FSPP_LOG
|
||||
LOG(DEBUG, "rename({}, {})", from, to);
|
||||
#endif
|
||||
@ -533,6 +557,7 @@ int Fuse::rename(const bf::path &from, const bf::path &to) {
|
||||
|
||||
//TODO
|
||||
int Fuse::link(const bf::path &from, const bf::path &to) {
|
||||
ThreadNameForDebugging _threadName("link");
|
||||
LOG(WARN, "NOT IMPLEMENTED: link({}, {})", from, to);
|
||||
//auto real_from = _impl->RootDir() / from;
|
||||
//auto real_to = _impl->RootDir() / to;
|
||||
@ -542,6 +567,7 @@ int Fuse::link(const bf::path &from, const bf::path &to) {
|
||||
}
|
||||
|
||||
int Fuse::chmod(const bf::path &path, ::mode_t mode) {
|
||||
ThreadNameForDebugging _threadName("chmod");
|
||||
#ifdef FSPP_LOG
|
||||
LOG(DEBUG, "chmod({}, {})", path, mode);
|
||||
#endif
|
||||
@ -564,6 +590,7 @@ int Fuse::chmod(const bf::path &path, ::mode_t mode) {
|
||||
}
|
||||
|
||||
int Fuse::chown(const bf::path &path, ::uid_t uid, ::gid_t gid) {
|
||||
ThreadNameForDebugging _threadName("chown");
|
||||
#ifdef FSPP_LOG
|
||||
LOG(DEBUG, "chown({}, {}, {})", path, uid, gid);
|
||||
#endif
|
||||
@ -586,6 +613,7 @@ int Fuse::chown(const bf::path &path, ::uid_t uid, ::gid_t gid) {
|
||||
}
|
||||
|
||||
int Fuse::truncate(const bf::path &path, int64_t size) {
|
||||
ThreadNameForDebugging _threadName("truncate");
|
||||
#ifdef FSPP_LOG
|
||||
LOG(DEBUG, "truncate({}, {})", path, size);
|
||||
#endif
|
||||
@ -608,6 +636,7 @@ int Fuse::truncate(const bf::path &path, int64_t size) {
|
||||
}
|
||||
|
||||
int Fuse::ftruncate(const bf::path &path, int64_t size, fuse_file_info *fileinfo) {
|
||||
ThreadNameForDebugging _threadName("ftruncate");
|
||||
#ifdef FSPP_LOG
|
||||
LOG(DEBUG, "ftruncate({}, {})", path, size);
|
||||
#endif
|
||||
@ -630,6 +659,7 @@ int Fuse::ftruncate(const bf::path &path, int64_t size, fuse_file_info *fileinfo
|
||||
}
|
||||
|
||||
int Fuse::utimens(const bf::path &path, const timespec times[2]) {
|
||||
ThreadNameForDebugging _threadName("utimens");
|
||||
#ifdef FSPP_LOG
|
||||
LOG(DEBUG, "utimens({}, _)", path);
|
||||
#endif
|
||||
@ -652,6 +682,7 @@ int Fuse::utimens(const bf::path &path, const timespec times[2]) {
|
||||
}
|
||||
|
||||
int Fuse::open(const bf::path &path, fuse_file_info *fileinfo) {
|
||||
ThreadNameForDebugging _threadName("open");
|
||||
#ifdef FSPP_LOG
|
||||
LOG(DEBUG, "open({}, _)", path);
|
||||
#endif
|
||||
@ -674,6 +705,7 @@ int Fuse::open(const bf::path &path, fuse_file_info *fileinfo) {
|
||||
}
|
||||
|
||||
int Fuse::release(const bf::path &path, fuse_file_info *fileinfo) {
|
||||
ThreadNameForDebugging _threadName("release");
|
||||
#ifdef FSPP_LOG
|
||||
LOG(DEBUG, "release({}, _)", path);
|
||||
#endif
|
||||
@ -696,6 +728,7 @@ int Fuse::release(const bf::path &path, fuse_file_info *fileinfo) {
|
||||
}
|
||||
|
||||
int Fuse::read(const bf::path &path, char *buf, size_t size, int64_t offset, fuse_file_info *fileinfo) {
|
||||
ThreadNameForDebugging _threadName("read");
|
||||
#ifdef FSPP_LOG
|
||||
LOG(DEBUG, "read({}, _, {}, {}, _)", path, size, offset);
|
||||
#endif
|
||||
@ -717,6 +750,7 @@ int Fuse::read(const bf::path &path, char *buf, size_t size, int64_t offset, fus
|
||||
}
|
||||
|
||||
int Fuse::write(const bf::path &path, const char *buf, size_t size, int64_t offset, fuse_file_info *fileinfo) {
|
||||
ThreadNameForDebugging _threadName("write");
|
||||
#ifdef FSPP_LOG
|
||||
LOG(DEBUG, "write({}, _, {}, {}, _)", path, size, offsset);
|
||||
#endif
|
||||
@ -739,6 +773,7 @@ int Fuse::write(const bf::path &path, const char *buf, size_t size, int64_t offs
|
||||
}
|
||||
|
||||
int Fuse::statfs(const bf::path &path, struct ::statvfs *fsstat) {
|
||||
ThreadNameForDebugging _threadName("statfs");
|
||||
#ifdef FSPP_LOG
|
||||
LOG(DEBUG, "statfs({}, _)", path);
|
||||
#endif
|
||||
@ -762,6 +797,7 @@ int Fuse::statfs(const bf::path &path, struct ::statvfs *fsstat) {
|
||||
}
|
||||
|
||||
int Fuse::flush(const bf::path &path, fuse_file_info *fileinfo) {
|
||||
ThreadNameForDebugging _threadName("flush");
|
||||
#ifdef FSPP_LOG
|
||||
LOG(WARN, "flush({}, _)", path);
|
||||
#endif
|
||||
@ -784,6 +820,7 @@ int Fuse::flush(const bf::path &path, fuse_file_info *fileinfo) {
|
||||
}
|
||||
|
||||
int Fuse::fsync(const bf::path &path, int datasync, fuse_file_info *fileinfo) {
|
||||
ThreadNameForDebugging _threadName("fsync");
|
||||
#ifdef FSPP_LOG
|
||||
LOG(DEBUG, "fsync({}, {}, _)", path, datasync);
|
||||
#endif
|
||||
@ -812,12 +849,14 @@ int Fuse::fsync(const bf::path &path, int datasync, fuse_file_info *fileinfo) {
|
||||
int Fuse::opendir(const bf::path &path, fuse_file_info *fileinfo) {
|
||||
UNUSED(path);
|
||||
UNUSED(fileinfo);
|
||||
ThreadNameForDebugging _threadName("opendir");
|
||||
//LOG(DEBUG, "opendir({}, _)", path);
|
||||
//We don't need opendir, because readdir works directly on the path
|
||||
return 0;
|
||||
}
|
||||
|
||||
int Fuse::readdir(const bf::path &path, void *buf, fuse_fill_dir_t filler, int64_t offset, fuse_file_info *fileinfo) {
|
||||
ThreadNameForDebugging _threadName("readdir");
|
||||
#ifdef FSPP_LOG
|
||||
LOG(DEBUG, "readdir({}, _, _, {}, _)", path, offest);
|
||||
#endif
|
||||
@ -863,6 +902,7 @@ int Fuse::readdir(const bf::path &path, void *buf, fuse_fill_dir_t filler, int64
|
||||
int Fuse::releasedir(const bf::path &path, fuse_file_info *fileinfo) {
|
||||
UNUSED(path);
|
||||
UNUSED(fileinfo);
|
||||
ThreadNameForDebugging _threadName("releasedir");
|
||||
//LOG(DEBUG, "releasedir({}, _)", path);
|
||||
//We don't need releasedir, because readdir works directly on the path
|
||||
return 0;
|
||||
@ -873,12 +913,14 @@ int Fuse::fsyncdir(const bf::path &path, int datasync, fuse_file_info *fileinfo)
|
||||
UNUSED(fileinfo);
|
||||
UNUSED(datasync);
|
||||
UNUSED(path);
|
||||
ThreadNameForDebugging _threadName("fsyncdir");
|
||||
//LOG(WARN, "Called non-implemented fsyncdir({}, {}, _)", path, datasync);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Fuse::init(fuse_conn_info *conn) {
|
||||
UNUSED(conn);
|
||||
ThreadNameForDebugging _threadName("init");
|
||||
_fs = _init(this);
|
||||
|
||||
LOG(INFO, "Filesystem started.");
|
||||
@ -892,6 +934,7 @@ void Fuse::init(fuse_conn_info *conn) {
|
||||
}
|
||||
|
||||
void Fuse::destroy() {
|
||||
ThreadNameForDebugging _threadName("destroy");
|
||||
_fs = make_shared<InvalidFilesystem>();
|
||||
LOG(INFO, "Filesystem stopped.");
|
||||
_running = false;
|
||||
@ -899,6 +942,7 @@ void Fuse::destroy() {
|
||||
}
|
||||
|
||||
int Fuse::access(const bf::path &path, int mask) {
|
||||
ThreadNameForDebugging _threadName("access");
|
||||
#ifdef FSPP_LOG
|
||||
LOG(DEBUG, "access({}, {})", path, mask);
|
||||
#endif
|
||||
@ -921,6 +965,7 @@ int Fuse::access(const bf::path &path, int mask) {
|
||||
}
|
||||
|
||||
int Fuse::create(const bf::path &path, ::mode_t mode, fuse_file_info *fileinfo) {
|
||||
ThreadNameForDebugging _threadName("create");
|
||||
#ifdef FSPP_LOG
|
||||
LOG(DEBUG, "create({}, {}, _)", path, mode);
|
||||
#endif
|
||||
|
@ -13,7 +13,7 @@ using ::testing::Test;
|
||||
//Test that Cache uses a move constructor for Value if possible
|
||||
class CacheTest_MoveConstructor: public Test {
|
||||
public:
|
||||
CacheTest_MoveConstructor(): cache(make_unique_ref<Cache<MinimalKeyType, CopyableMovableValueType, 100>>()) {
|
||||
CacheTest_MoveConstructor(): cache(make_unique_ref<Cache<MinimalKeyType, CopyableMovableValueType, 100>>("test")) {
|
||||
CopyableMovableValueType::numCopyConstructorCalled = 0;
|
||||
}
|
||||
unique_ref<Cache<MinimalKeyType, CopyableMovableValueType, 100>> cache;
|
||||
|
@ -36,7 +36,7 @@ private:
|
||||
|
||||
class CacheTest_RaceCondition: public ::testing::Test {
|
||||
public:
|
||||
CacheTest_RaceCondition(): cache(), destructorStarted(), destructorFinished(false) {}
|
||||
CacheTest_RaceCondition(): cache("test"), destructorStarted(), destructorFinished(false) {}
|
||||
|
||||
static constexpr unsigned int MAX_ENTRIES = 100;
|
||||
|
||||
|
@ -37,7 +37,7 @@ class PeriodicTaskTest: public Test {
|
||||
};
|
||||
|
||||
TEST_F(PeriodicTaskTest, DoesntDeadlockInDestructorWhenDestructedImmediately) {
|
||||
PeriodicTask task([](){}, 1);
|
||||
PeriodicTask task([](){}, 1, "test");
|
||||
}
|
||||
|
||||
TEST_F(PeriodicTaskTest, CallsCallbackAtLeast10Times) {
|
||||
@ -45,7 +45,7 @@ TEST_F(PeriodicTaskTest, CallsCallbackAtLeast10Times) {
|
||||
|
||||
PeriodicTask task([&counter](){
|
||||
counter.decrease();
|
||||
}, 0.001);
|
||||
}, 0.001, "test");
|
||||
|
||||
counter.waitForZero();
|
||||
}
|
||||
@ -55,7 +55,7 @@ TEST_F(PeriodicTaskTest, DoesntCallCallbackAfterDestruction) {
|
||||
{
|
||||
PeriodicTask task([&callCount](){
|
||||
callCount += 1;
|
||||
}, 0.001);
|
||||
}, 0.001, "test");
|
||||
}
|
||||
int callCountDirectlyAfterDestruction = callCount;
|
||||
boost::this_thread::sleep_for(boost::chrono::seconds(1));
|
||||
|
@ -13,7 +13,7 @@
|
||||
// Furthermore, the class checks that there are no memory leaks left after destructing the QueueMap (by counting leftover instances of Keys/Values).
|
||||
class CacheTest: public ::testing::Test {
|
||||
public:
|
||||
CacheTest(): _cache() {}
|
||||
CacheTest(): _cache("test") {}
|
||||
|
||||
void push(int key, int value);
|
||||
boost::optional<int> pop(int key);
|
||||
|
@ -54,6 +54,7 @@ set(SOURCES
|
||||
system/MemoryTest.cpp
|
||||
system/HomedirTest.cpp
|
||||
system/EnvTest.cpp
|
||||
thread/debugging_test.cpp
|
||||
value_type/ValueTypeTest.cpp
|
||||
)
|
||||
|
||||
|
62
test/cpp-utils/thread/debugging_test.cpp
Normal file
62
test/cpp-utils/thread/debugging_test.cpp
Normal file
@ -0,0 +1,62 @@
|
||||
#include <cpp-utils/thread/debugging.h>
|
||||
#include <cpp-utils/assert/assert.h>
|
||||
#include <cpp-utils/lock/ConditionBarrier.h>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
using namespace cpputils;
|
||||
using std::string;
|
||||
|
||||
TEST(ThreadDebuggingTest_ThreadName, givenMainThread_whenSettingAndGetting_thenDoesntCrash) {
|
||||
set_thread_name("my_thread_name");
|
||||
get_thread_name();
|
||||
}
|
||||
|
||||
TEST(ThreadDebuggingTest_ThreadName, givenChildThread_whenSettingAndGetting_thenDoesntCrash) {
|
||||
ConditionBarrier nameIsChecked;
|
||||
|
||||
bool child_didnt_crash = false;
|
||||
std::thread child([&] {
|
||||
set_thread_name("my_thread_name");
|
||||
get_thread_name();
|
||||
child_didnt_crash = true;
|
||||
nameIsChecked.wait();
|
||||
});
|
||||
get_thread_name(&child);
|
||||
nameIsChecked.release(); // getting the name of a not-running thread would cause errors, so let's make sure we only exit after getting the name
|
||||
child.join();
|
||||
EXPECT_TRUE(child_didnt_crash);
|
||||
}
|
||||
|
||||
TEST(ThreadDebuggingTest_ThreadName, givenMainThread_whenGettingFromInside_thenIsCorrect) {
|
||||
set_thread_name("my_thread_name");
|
||||
string name = get_thread_name();
|
||||
EXPECT_EQ("my_thread_name", name);
|
||||
}
|
||||
|
||||
TEST(ThreadDebuggingTest_ThreadName, givenChildThread_whenGettingFromInside_thenIsCorrect) {
|
||||
std::thread child([] {
|
||||
set_thread_name("my_thread_name");
|
||||
string name = get_thread_name();
|
||||
EXPECT_EQ("my_thread_name", name);
|
||||
});
|
||||
child.join();
|
||||
}
|
||||
|
||||
TEST(ThreadDebuggingTest_ThreadName, givenChildThread_whenGettingFromOutside_thenIsCorrect) {
|
||||
ConditionBarrier nameIsSet;
|
||||
ConditionBarrier nameIsChecked;
|
||||
|
||||
std::thread child([&] {
|
||||
set_thread_name("my_thread_name");
|
||||
nameIsSet.release();
|
||||
nameIsChecked.wait();
|
||||
});
|
||||
|
||||
nameIsSet.wait();
|
||||
string name = get_thread_name(&child);
|
||||
EXPECT_EQ("my_thread_name", name);
|
||||
|
||||
nameIsChecked.release();
|
||||
child.join();
|
||||
}
|
||||
|
@ -15,7 +15,7 @@ public:
|
||||
CallAfterTimeoutTest(): called(false) {}
|
||||
|
||||
unique_ref<CallAfterTimeout> callAfterTimeout(milliseconds timeout) {
|
||||
return make_unique_ref<CallAfterTimeout>(timeout, [this] {called = true;});
|
||||
return make_unique_ref<CallAfterTimeout>(timeout, [this] {called = true;}, "test");
|
||||
}
|
||||
|
||||
std::atomic<bool> called;
|
||||
|
Loading…
Reference in New Issue
Block a user