Merge branch 'hotfix/threadname' into release/0.10

This commit is contained in:
Sebastian Messmer 2019-01-20 20:19:54 -08:00
commit 1d45833f90
25 changed files with 346 additions and 48 deletions

View File

@ -20,13 +20,11 @@ init:
- echo %NUMBER_OF_PROCESSORS% - echo %NUMBER_OF_PROCESSORS%
- echo %PLATFORM% - echo %PLATFORM%
- echo %APPVEYOR_BUILD_WORKER_IMAGE% - echo %APPVEYOR_BUILD_WORKER_IMAGE%
- set arch= - set arch=32
- if "%PLATFORM%"=="x64" ( set arch= Win64) - if "%PLATFORM%"=="x64" ( set arch=64)
- if "%APPVEYOR_BUILD_WORKER_IMAGE%"=="Visual Studio 2013" (set generator=Visual Studio 12 2013%arch%) - set VisualStudioVersion=2017
- if "%APPVEYOR_BUILD_WORKER_IMAGE%"=="Visual Studio 2015" (set generator=Visual Studio 14 2015%arch%) - if "%APPVEYOR_BUILD_WORKER_IMAGE%" == "Visual Studio 2017 Preview" ( set VisualStudioVersion=Preview)
- if "%APPVEYOR_BUILD_WORKER_IMAGE%"=="Visual Studio 2017" (set generator=Visual Studio 15 2017%arch%) - cmd: call "C:\Program Files (x86)\Microsoft Visual Studio\%VisualStudioVersion%\Community\VC\Auxiliary\Build\vcvars%arch%.bat"
- if "%APPVEYOR_BUILD_WORKER_IMAGE%"=="Visual Studio 2017 Preview" (set generator=Visual Studio 15 2017%arch%)
- echo %generator%
install: install:
- choco install -y dokany --version 1.1.0.2000 --installargs INSTALLDEVFILES=1 - choco install -y dokany --version 1.1.0.2000 --installargs INSTALLDEVFILES=1
@ -35,17 +33,17 @@ install:
build_script: build_script:
- cmd: mkdir build - cmd: mkdir build
- cmd: cd 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" - 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"
# TODO Make build parallel
- cmd: cmake --build . --config %CONFIGURATION% - cmd: cmake --build . --config %CONFIGURATION%
- cmd: .\test\gitversion\%CONFIGURATION%\gitversion-test.exe - cmd: .\test\gitversion\gitversion-test.exe
- cmd: cd .\test\cpp-utils\%CONFIGURATION%\ && .\cpp-utils-test.exe && cd ..\..\.. # cpp-utils-test disables ThreadDebuggingTest_ThreadName.*_thenIsCorrect because the appveyor image is too old to support the API needed for that
#- cmd: .\test\fspp\%CONFIGURATION%\fspp-test.exe - cmd: cd .\test\cpp-utils\ && .\cpp-utils-test.exe --gtest_filter=-ThreadDebuggingTest_ThreadName.*_thenIsCorrect && cd ..\..
- cmd: .\test\parallelaccessstore\%CONFIGURATION%\parallelaccessstore-test.exe #- cmd: .\test\fspp\fspp-test.exe
- cmd: .\test\blockstore\%CONFIGURATION%\blockstore-test.exe - cmd: .\test\parallelaccessstore\parallelaccessstore-test.exe
- cmd: .\test\blobstore\%CONFIGURATION%\blobstore-test.exe - cmd: .\test\blockstore\blockstore-test.exe
- cmd: .\test\cryfs\%CONFIGURATION%\cryfs-test.exe - cmd: .\test\blobstore\blobstore-test.exe
#- cmd: .\test\cryfs-cli\%CONFIGURATION%\cryfs-cli-test.exe - cmd: .\test\cryfs\cryfs-test.exe
#- cmd: .\test\cryfs-cli\cryfs-cli-test.exe
- cmd: cpack -C %CONFIGURATION% --verbose -G WIX - cmd: cpack -C %CONFIGURATION% --verbose -G WIX

View File

@ -45,7 +45,7 @@ void CachingBlockStore2::CachedBlock::write(Data data) {
} }
CachingBlockStore2::CachingBlockStore2(cpputils::unique_ref<BlockStore2> baseBlockStore) 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) { bool CachingBlockStore2::tryCreate(const BlockId &blockId, const Data &data) {

View File

@ -24,7 +24,7 @@ public:
static constexpr double PURGE_INTERVAL = 0.5; // With this interval, we check for entries to purge 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) 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(); ~Cache();
uint32_t size() const; 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> constexpr double Cache<Key, Value, MAX_ENTRIES>::MAX_LIFETIME_SEC;
template<class Key, class Value, uint32_t MAX_ENTRIES> 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, //Don't initialize timeoutFlusher in the initializer list,
//because it then might already call Cache::popOldEntries() before Cache is done constructing. //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> template<class Key, class Value, uint32_t MAX_ENTRIES>

View File

@ -7,10 +7,10 @@ using namespace cpputils::logging;
namespace blockstore { namespace blockstore {
namespace caching { namespace caching {
PeriodicTask::PeriodicTask(function<void ()> task, double intervalSec) : PeriodicTask::PeriodicTask(function<void ()> task, double intervalSec, std::string threadName) :
_task(task), _task(task),
_interval(static_cast<uint64_t>(UINT64_C(1000000000) * intervalSec)), _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(); _thread.start();
} }

View File

@ -11,7 +11,7 @@ namespace caching {
class PeriodicTask final { class PeriodicTask final {
public: public:
PeriodicTask(std::function<void ()> task, double intervalSec); PeriodicTask(std::function<void ()> task, double intervalSec, std::string threadName);
private: private:
bool _loopIteration(); bool _loopIteration();

View File

@ -24,6 +24,8 @@ set(SOURCES
io/pipestream.cpp io/pipestream.cpp
thread/LoopThread.cpp thread/LoopThread.cpp
thread/ThreadSystem.cpp thread/ThreadSystem.cpp
thread/debugging_nonwindows.cpp
thread/debugging_windows.cpp
random/Random.cpp random/Random.cpp
random/RandomGeneratorThread.cpp random/RandomGeneratorThread.cpp
random/OSRandomGenerator.cpp random/OSRandomGenerator.cpp

View File

@ -8,7 +8,7 @@ namespace cpputils {
_buffer(buffer), _buffer(buffer),
_minSize(minSize), _minSize(minSize),
_maxSize(maxSize), _maxSize(maxSize),
_thread(std::bind(&RandomGeneratorThread::_loopIteration, this)) { _thread(std::bind(&RandomGeneratorThread::_loopIteration, this), "RandomGeneratorThread") {
ASSERT(_maxSize >= _minSize, "Invalid parameters"); ASSERT(_maxSize >= _minSize, "Invalid parameters");
} }

View File

@ -6,7 +6,8 @@ using boost::none;
namespace cpputils { 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() { LoopThread::~LoopThread() {
@ -16,7 +17,7 @@ namespace cpputils {
} }
void LoopThread::start() { void LoopThread::start() {
_runningHandle = ThreadSystem::singleton().start(_loopIteration); _runningHandle = ThreadSystem::singleton().start(_loopIteration, _threadName);
} }
void LoopThread::stop() { void LoopThread::stop() {

View File

@ -14,7 +14,7 @@ namespace cpputils {
class LoopThread final { class LoopThread final {
public: public:
// The loopIteration callback returns true, if more iterations should be run, and false, if the thread should be terminated. // 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(); ~LoopThread();
void start(); void start();
void stop(); void stop();
@ -22,6 +22,7 @@ namespace cpputils {
private: private:
std::function<bool()> _loopIteration; std::function<bool()> _loopIteration;
boost::optional<ThreadSystem::Handle> _runningHandle; boost::optional<ThreadSystem::Handle> _runningHandle;
std::string _threadName;
DISALLOW_COPY_AND_ASSIGN(LoopThread); DISALLOW_COPY_AND_ASSIGN(LoopThread);
}; };

View File

@ -1,7 +1,9 @@
#include "ThreadSystem.h" #include "ThreadSystem.h"
#include "../logging/logging.h" #include "../logging/logging.h"
#include "debugging.h"
using std::function; using std::function;
using std::string;
using namespace cpputils::logging; using namespace cpputils::logging;
namespace cpputils { namespace cpputils {
@ -21,10 +23,10 @@ namespace cpputils {
#endif #endif
} }
ThreadSystem::Handle ThreadSystem::start(function<bool()> loopIteration) { ThreadSystem::Handle ThreadSystem::start(function<bool()> loopIteration, string threadName) {
boost::unique_lock<boost::mutex> lock(_mutex); boost::unique_lock<boost::mutex> lock(_mutex);
auto thread = _startThread(loopIteration); auto thread = _startThread(loopIteration, threadName);
_runningThreads.push_back(RunningThread{std::move(loopIteration), std::move(thread)}); _runningThreads.push_back(RunningThread{std::move(threadName), std::move(loopIteration), std::move(thread)});
return std::prev(_runningThreads.end()); return std::prev(_runningThreads.end());
} }
@ -59,13 +61,14 @@ namespace cpputils {
void ThreadSystem::_restartAllThreads() { void ThreadSystem::_restartAllThreads() {
for (RunningThread &thread : _runningThreads) { 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 _mutex.unlock(); // Was locked in the before-fork handler
} }
boost::thread ThreadSystem::_startThread(function<bool()> loopIteration) { boost::thread ThreadSystem::_startThread(function<bool()> loopIteration, const string& threadName) {
return boost::thread([loopIteration = std::move(loopIteration)] { return boost::thread([loopIteration = std::move(loopIteration), threadName] {
cpputils::set_thread_name(threadName.c_str());
ThreadSystem::_runThread(loopIteration); ThreadSystem::_runThread(loopIteration);
}); });
} }

View File

@ -13,6 +13,7 @@ namespace cpputils {
class ThreadSystem final { class ThreadSystem final {
private: private:
struct RunningThread { 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. 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. boost::thread thread; // boost::thread because we need it to be interruptible.
}; };
@ -21,7 +22,7 @@ namespace cpputils {
static ThreadSystem &singleton(); static ThreadSystem &singleton();
Handle start(std::function<bool()> loopIteration); Handle start(std::function<bool()> loopIteration, std::string threadName);
void stop(Handle handle); void stop(Handle handle);
private: private:
@ -34,7 +35,7 @@ namespace cpputils {
//TODO Rename to _doOnBeforeFork and _doAfterFork or similar, because they also handle locking _mutex for fork(). //TODO Rename to _doOnBeforeFork and _doAfterFork or similar, because they also handle locking _mutex for fork().
void _stopAllThreadsForRestart(); void _stopAllThreadsForRestart();
void _restartAllThreads(); 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 std::list<RunningThread> _runningThreads; // std::list, because we give out iterators as handles
boost::mutex _mutex; boost::mutex _mutex;

View 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

View 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

View 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

View File

@ -9,7 +9,7 @@
namespace cryfs_cli { namespace cryfs_cli {
class CallAfterTimeout final { class CallAfterTimeout final {
public: 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(); void resetTimer();
private: private:
bool _checkTimeoutThreadIteration(); bool _checkTimeoutThreadIteration();
@ -25,8 +25,8 @@ namespace cryfs_cli {
DISALLOW_COPY_AND_ASSIGN(CallAfterTimeout); DISALLOW_COPY_AND_ASSIGN(CallAfterTimeout);
}; };
inline CallAfterTimeout::CallAfterTimeout(boost::chrono::milliseconds timeout, std::function<void()> callback) 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)) { :_callback(std::move(callback)), _timeout(timeout), _start(), _checkTimeoutThread(std::bind(&CallAfterTimeout::_checkTimeoutThreadIteration, this), "timeout_" + timeoutName) {
resetTimer(); resetTimer();
_checkTimeoutThread.start(); _checkTimeoutThread.start();
} }

View File

@ -26,6 +26,7 @@
#include <cryfs/localstate/BasedirMetadata.h> #include <cryfs/localstate/BasedirMetadata.h>
#include "Environment.h" #include "Environment.h"
#include <cryfs/CryfsException.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 Many functions accessing the ProgramOptions object. Factor out into class that stores it as a member.
//TODO Factor out class handling askPassword //TODO Factor out class handling askPassword
@ -297,7 +298,7 @@ namespace cryfs_cli {
return none; return none;
} }
uint64_t millis = std::llround(60000 * (*minutes)); 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) { 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) { int Cli::main(int argc, const char *argv[], unique_ref<HttpClient> httpClient, std::function<void()> onMounted) {
cpputils::showBacktraceOnCrash(); cpputils::showBacktraceOnCrash();
cpputils::set_thread_name("cryfs");
try { try {
_showVersion(std::move(httpClient)); _showVersion(std::move(httpClient));

View File

@ -50,7 +50,7 @@ namespace cryfs {
inline CachingFsBlobStore::CachingFsBlobStore(cpputils::unique_ref<fsblobstore::FsBlobStore> baseBlobStore) inline CachingFsBlobStore::CachingFsBlobStore(cpputils::unique_ref<fsblobstore::FsBlobStore> baseBlobStore)
: _baseBlobStore(std::move(baseBlobStore)), _cache() { : _baseBlobStore(std::move(baseBlobStore)), _cache("fsblobstore") {
} }
inline CachingFsBlobStore::~CachingFsBlobStore() { inline CachingFsBlobStore::~CachingFsBlobStore() {

View File

@ -8,6 +8,7 @@
#include <cpp-utils/assert/assert.h> #include <cpp-utils/assert/assert.h>
#include <cpp-utils/logging/logging.h> #include <cpp-utils/logging/logging.h>
#include <cpp-utils/process/subprocess.h> #include <cpp-utils/process/subprocess.h>
#include <cpp-utils/thread/debugging.h>
#include <csignal> #include <csignal>
#include "InvalidFilesystem.h" #include "InvalidFilesystem.h"
@ -23,7 +24,9 @@ namespace bf = boost::filesystem;
using namespace cpputils::logging; using namespace cpputils::logging;
using std::make_shared; using std::make_shared;
using std::shared_ptr; using std::shared_ptr;
using std::string;
using namespace fspp::fuse; using namespace fspp::fuse;
using cpputils::set_thread_name;
namespace { namespace {
bool is_valid_fspp_path(const bf::path& path) { 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.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 && (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)) #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) { int Fuse::getattr(const bf::path &path, fspp::fuse::STAT *stbuf) {
ThreadNameForDebugging _threadName("getattr");
#ifdef FSPP_LOG #ifdef FSPP_LOG
LOG(DEBUG, "getattr({}, _, _)", path); LOG(DEBUG, "getattr({}, _, _)", path);
#endif #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) { int Fuse::fgetattr(const bf::path &path, fspp::fuse::STAT *stbuf, fuse_file_info *fileinfo) {
ThreadNameForDebugging _threadName("fgetattr");
#ifdef FSPP_LOG #ifdef FSPP_LOG
LOG(DEBUG, "fgetattr({}, _, _)\n", path); LOG(DEBUG, "fgetattr({}, _, _)\n", path);
#endif #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) { int Fuse::readlink(const bf::path &path, char *buf, size_t size) {
ThreadNameForDebugging _threadName("readlink");
#ifdef FSPP_LOG #ifdef FSPP_LOG
LOG(DEBUG, "readlink({}, _, {})", path, size); LOG(DEBUG, "readlink({}, _, {})", path, size);
#endif #endif
@ -414,11 +432,13 @@ int Fuse::mknod(const bf::path &path, ::mode_t mode, dev_t rdev) {
UNUSED(rdev); UNUSED(rdev);
UNUSED(mode); UNUSED(mode);
UNUSED(path); UNUSED(path);
ThreadNameForDebugging _threadName("mknod");
LOG(WARN, "Called non-implemented mknod({}, {}, _)", path, mode); LOG(WARN, "Called non-implemented mknod({}, {}, _)", path, mode);
return ENOSYS; return ENOSYS;
} }
int Fuse::mkdir(const bf::path &path, ::mode_t mode) { int Fuse::mkdir(const bf::path &path, ::mode_t mode) {
ThreadNameForDebugging _threadName("mkdir");
#ifdef FSPP_LOG #ifdef FSPP_LOG
LOG(DEBUG, "mkdir({}, {})", path, mode); LOG(DEBUG, "mkdir({}, {})", path, mode);
#endif #endif
@ -442,6 +462,7 @@ int Fuse::mkdir(const bf::path &path, ::mode_t mode) {
} }
int Fuse::unlink(const bf::path &path) { int Fuse::unlink(const bf::path &path) {
ThreadNameForDebugging _threadName("unlink");
#ifdef FSPP_LOG #ifdef FSPP_LOG
LOG(DEBUG, "unlink({})", path); LOG(DEBUG, "unlink({})", path);
#endif #endif
@ -464,6 +485,7 @@ int Fuse::unlink(const bf::path &path) {
} }
int Fuse::rmdir(const bf::path &path) { int Fuse::rmdir(const bf::path &path) {
ThreadNameForDebugging _threadName("rmdir");
#ifdef FSPP_LOG #ifdef FSPP_LOG
LOG(DEBUG, "rmdir({})", path); LOG(DEBUG, "rmdir({})", path);
#endif #endif
@ -486,6 +508,7 @@ int Fuse::rmdir(const bf::path &path) {
} }
int Fuse::symlink(const bf::path &to, const bf::path &from) { int Fuse::symlink(const bf::path &to, const bf::path &from) {
ThreadNameForDebugging _threadName("symlink");
#ifdef FSPP_LOG #ifdef FSPP_LOG
LOG(DEBUG, "symlink({}, {})", to, from); LOG(DEBUG, "symlink({}, {})", to, from);
#endif #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) { int Fuse::rename(const bf::path &from, const bf::path &to) {
ThreadNameForDebugging _threadName("rename");
#ifdef FSPP_LOG #ifdef FSPP_LOG
LOG(DEBUG, "rename({}, {})", from, to); LOG(DEBUG, "rename({}, {})", from, to);
#endif #endif
@ -533,6 +557,7 @@ int Fuse::rename(const bf::path &from, const bf::path &to) {
//TODO //TODO
int Fuse::link(const bf::path &from, const bf::path &to) { int Fuse::link(const bf::path &from, const bf::path &to) {
ThreadNameForDebugging _threadName("link");
LOG(WARN, "NOT IMPLEMENTED: link({}, {})", from, to); LOG(WARN, "NOT IMPLEMENTED: link({}, {})", from, to);
//auto real_from = _impl->RootDir() / from; //auto real_from = _impl->RootDir() / from;
//auto real_to = _impl->RootDir() / to; //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) { int Fuse::chmod(const bf::path &path, ::mode_t mode) {
ThreadNameForDebugging _threadName("chmod");
#ifdef FSPP_LOG #ifdef FSPP_LOG
LOG(DEBUG, "chmod({}, {})", path, mode); LOG(DEBUG, "chmod({}, {})", path, mode);
#endif #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) { int Fuse::chown(const bf::path &path, ::uid_t uid, ::gid_t gid) {
ThreadNameForDebugging _threadName("chown");
#ifdef FSPP_LOG #ifdef FSPP_LOG
LOG(DEBUG, "chown({}, {}, {})", path, uid, gid); LOG(DEBUG, "chown({}, {}, {})", path, uid, gid);
#endif #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) { int Fuse::truncate(const bf::path &path, int64_t size) {
ThreadNameForDebugging _threadName("truncate");
#ifdef FSPP_LOG #ifdef FSPP_LOG
LOG(DEBUG, "truncate({}, {})", path, size); LOG(DEBUG, "truncate({}, {})", path, size);
#endif #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) { int Fuse::ftruncate(const bf::path &path, int64_t size, fuse_file_info *fileinfo) {
ThreadNameForDebugging _threadName("ftruncate");
#ifdef FSPP_LOG #ifdef FSPP_LOG
LOG(DEBUG, "ftruncate({}, {})", path, size); LOG(DEBUG, "ftruncate({}, {})", path, size);
#endif #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]) { int Fuse::utimens(const bf::path &path, const timespec times[2]) {
ThreadNameForDebugging _threadName("utimens");
#ifdef FSPP_LOG #ifdef FSPP_LOG
LOG(DEBUG, "utimens({}, _)", path); LOG(DEBUG, "utimens({}, _)", path);
#endif #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) { int Fuse::open(const bf::path &path, fuse_file_info *fileinfo) {
ThreadNameForDebugging _threadName("open");
#ifdef FSPP_LOG #ifdef FSPP_LOG
LOG(DEBUG, "open({}, _)", path); LOG(DEBUG, "open({}, _)", path);
#endif #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) { int Fuse::release(const bf::path &path, fuse_file_info *fileinfo) {
ThreadNameForDebugging _threadName("release");
#ifdef FSPP_LOG #ifdef FSPP_LOG
LOG(DEBUG, "release({}, _)", path); LOG(DEBUG, "release({}, _)", path);
#endif #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) { 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 #ifdef FSPP_LOG
LOG(DEBUG, "read({}, _, {}, {}, _)", path, size, offset); LOG(DEBUG, "read({}, _, {}, {}, _)", path, size, offset);
#endif #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) { 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 #ifdef FSPP_LOG
LOG(DEBUG, "write({}, _, {}, {}, _)", path, size, offsset); LOG(DEBUG, "write({}, _, {}, {}, _)", path, size, offsset);
#endif #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) { int Fuse::statfs(const bf::path &path, struct ::statvfs *fsstat) {
ThreadNameForDebugging _threadName("statfs");
#ifdef FSPP_LOG #ifdef FSPP_LOG
LOG(DEBUG, "statfs({}, _)", path); LOG(DEBUG, "statfs({}, _)", path);
#endif #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) { int Fuse::flush(const bf::path &path, fuse_file_info *fileinfo) {
ThreadNameForDebugging _threadName("flush");
#ifdef FSPP_LOG #ifdef FSPP_LOG
LOG(WARN, "flush({}, _)", path); LOG(WARN, "flush({}, _)", path);
#endif #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) { int Fuse::fsync(const bf::path &path, int datasync, fuse_file_info *fileinfo) {
ThreadNameForDebugging _threadName("fsync");
#ifdef FSPP_LOG #ifdef FSPP_LOG
LOG(DEBUG, "fsync({}, {}, _)", path, datasync); LOG(DEBUG, "fsync({}, {}, _)", path, datasync);
#endif #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) { int Fuse::opendir(const bf::path &path, fuse_file_info *fileinfo) {
UNUSED(path); UNUSED(path);
UNUSED(fileinfo); UNUSED(fileinfo);
ThreadNameForDebugging _threadName("opendir");
//LOG(DEBUG, "opendir({}, _)", path); //LOG(DEBUG, "opendir({}, _)", path);
//We don't need opendir, because readdir works directly on the path //We don't need opendir, because readdir works directly on the path
return 0; return 0;
} }
int Fuse::readdir(const bf::path &path, void *buf, fuse_fill_dir_t filler, int64_t offset, fuse_file_info *fileinfo) { 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 #ifdef FSPP_LOG
LOG(DEBUG, "readdir({}, _, _, {}, _)", path, offest); LOG(DEBUG, "readdir({}, _, _, {}, _)", path, offest);
#endif #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) { int Fuse::releasedir(const bf::path &path, fuse_file_info *fileinfo) {
UNUSED(path); UNUSED(path);
UNUSED(fileinfo); UNUSED(fileinfo);
ThreadNameForDebugging _threadName("releasedir");
//LOG(DEBUG, "releasedir({}, _)", path); //LOG(DEBUG, "releasedir({}, _)", path);
//We don't need releasedir, because readdir works directly on the path //We don't need releasedir, because readdir works directly on the path
return 0; return 0;
@ -873,12 +913,14 @@ int Fuse::fsyncdir(const bf::path &path, int datasync, fuse_file_info *fileinfo)
UNUSED(fileinfo); UNUSED(fileinfo);
UNUSED(datasync); UNUSED(datasync);
UNUSED(path); UNUSED(path);
ThreadNameForDebugging _threadName("fsyncdir");
//LOG(WARN, "Called non-implemented fsyncdir({}, {}, _)", path, datasync); //LOG(WARN, "Called non-implemented fsyncdir({}, {}, _)", path, datasync);
return 0; return 0;
} }
void Fuse::init(fuse_conn_info *conn) { void Fuse::init(fuse_conn_info *conn) {
UNUSED(conn); UNUSED(conn);
ThreadNameForDebugging _threadName("init");
_fs = _init(this); _fs = _init(this);
LOG(INFO, "Filesystem started."); LOG(INFO, "Filesystem started.");
@ -892,6 +934,7 @@ void Fuse::init(fuse_conn_info *conn) {
} }
void Fuse::destroy() { void Fuse::destroy() {
ThreadNameForDebugging _threadName("destroy");
_fs = make_shared<InvalidFilesystem>(); _fs = make_shared<InvalidFilesystem>();
LOG(INFO, "Filesystem stopped."); LOG(INFO, "Filesystem stopped.");
_running = false; _running = false;
@ -899,6 +942,7 @@ void Fuse::destroy() {
} }
int Fuse::access(const bf::path &path, int mask) { int Fuse::access(const bf::path &path, int mask) {
ThreadNameForDebugging _threadName("access");
#ifdef FSPP_LOG #ifdef FSPP_LOG
LOG(DEBUG, "access({}, {})", path, mask); LOG(DEBUG, "access({}, {})", path, mask);
#endif #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) { int Fuse::create(const bf::path &path, ::mode_t mode, fuse_file_info *fileinfo) {
ThreadNameForDebugging _threadName("create");
#ifdef FSPP_LOG #ifdef FSPP_LOG
LOG(DEBUG, "create({}, {}, _)", path, mode); LOG(DEBUG, "create({}, {}, _)", path, mode);
#endif #endif

View File

@ -13,7 +13,7 @@ using ::testing::Test;
//Test that Cache uses a move constructor for Value if possible //Test that Cache uses a move constructor for Value if possible
class CacheTest_MoveConstructor: public Test { class CacheTest_MoveConstructor: public Test {
public: 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; CopyableMovableValueType::numCopyConstructorCalled = 0;
} }
unique_ref<Cache<MinimalKeyType, CopyableMovableValueType, 100>> cache; unique_ref<Cache<MinimalKeyType, CopyableMovableValueType, 100>> cache;

View File

@ -36,7 +36,7 @@ private:
class CacheTest_RaceCondition: public ::testing::Test { class CacheTest_RaceCondition: public ::testing::Test {
public: public:
CacheTest_RaceCondition(): cache(), destructorStarted(), destructorFinished(false) {} CacheTest_RaceCondition(): cache("test"), destructorStarted(), destructorFinished(false) {}
static constexpr unsigned int MAX_ENTRIES = 100; static constexpr unsigned int MAX_ENTRIES = 100;

View File

@ -37,7 +37,7 @@ class PeriodicTaskTest: public Test {
}; };
TEST_F(PeriodicTaskTest, DoesntDeadlockInDestructorWhenDestructedImmediately) { TEST_F(PeriodicTaskTest, DoesntDeadlockInDestructorWhenDestructedImmediately) {
PeriodicTask task([](){}, 1); PeriodicTask task([](){}, 1, "test");
} }
TEST_F(PeriodicTaskTest, CallsCallbackAtLeast10Times) { TEST_F(PeriodicTaskTest, CallsCallbackAtLeast10Times) {
@ -45,7 +45,7 @@ TEST_F(PeriodicTaskTest, CallsCallbackAtLeast10Times) {
PeriodicTask task([&counter](){ PeriodicTask task([&counter](){
counter.decrease(); counter.decrease();
}, 0.001); }, 0.001, "test");
counter.waitForZero(); counter.waitForZero();
} }
@ -55,7 +55,7 @@ TEST_F(PeriodicTaskTest, DoesntCallCallbackAfterDestruction) {
{ {
PeriodicTask task([&callCount](){ PeriodicTask task([&callCount](){
callCount += 1; callCount += 1;
}, 0.001); }, 0.001, "test");
} }
int callCountDirectlyAfterDestruction = callCount; int callCountDirectlyAfterDestruction = callCount;
boost::this_thread::sleep_for(boost::chrono::seconds(1)); boost::this_thread::sleep_for(boost::chrono::seconds(1));

View File

@ -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). // 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 { class CacheTest: public ::testing::Test {
public: public:
CacheTest(): _cache() {} CacheTest(): _cache("test") {}
void push(int key, int value); void push(int key, int value);
boost::optional<int> pop(int key); boost::optional<int> pop(int key);

View File

@ -54,6 +54,7 @@ set(SOURCES
system/MemoryTest.cpp system/MemoryTest.cpp
system/HomedirTest.cpp system/HomedirTest.cpp
system/EnvTest.cpp system/EnvTest.cpp
thread/debugging_test.cpp
value_type/ValueTypeTest.cpp value_type/ValueTypeTest.cpp
) )

View 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();
}

View File

@ -15,7 +15,7 @@ public:
CallAfterTimeoutTest(): called(false) {} CallAfterTimeoutTest(): called(false) {}
unique_ref<CallAfterTimeout> callAfterTimeout(milliseconds timeout) { 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; std::atomic<bool> called;