mirror of
https://github.com/trapexit/mergerfs.git
synced 2026-10-26 10:51:52 -04:00
The old macro expanded to two statements (mutex_lock + DEFER unlock). In an unbraced if/else only the lock would be conditional while the DEFER always executed — a latent double-unlock / unbalanced-unlock bug. Add ScopedMutexLock to both debug and ndebug headers: - debug: stores __FILE__/__func__/__LINE__ from the call site and passes them to _mutex_lock/_mutex_unlock for diagnostics - ndebug: lightweight wrapper around mutex_lock/mutex_unlock Redefine mutex_lockguard to create a single uniquely-named ScopedMutexLock variable (via __COUNTER__), making it safe in any statement context. Remove the now-unused scope_guard include. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
159 lines
3.6 KiB
C++
159 lines
3.6 KiB
C++
#pragma once
|
|
|
|
#include "fmt/core.h"
|
|
|
|
#include <ctime>
|
|
#include <cstdlib>
|
|
|
|
#include <pthread.h>
|
|
|
|
#ifndef PTHREAD_MUTEX_ADAPTIVE_NP
|
|
# define PTHREAD_MUTEX_ADAPTIVE_NP PTHREAD_MUTEX_NORMAL
|
|
#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__)
|
|
#define mutex_unlock(M) _mutex_unlock((M),__FILE__,__func__,__LINE__)
|
|
|
|
|
|
static
|
|
inline
|
|
void
|
|
_print_error_and_abort(const char *func1_,
|
|
const int errno_,
|
|
const char *file_,
|
|
const char *func2_,
|
|
const int linenum_)
|
|
{
|
|
fmt::println(stderr,
|
|
"ERROR: {} returned {} ({}) - ({}:{}:{})",
|
|
func1_,
|
|
errno_,
|
|
strerror(errno_),
|
|
file_,
|
|
func2_,
|
|
linenum_);
|
|
std::abort();
|
|
}
|
|
|
|
static
|
|
inline
|
|
void
|
|
_mutex_init(pthread_mutex_t &mutex_,
|
|
const char *file_,
|
|
const char *func_,
|
|
const int linenum_)
|
|
{
|
|
int rv;
|
|
pthread_mutexattr_t attr;
|
|
|
|
pthread_mutexattr_init(&attr);
|
|
pthread_mutexattr_settype(&attr,PTHREAD_MUTEX_ADAPTIVE_NP);
|
|
|
|
rv = pthread_mutex_init(&mutex_,&attr);
|
|
if(rv != 0)
|
|
_print_error_and_abort(__func__,rv,file_,func_,linenum_);
|
|
|
|
pthread_mutexattr_destroy(&attr);
|
|
}
|
|
|
|
static
|
|
inline
|
|
void
|
|
_mutex_destroy(pthread_mutex_t &mutex_,
|
|
const char *file_,
|
|
const char *func_,
|
|
const int linenum_)
|
|
{
|
|
int rv;
|
|
|
|
rv = pthread_mutex_destroy(&mutex_);
|
|
if(rv != 0)
|
|
_print_error_and_abort(__func__,rv,file_,func_,linenum_);
|
|
}
|
|
|
|
static
|
|
inline
|
|
void
|
|
_mutex_lock(pthread_mutex_t &mutex_,
|
|
const char *file_,
|
|
const char *func_,
|
|
const int linenum_)
|
|
{
|
|
int rv;
|
|
int cnt = 0;
|
|
timespec timeout;
|
|
|
|
while(true)
|
|
{
|
|
clock_gettime(CLOCK_REALTIME,&timeout);
|
|
timeout.tv_nsec += 1000000 * 1; // 1ms
|
|
if(timeout.tv_nsec >= 1000000000)
|
|
{
|
|
timeout.tv_sec += 1;
|
|
timeout.tv_nsec -= 1000000000;
|
|
}
|
|
|
|
rv = pthread_mutex_timedlock(&mutex_,&timeout);
|
|
switch(rv)
|
|
{
|
|
case 0:
|
|
return;
|
|
case ETIMEDOUT:
|
|
cnt++;
|
|
fmt::println(stderr,
|
|
"NOTICE: pthread_mutex_timedlock expired - count={}; ({}:{}:{})",
|
|
cnt,
|
|
file_,
|
|
func_,
|
|
linenum_);
|
|
continue;
|
|
default:
|
|
_print_error_and_abort(__func__,rv,file_,func_,linenum_);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
static
|
|
inline
|
|
void
|
|
_mutex_unlock(pthread_mutex_t &mutex_,
|
|
const char *file_,
|
|
const char *func_,
|
|
const int linenum_)
|
|
{
|
|
int rv;
|
|
|
|
rv = pthread_mutex_unlock(&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:
|
|
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()
|
|
{
|
|
_mutex_unlock(_mutex,_file,_func,_line);
|
|
}
|
|
|
|
ScopedMutexLock(const ScopedMutexLock&) = delete;
|
|
ScopedMutexLock& operator=(const ScopedMutexLock&) = delete;
|
|
};
|