mirror of
https://github.com/trapexit/mergerfs.git
synced 2026-10-26 19:01:56 -04:00
libfuse: tighten scoped mutex locking
This commit is contained in:
parent
3ff9794fff
commit
fae31d68c2
11 changed files with 185 additions and 80 deletions
|
|
@ -80,7 +80,7 @@ fs::statvfs_cache(const std::string &path_,
|
|||
rv = 0;
|
||||
now = ::_get_time();
|
||||
|
||||
mutex_lock(g_cache_lock);
|
||||
mutex_lockguard(g_cache_lock);
|
||||
|
||||
e = &g_cache[path_];
|
||||
|
||||
|
|
@ -92,8 +92,6 @@ fs::statvfs_cache(const std::string &path_,
|
|||
|
||||
*st_ = e->st;
|
||||
|
||||
mutex_unlock(g_cache_lock);
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -67,7 +67,7 @@ _readdir(const fs::path &branch_path_,
|
|||
if(nread <= 0)
|
||||
return nread;
|
||||
|
||||
LockGuard lk(mutex_);
|
||||
mutex_lockguard(mutex_);
|
||||
for(ssize_t pos = 0; pos < nread;)
|
||||
{
|
||||
int rv;
|
||||
|
|
|
|||
|
|
@ -70,7 +70,7 @@ _readdir(const fs::path &branch_path_,
|
|||
fs::inode::ReaddirCalc inodecalc(branch_path_,rel_dirpath_);
|
||||
while(true)
|
||||
{
|
||||
LockGuard lk(mutex_);
|
||||
mutex_lockguard(mutex_);
|
||||
for(int i = 0; i < MAX_ENTRIES_PER_LOOP; i++)
|
||||
{
|
||||
int rv;
|
||||
|
|
|
|||
|
|
@ -6,8 +6,6 @@
|
|||
#include "mutex_ndebug.hpp"
|
||||
#endif
|
||||
|
||||
#include "scope_guard/scope_guard.hpp"
|
||||
|
||||
typedef pthread_mutex_t mutex_t;
|
||||
|
||||
// Simple mutex_t wrapper to provide RAII
|
||||
|
|
@ -29,33 +27,29 @@ public:
|
|||
mutex_destroy(_mutex);
|
||||
}
|
||||
|
||||
Mutex(const Mutex&) = delete;
|
||||
Mutex& operator=(const Mutex&) = delete;
|
||||
Mutex(Mutex&&) = delete;
|
||||
Mutex& operator=(Mutex&&) = delete;
|
||||
|
||||
operator mutex_t&()
|
||||
{
|
||||
return _mutex;
|
||||
}
|
||||
};
|
||||
|
||||
class LockGuard
|
||||
{
|
||||
private:
|
||||
mutex_t &_mutex;
|
||||
// RAII mutex guard macro. Expands to a single variable declaration,
|
||||
// so it is safe in unbraced if/else/for/while.
|
||||
// In debug builds the call-site __FILE__/__func__/__LINE__ are
|
||||
// forwarded to _mutex_lock/_mutex_unlock for diagnostics.
|
||||
#define MUTEX_LOCKGUARD_CONCAT_(a,b) a##b
|
||||
#define MUTEX_LOCKGUARD_CONCAT(a,b) MUTEX_LOCKGUARD_CONCAT_(a,b)
|
||||
|
||||
public:
|
||||
LockGuard(mutex_t &mutex_)
|
||||
: _mutex(mutex_)
|
||||
{
|
||||
mutex_lock(_mutex);
|
||||
}
|
||||
|
||||
~LockGuard()
|
||||
{
|
||||
mutex_unlock(_mutex);
|
||||
}
|
||||
};
|
||||
|
||||
// Single-line mutex guard using scope_guard
|
||||
// Expands to mutex_lock() followed by DEFER mutex_unlock()
|
||||
// Both lock and unlock capture __FILE__/__func__/__LINE__ from the call site
|
||||
#ifdef DEBUG
|
||||
#define mutex_lockguard(m) \
|
||||
mutex_lock(m); \
|
||||
DEFER { mutex_unlock(m); }
|
||||
ScopedMutexLock MUTEX_LOCKGUARD_CONCAT(_mtx_guard_,__COUNTER__) \
|
||||
((m),__FILE__,__func__,__LINE__)
|
||||
#else
|
||||
#define mutex_lockguard(m) \
|
||||
ScopedMutexLock MUTEX_LOCKGUARD_CONCAT(_mtx_guard_,__COUNTER__)(m)
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -2,8 +2,16 @@
|
|||
|
||||
#include "fmt/core.h"
|
||||
|
||||
#include <ctime>
|
||||
#include <cerrno>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <ctime>
|
||||
|
||||
#if defined(__has_include)
|
||||
# if __has_include(<features.h>)
|
||||
# include <features.h>
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#include <pthread.h>
|
||||
|
||||
|
|
@ -11,6 +19,12 @@
|
|||
# define PTHREAD_MUTEX_ADAPTIVE_NP PTHREAD_MUTEX_NORMAL
|
||||
#endif
|
||||
|
||||
#if defined(_GNU_SOURCE) && defined(__GLIBC_PREREQ)
|
||||
# if __GLIBC_PREREQ(2,30)
|
||||
# define MUTEX_HAS_PTHREAD_MUTEX_CLOCKLOCK 1
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#define mutex_init(M) _mutex_init((M),__FILE__,__func__,__LINE__)
|
||||
#define mutex_destroy(M) _mutex_destroy((M),__FILE__,__func__,__LINE__)
|
||||
#define mutex_lock(M) _mutex_lock((M),__FILE__,__func__,__LINE__)
|
||||
|
|
@ -48,14 +62,27 @@ _mutex_init(pthread_mutex_t &mutex_,
|
|||
int rv;
|
||||
pthread_mutexattr_t attr;
|
||||
|
||||
pthread_mutexattr_init(&attr);
|
||||
pthread_mutexattr_settype(&attr,PTHREAD_MUTEX_ADAPTIVE_NP);
|
||||
rv = pthread_mutexattr_init(&attr);
|
||||
if(rv != 0)
|
||||
_print_error_and_abort("pthread_mutexattr_init",rv,file_,func_,linenum_);
|
||||
|
||||
rv = pthread_mutexattr_settype(&attr,PTHREAD_MUTEX_ADAPTIVE_NP);
|
||||
if(rv != 0)
|
||||
{
|
||||
pthread_mutexattr_destroy(&attr);
|
||||
_print_error_and_abort("pthread_mutexattr_settype",rv,file_,func_,linenum_);
|
||||
}
|
||||
|
||||
rv = pthread_mutex_init(&mutex_,&attr);
|
||||
if(rv != 0)
|
||||
_print_error_and_abort(__func__,rv,file_,func_,linenum_);
|
||||
{
|
||||
pthread_mutexattr_destroy(&attr);
|
||||
_print_error_and_abort("pthread_mutex_init",rv,file_,func_,linenum_);
|
||||
}
|
||||
|
||||
pthread_mutexattr_destroy(&attr);
|
||||
rv = pthread_mutexattr_destroy(&attr);
|
||||
if(rv != 0)
|
||||
_print_error_and_abort("pthread_mutexattr_destroy",rv,file_,func_,linenum_);
|
||||
}
|
||||
|
||||
static
|
||||
|
|
@ -87,7 +114,14 @@ _mutex_lock(pthread_mutex_t &mutex_,
|
|||
|
||||
while(true)
|
||||
{
|
||||
clock_gettime(CLOCK_REALTIME,&timeout);
|
||||
#ifdef MUTEX_HAS_PTHREAD_MUTEX_CLOCKLOCK
|
||||
rv = clock_gettime(CLOCK_MONOTONIC,&timeout);
|
||||
#else
|
||||
rv = clock_gettime(CLOCK_REALTIME,&timeout);
|
||||
#endif
|
||||
if(rv != 0)
|
||||
_print_error_and_abort("clock_gettime",errno,file_,func_,linenum_);
|
||||
|
||||
timeout.tv_nsec += 1000000 * 1; // 1ms
|
||||
if(timeout.tv_nsec >= 1000000000)
|
||||
{
|
||||
|
|
@ -95,7 +129,11 @@ _mutex_lock(pthread_mutex_t &mutex_,
|
|||
timeout.tv_nsec -= 1000000000;
|
||||
}
|
||||
|
||||
#ifdef MUTEX_HAS_PTHREAD_MUTEX_CLOCKLOCK
|
||||
rv = pthread_mutex_clocklock(&mutex_,CLOCK_MONOTONIC,&timeout);
|
||||
#else
|
||||
rv = pthread_mutex_timedlock(&mutex_,&timeout);
|
||||
#endif
|
||||
switch(rv)
|
||||
{
|
||||
case 0:
|
||||
|
|
@ -103,7 +141,7 @@ _mutex_lock(pthread_mutex_t &mutex_,
|
|||
case ETIMEDOUT:
|
||||
cnt++;
|
||||
fmt::println(stderr,
|
||||
"NOTICE: pthread_mutex_timedlock expired - count={}; ({}:{}:{})",
|
||||
"NOTICE: mutex timed lock expired - count={}; ({}:{}:{})",
|
||||
cnt,
|
||||
file_,
|
||||
func_,
|
||||
|
|
@ -130,3 +168,31 @@ _mutex_unlock(pthread_mutex_t &mutex_,
|
|||
if(rv != 0)
|
||||
_print_error_and_abort(__func__,rv,file_,func_,linenum_);
|
||||
}
|
||||
|
||||
class ScopedMutexLock
|
||||
{
|
||||
private:
|
||||
pthread_mutex_t &_mutex;
|
||||
const char *_file;
|
||||
const char *_func;
|
||||
int _line;
|
||||
|
||||
public:
|
||||
explicit
|
||||
ScopedMutexLock(pthread_mutex_t &mutex_,
|
||||
const char *file_,
|
||||
const char *func_,
|
||||
int line_)
|
||||
: _mutex(mutex_), _file(file_), _func(func_), _line(line_)
|
||||
{
|
||||
_mutex_lock(_mutex,_file,_func,_line);
|
||||
}
|
||||
|
||||
~ScopedMutexLock() noexcept
|
||||
{
|
||||
_mutex_unlock(_mutex,_file,_func,_line);
|
||||
}
|
||||
|
||||
ScopedMutexLock(const ScopedMutexLock&) = delete;
|
||||
ScopedMutexLock& operator=(const ScopedMutexLock&) = delete;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
#include <cerrno>
|
||||
#include <cstdlib>
|
||||
|
||||
#include <pthread.h>
|
||||
|
|
@ -17,14 +18,27 @@ mutex_init(pthread_mutex_t &mutex_)
|
|||
int rv;
|
||||
pthread_mutexattr_t attr;
|
||||
|
||||
pthread_mutexattr_init(&attr);
|
||||
pthread_mutexattr_settype(&attr,PTHREAD_MUTEX_ADAPTIVE_NP);
|
||||
|
||||
rv = pthread_mutex_init(&mutex_,&attr);
|
||||
rv = pthread_mutexattr_init(&attr);
|
||||
if(rv != 0)
|
||||
std::abort();
|
||||
|
||||
pthread_mutexattr_destroy(&attr);
|
||||
rv = pthread_mutexattr_settype(&attr,PTHREAD_MUTEX_ADAPTIVE_NP);
|
||||
if(rv != 0)
|
||||
{
|
||||
pthread_mutexattr_destroy(&attr);
|
||||
std::abort();
|
||||
}
|
||||
|
||||
rv = pthread_mutex_init(&mutex_,&attr);
|
||||
if(rv != 0)
|
||||
{
|
||||
pthread_mutexattr_destroy(&attr);
|
||||
std::abort();
|
||||
}
|
||||
|
||||
rv = pthread_mutexattr_destroy(&attr);
|
||||
if(rv != 0)
|
||||
std::abort();
|
||||
}
|
||||
|
||||
static
|
||||
|
|
@ -62,3 +76,25 @@ mutex_destroy(pthread_mutex_t &mutex_)
|
|||
if(rv != 0)
|
||||
std::abort();
|
||||
}
|
||||
|
||||
class ScopedMutexLock
|
||||
{
|
||||
private:
|
||||
pthread_mutex_t &_mutex;
|
||||
|
||||
public:
|
||||
explicit
|
||||
ScopedMutexLock(pthread_mutex_t &mutex_)
|
||||
: _mutex(mutex_)
|
||||
{
|
||||
mutex_lock(_mutex);
|
||||
}
|
||||
|
||||
~ScopedMutexLock() noexcept
|
||||
{
|
||||
mutex_unlock(_mutex);
|
||||
}
|
||||
|
||||
ScopedMutexLock(const ScopedMutexLock&) = delete;
|
||||
ScopedMutexLock& operator=(const ScopedMutexLock&) = delete;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -81,7 +81,7 @@ private:
|
|||
}
|
||||
|
||||
private:
|
||||
mutex_t _mtx;
|
||||
Mutex _mtx;
|
||||
Node *_head = nullptr;
|
||||
std::atomic<size_t> _pool_count{0};
|
||||
Allocator _allocator;
|
||||
|
|
@ -124,7 +124,7 @@ private:
|
|||
Node*
|
||||
_pop_node(const size_t required_size_)
|
||||
{
|
||||
mutex_lock(_mtx);
|
||||
mutex_lockguard(_mtx);
|
||||
|
||||
Node **prev = &_head;
|
||||
while(*prev)
|
||||
|
|
@ -134,15 +134,12 @@ private:
|
|||
{
|
||||
*prev = node->next;
|
||||
_pool_count.fetch_sub(1,std::memory_order_relaxed);
|
||||
mutex_unlock(_mtx);
|
||||
return node;
|
||||
}
|
||||
|
||||
prev = &node->next;
|
||||
}
|
||||
|
||||
mutex_unlock(_mtx);
|
||||
|
||||
return nullptr;;
|
||||
}
|
||||
|
||||
|
|
@ -152,13 +149,11 @@ private:
|
|||
{
|
||||
node_->alloc_size = alloc_size_;
|
||||
|
||||
mutex_lock(_mtx);
|
||||
mutex_lockguard(_mtx);
|
||||
|
||||
node_->next = _head;
|
||||
_head = node_;
|
||||
_pool_count.fetch_add(1,std::memory_order_relaxed);
|
||||
|
||||
mutex_unlock(_mtx);
|
||||
}
|
||||
|
||||
public:
|
||||
|
|
@ -167,13 +162,13 @@ public:
|
|||
{
|
||||
Node *head;
|
||||
|
||||
mutex_lock(_mtx);
|
||||
{
|
||||
mutex_lockguard(_mtx);
|
||||
|
||||
head = _head;
|
||||
_head = nullptr;
|
||||
_pool_count.store(0,std::memory_order_relaxed);
|
||||
|
||||
mutex_unlock(_mtx);
|
||||
head = _head;
|
||||
_head = nullptr;
|
||||
_pool_count.store(0,std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
while(head)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -326,8 +326,8 @@ fuse_loop_mt(struct fuse *f_)
|
|||
if(f_ == NULL)
|
||||
return -1;
|
||||
|
||||
MaintenanceThread::setup();
|
||||
fuse_populate_maintenance_thread(f_);
|
||||
MaintenanceThread::setup();
|
||||
|
||||
res = fuse_session_loop_mt(fuse_get_session(),
|
||||
fuse_cfg.read_thread_count,
|
||||
|
|
|
|||
|
|
@ -1326,17 +1326,18 @@ do_notify_reply(fuse_req_t *req,
|
|||
struct fuse_notify_req *nreq;
|
||||
struct fuse_notify_req *head;
|
||||
|
||||
mutex_lock(f.lock);
|
||||
head = &f.notify_list;
|
||||
for(nreq = head->next; nreq != head; nreq = nreq->next)
|
||||
{
|
||||
if(nreq->unique == req->ctx.unique)
|
||||
{
|
||||
list_del_nreq(nreq);
|
||||
break;
|
||||
}
|
||||
}
|
||||
mutex_unlock(f.lock);
|
||||
{
|
||||
mutex_lockguard(f.lock);
|
||||
head = &f.notify_list;
|
||||
for(nreq = head->next; nreq != head; nreq = nreq->next)
|
||||
{
|
||||
if(nreq->unique == req->ctx.unique)
|
||||
{
|
||||
list_del_nreq(nreq);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(nreq != head)
|
||||
nreq->reply(nreq, req, hdr_->nodeid, &hdr_[1]);
|
||||
|
|
@ -1574,12 +1575,13 @@ fuse_lowlevel_notify_retrieve(struct fuse_session *se,
|
|||
if(rreq == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
mutex_lock(f.lock);
|
||||
rreq->cookie = cookie;
|
||||
rreq->nreq.unique = f.notify_ctr++;
|
||||
rreq->nreq.reply = fuse_ll_retrieve_reply;
|
||||
list_add_nreq(&rreq->nreq, &f.notify_list);
|
||||
mutex_unlock(f.lock);
|
||||
{
|
||||
mutex_lockguard(f.lock);
|
||||
rreq->cookie = cookie;
|
||||
rreq->nreq.unique = f.notify_ctr++;
|
||||
rreq->nreq.reply = fuse_ll_retrieve_reply;
|
||||
list_add_nreq(&rreq->nreq, &f.notify_list);
|
||||
}
|
||||
|
||||
outarg.notify_unique = rreq->nreq.unique;
|
||||
outarg.nodeid = ino;
|
||||
|
|
@ -1592,9 +1594,10 @@ fuse_lowlevel_notify_retrieve(struct fuse_session *se,
|
|||
err = send_notify_iov(se, FUSE_NOTIFY_RETRIEVE, iov, 2);
|
||||
if(err)
|
||||
{
|
||||
mutex_lock(f.lock);
|
||||
list_del_nreq(&rreq->nreq);
|
||||
mutex_unlock(f.lock);
|
||||
{
|
||||
mutex_lockguard(f.lock);
|
||||
list_del_nreq(&rreq->nreq);
|
||||
}
|
||||
free(rreq);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -14,6 +14,8 @@
|
|||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <new>
|
||||
|
||||
#include <unistd.h>
|
||||
#include <sys/uio.h>
|
||||
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@
|
|||
|
||||
static pthread_t g_thread;
|
||||
static std::vector<std::function<void(u64)>> g_funcs;
|
||||
static mutex_t g_mutex;
|
||||
static Mutex g_mutex;
|
||||
|
||||
static
|
||||
void*
|
||||
|
|
@ -25,8 +25,14 @@ _thread_loop(void *)
|
|||
{
|
||||
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE,NULL);
|
||||
{
|
||||
LockGuard lg(g_mutex);
|
||||
for(auto &func : g_funcs)
|
||||
std::vector<std::function<void(u64)>> funcs;
|
||||
|
||||
{
|
||||
mutex_lockguard(g_mutex);
|
||||
funcs = g_funcs;
|
||||
}
|
||||
|
||||
for(auto &func : funcs)
|
||||
func(count);
|
||||
}
|
||||
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE,NULL);
|
||||
|
|
@ -51,7 +57,7 @@ MaintenanceThread::setup()
|
|||
void
|
||||
MaintenanceThread::push_job(const std::function<void(u64)> &func_)
|
||||
{
|
||||
LockGuard lg(g_mutex);
|
||||
mutex_lockguard(g_mutex);
|
||||
|
||||
g_funcs.emplace_back(func_);
|
||||
}
|
||||
|
|
@ -61,4 +67,9 @@ MaintenanceThread::stop()
|
|||
{
|
||||
pthread_cancel(g_thread);
|
||||
pthread_join(g_thread,NULL);
|
||||
|
||||
{
|
||||
mutex_lockguard(g_mutex);
|
||||
g_funcs.clear();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue