mirror of
https://github.com/trapexit/mergerfs.git
synced 2025-02-17 01:32:44 +08:00
Rework thread pool queue depth impl
This commit is contained in:
parent
8635456818
commit
620cab2948
|
@ -63,14 +63,14 @@ int fuse_dirents_add_plus(fuse_dirents_t *d,
|
||||||
const uint64_t namelen,
|
const uint64_t namelen,
|
||||||
const fuse_entry_t *entry,
|
const fuse_entry_t *entry,
|
||||||
const struct stat *st);
|
const struct stat *st);
|
||||||
int fuse_dirents_add_linux(fuse_dirents_t *d,
|
int fuse_dirents_add_linux(fuse_dirents_t *d,
|
||||||
const struct linux_dirent64 *de,
|
const linux_dirent64_t *de,
|
||||||
const uint64_t namelen);
|
const uint64_t namelen);
|
||||||
int fuse_dirents_add_linux_plus(fuse_dirents_t *d,
|
int fuse_dirents_add_linux_plus(fuse_dirents_t *d,
|
||||||
const struct linux_dirent64 *de,
|
const linux_dirent64_t *de,
|
||||||
const uint64_t namelen,
|
const uint64_t namelen,
|
||||||
const fuse_entry_t *entry,
|
const fuse_entry_t *entry,
|
||||||
const struct stat *st);
|
const struct stat *st);
|
||||||
|
|
||||||
void *fuse_dirents_find(fuse_dirents_t *d,
|
void *fuse_dirents_find(fuse_dirents_t *d,
|
||||||
const uint64_t ino);
|
const uint64_t ino);
|
||||||
|
|
|
@ -2,7 +2,10 @@
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
struct linux_dirent64
|
#define DIRENT_NAMELEN(X) ((X)->reclen - offsetof(linux_dirent64_t,name))
|
||||||
|
|
||||||
|
typedef struct linux_dirent64_t linux_dirent64_t;
|
||||||
|
struct linux_dirent64_t
|
||||||
{
|
{
|
||||||
uint64_t ino;
|
uint64_t ino;
|
||||||
int64_t off;
|
int64_t off;
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
#include "moodycamel/blockingconcurrentqueue.h"
|
#include "moodycamel/blockingconcurrentqueue.h"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <csignal>
|
#include <csignal>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
@ -15,6 +16,7 @@
|
||||||
|
|
||||||
#include <syslog.h>
|
#include <syslog.h>
|
||||||
|
|
||||||
|
|
||||||
struct ThreadPoolTraits : public moodycamel::ConcurrentQueueDefaultTraits
|
struct ThreadPoolTraits : public moodycamel::ConcurrentQueueDefaultTraits
|
||||||
{
|
{
|
||||||
static const int MAX_SEMA_SPINS = 1;
|
static const int MAX_SEMA_SPINS = 1;
|
||||||
|
@ -29,17 +31,20 @@ private:
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit
|
explicit
|
||||||
ThreadPool(std::size_t const thread_count_ = std::thread::hardware_concurrency(),
|
ThreadPool(unsigned const thread_count_ = std::thread::hardware_concurrency(),
|
||||||
std::size_t const queue_depth_ = 1,
|
unsigned const max_queue_depth_ = std::thread::hardware_concurrency(),
|
||||||
std::string const name_ = {})
|
std::string const name_ = {})
|
||||||
: _queue(queue_depth_,thread_count_,thread_count_),
|
: _queue(),
|
||||||
|
_queue_depth(0),
|
||||||
|
_max_queue_depth(std::max(thread_count_,max_queue_depth_)),
|
||||||
_name(get_thread_name(name_))
|
_name(get_thread_name(name_))
|
||||||
{
|
{
|
||||||
syslog(LOG_DEBUG,
|
syslog(LOG_DEBUG,
|
||||||
"threadpool: spawning %zu threads of queue depth %zu named '%s'",
|
"threadpool (%s): spawning %u threads w/ max queue depth %u%s",
|
||||||
|
_name.c_str(),
|
||||||
thread_count_,
|
thread_count_,
|
||||||
queue_depth_,
|
_max_queue_depth,
|
||||||
_name.c_str());
|
((_max_queue_depth != max_queue_depth_) ? " (adjusted)" : ""));
|
||||||
|
|
||||||
sigset_t oldset;
|
sigset_t oldset;
|
||||||
sigset_t newset;
|
sigset_t newset;
|
||||||
|
@ -57,7 +62,8 @@ public:
|
||||||
if(rv != 0)
|
if(rv != 0)
|
||||||
{
|
{
|
||||||
syslog(LOG_WARNING,
|
syslog(LOG_WARNING,
|
||||||
"threadpool: error spawning thread - %d (%s)",
|
"threadpool (%s): error spawning thread - %d (%s)",
|
||||||
|
_name.c_str(),
|
||||||
rv,
|
rv,
|
||||||
strerror(rv));
|
strerror(rv));
|
||||||
continue;
|
continue;
|
||||||
|
@ -78,9 +84,9 @@ public:
|
||||||
~ThreadPool()
|
~ThreadPool()
|
||||||
{
|
{
|
||||||
syslog(LOG_DEBUG,
|
syslog(LOG_DEBUG,
|
||||||
"threadpool: destroying %zu threads named '%s'",
|
"threadpool (%s): destroying %lu threads",
|
||||||
_threads.size(),
|
_name.c_str(),
|
||||||
_name.c_str());
|
_threads.size());
|
||||||
|
|
||||||
auto func = []() { pthread_exit(NULL); };
|
auto func = []() { pthread_exit(NULL); };
|
||||||
for(std::size_t i = 0; i < _threads.size(); i++)
|
for(std::size_t i = 0; i < _threads.size(); i++)
|
||||||
|
@ -114,6 +120,7 @@ private:
|
||||||
ThreadPool *btp = static_cast<ThreadPool*>(arg_);
|
ThreadPool *btp = static_cast<ThreadPool*>(arg_);
|
||||||
ThreadPool::Func func;
|
ThreadPool::Func func;
|
||||||
ThreadPool::Queue &q = btp->_queue;
|
ThreadPool::Queue &q = btp->_queue;
|
||||||
|
std::atomic<unsigned> &queue_depth = btp->_queue_depth;
|
||||||
moodycamel::ConsumerToken ctok(btp->_queue);
|
moodycamel::ConsumerToken ctok(btp->_queue);
|
||||||
|
|
||||||
while(true)
|
while(true)
|
||||||
|
@ -121,6 +128,8 @@ private:
|
||||||
q.wait_dequeue(ctok,func);
|
q.wait_dequeue(ctok,func);
|
||||||
|
|
||||||
func();
|
func();
|
||||||
|
|
||||||
|
queue_depth.fetch_sub(1,std::memory_order_release);
|
||||||
}
|
}
|
||||||
|
|
||||||
return NULL;
|
return NULL;
|
||||||
|
@ -146,7 +155,8 @@ public:
|
||||||
if(rv != 0)
|
if(rv != 0)
|
||||||
{
|
{
|
||||||
syslog(LOG_WARNING,
|
syslog(LOG_WARNING,
|
||||||
"threadpool: error spawning thread - %d (%s)",
|
"threadpool (%s): error spawning thread - %d (%s)",
|
||||||
|
_name.c_str(),
|
||||||
rv,
|
rv,
|
||||||
strerror(rv));
|
strerror(rv));
|
||||||
return -rv;
|
return -rv;
|
||||||
|
@ -161,7 +171,7 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
syslog(LOG_DEBUG,
|
syslog(LOG_DEBUG,
|
||||||
"threadpool: 1 thread added to pool '%s' named '%s'",
|
"threadpool (%s): 1 thread added named '%s'",
|
||||||
_name.c_str(),
|
_name.c_str(),
|
||||||
name.c_str());
|
name.c_str());
|
||||||
|
|
||||||
|
@ -201,7 +211,7 @@ public:
|
||||||
char name[16];
|
char name[16];
|
||||||
pthread_getname_np(t,name,sizeof(name));
|
pthread_getname_np(t,name,sizeof(name));
|
||||||
syslog(LOG_DEBUG,
|
syslog(LOG_DEBUG,
|
||||||
"threadpool: 1 thread removed from pool '%s' named '%s'",
|
"threadpool (%s): 1 thread removed named '%s'",
|
||||||
_name.c_str(),
|
_name.c_str(),
|
||||||
name);
|
name);
|
||||||
|
|
||||||
|
@ -238,28 +248,32 @@ public:
|
||||||
enqueue_work(moodycamel::ProducerToken &ptok_,
|
enqueue_work(moodycamel::ProducerToken &ptok_,
|
||||||
FuncType &&f_)
|
FuncType &&f_)
|
||||||
{
|
{
|
||||||
timespec ts = {0,10};
|
timespec ts = {0,1000};
|
||||||
while(true)
|
for(unsigned i = 0; i < 1000000; i++)
|
||||||
{
|
{
|
||||||
if(_queue.try_enqueue(ptok_,f_))
|
if(_queue_depth.load(std::memory_order_acquire) < _max_queue_depth)
|
||||||
return;
|
break;
|
||||||
::nanosleep(&ts,NULL);
|
::nanosleep(&ts,NULL);
|
||||||
ts.tv_nsec += 10;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_queue.enqueue(ptok_,f_);
|
||||||
|
_queue_depth.fetch_add(1,std::memory_order_release);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename FuncType>
|
template<typename FuncType>
|
||||||
void
|
void
|
||||||
enqueue_work(FuncType &&f_)
|
enqueue_work(FuncType &&f_)
|
||||||
{
|
{
|
||||||
timespec ts = {0,10};
|
timespec ts = {0,1000};
|
||||||
while(true)
|
for(unsigned i = 0; i < 1000000; i++)
|
||||||
{
|
{
|
||||||
if(_queue.try_enqueue(f_))
|
if(_queue_depth.load(std::memory_order_acquire) < _max_queue_depth)
|
||||||
return;
|
break;
|
||||||
::nanosleep(&ts,NULL);
|
::nanosleep(&ts,NULL);
|
||||||
ts.tv_nsec += 10;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_queue.enqueue(f_);
|
||||||
|
_queue_depth.fetch_add(1,std::memory_order_release);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename FuncType>
|
template<typename FuncType>
|
||||||
|
@ -272,21 +286,24 @@ public:
|
||||||
|
|
||||||
auto promise = std::make_shared<Promise>();
|
auto promise = std::make_shared<Promise>();
|
||||||
auto future = promise->get_future();
|
auto future = promise->get_future();
|
||||||
auto work = [=]()
|
|
||||||
|
auto work = [=]()
|
||||||
{
|
{
|
||||||
auto rv = f_();
|
auto rv = f_();
|
||||||
promise->set_value(rv);
|
promise->set_value(rv);
|
||||||
};
|
};
|
||||||
|
|
||||||
timespec ts = {0,10};
|
timespec ts = {0,1000};
|
||||||
while(true)
|
for(unsigned i = 0; i < 1000000; i++)
|
||||||
{
|
{
|
||||||
if(_queue.try_enqueue(work))
|
if(_queue_depth.load(std::memory_order_acquire) < _max_queue_depth)
|
||||||
break;
|
break;
|
||||||
::nanosleep(&ts,NULL);
|
::nanosleep(&ts,NULL);
|
||||||
ts.tv_nsec += 10;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_queue.enqueue(work);
|
||||||
|
_queue_depth.fetch_add(1,std::memory_order_release);
|
||||||
|
|
||||||
return future;
|
return future;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -307,6 +324,8 @@ public:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Queue _queue;
|
Queue _queue;
|
||||||
|
std::atomic<unsigned> _queue_depth;
|
||||||
|
unsigned const _max_queue_depth;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::string const _name;
|
std::string const _name;
|
||||||
|
|
|
@ -304,9 +304,9 @@ fuse_dirents_add_plus(fuse_dirents_t *d_,
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
fuse_dirents_add_linux(fuse_dirents_t *d_,
|
fuse_dirents_add_linux(fuse_dirents_t *d_,
|
||||||
const struct linux_dirent64 *dirent_,
|
const linux_dirent64_t *dirent_,
|
||||||
const uint64_t namelen_)
|
const uint64_t namelen_)
|
||||||
{
|
{
|
||||||
fuse_dirent_t *d;
|
fuse_dirent_t *d;
|
||||||
|
|
||||||
|
@ -336,11 +336,11 @@ fuse_dirents_add_linux(fuse_dirents_t *d_,
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
fuse_dirents_add_linux_plus(fuse_dirents_t *d_,
|
fuse_dirents_add_linux_plus(fuse_dirents_t *d_,
|
||||||
const struct linux_dirent64 *dirent_,
|
const linux_dirent64_t *dirent_,
|
||||||
const uint64_t namelen_,
|
const uint64_t namelen_,
|
||||||
const fuse_entry_t *entry_,
|
const fuse_entry_t *entry_,
|
||||||
const struct stat *st_)
|
const struct stat *st_)
|
||||||
{
|
{
|
||||||
fuse_direntplus_t *d;
|
fuse_direntplus_t *d;
|
||||||
|
|
||||||
|
|
|
@ -490,10 +490,13 @@ fuse_session_loop_mt(struct fuse_session *se_,
|
||||||
|
|
||||||
if(process_thread_count > 0)
|
if(process_thread_count > 0)
|
||||||
process_tp = std::make_shared<ThreadPool>(process_thread_count,
|
process_tp = std::make_shared<ThreadPool>(process_thread_count,
|
||||||
process_thread_queue_depth,
|
(process_thread_count *
|
||||||
|
process_thread_queue_depth),
|
||||||
"fuse.process");
|
"fuse.process");
|
||||||
|
|
||||||
read_tp = std::make_unique<ThreadPool>(read_thread_count,1,"fuse.read");
|
read_tp = std::make_unique<ThreadPool>(read_thread_count,
|
||||||
|
read_thread_count,
|
||||||
|
"fuse.read");
|
||||||
if(process_tp)
|
if(process_tp)
|
||||||
{
|
{
|
||||||
for(auto i = 0; i < read_thread_count; i++)
|
for(auto i = 0; i < read_thread_count; i++)
|
||||||
|
|
|
@ -19,20 +19,24 @@
|
||||||
#include "config.hpp"
|
#include "config.hpp"
|
||||||
#include "dirinfo.hpp"
|
#include "dirinfo.hpp"
|
||||||
#include "errno.hpp"
|
#include "errno.hpp"
|
||||||
#include "fs_closedir.hpp"
|
#include "fs_close.hpp"
|
||||||
#include "fs_devid.hpp"
|
#include "fs_devid.hpp"
|
||||||
|
#include "fs_getdents64.hpp"
|
||||||
#include "fs_inode.hpp"
|
#include "fs_inode.hpp"
|
||||||
#include "fs_opendir.hpp"
|
#include "fs_open.hpp"
|
||||||
#include "fs_path.hpp"
|
#include "fs_path.hpp"
|
||||||
#include "fs_readdir.hpp"
|
#include "fs_readdir.hpp"
|
||||||
#include "hashset.hpp"
|
#include "hashset.hpp"
|
||||||
|
#include "scope_guard.hpp"
|
||||||
#include "ugid.hpp"
|
#include "ugid.hpp"
|
||||||
|
|
||||||
#include "fuse_dirents.h"
|
#include "fuse_dirents.h"
|
||||||
|
#include "linux_dirent64.h"
|
||||||
|
|
||||||
|
|
||||||
FUSE::ReadDirCOR::ReadDirCOR(unsigned concurrency_)
|
FUSE::ReadDirCOR::ReadDirCOR(unsigned concurrency_,
|
||||||
: _tp(concurrency_,concurrency_,"readdir.cor")
|
unsigned max_queue_depth_)
|
||||||
|
: _tp(concurrency_,max_queue_depth_,"readdir.cor")
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -44,79 +48,98 @@ FUSE::ReadDirCOR::~ReadDirCOR()
|
||||||
|
|
||||||
namespace l
|
namespace l
|
||||||
{
|
{
|
||||||
static
|
struct Error
|
||||||
inline
|
|
||||||
uint64_t
|
|
||||||
dirent_exact_namelen(const struct dirent *d_)
|
|
||||||
{
|
{
|
||||||
#ifdef _D_EXACT_NAMLEN
|
private:
|
||||||
return _D_EXACT_NAMLEN(d_);
|
int _err;
|
||||||
#elif defined _DIRENT_HAVE_D_NAMLEN
|
|
||||||
return d_->d_namlen;
|
public:
|
||||||
#else
|
Error()
|
||||||
return strlen(d_->d_name);
|
: _err(ENOENT)
|
||||||
#endif
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
operator int()
|
||||||
|
{
|
||||||
|
return _err;
|
||||||
|
}
|
||||||
|
|
||||||
|
Error&
|
||||||
|
operator=(int v_)
|
||||||
|
{
|
||||||
|
if(_err != 0)
|
||||||
|
_err = v_;
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
static
|
static
|
||||||
inline
|
inline
|
||||||
int
|
int
|
||||||
readdir(std::string basepath_,
|
readdir(std::string basepath_,
|
||||||
HashSet &names_,
|
HashSet &names_,
|
||||||
std::mutex &names_mutex_,
|
|
||||||
fuse_dirents_t *buf_,
|
fuse_dirents_t *buf_,
|
||||||
std::mutex &dirents_mutex_)
|
std::mutex &mutex_)
|
||||||
{
|
{
|
||||||
int rv;
|
int rv;
|
||||||
int err;
|
int dfd;
|
||||||
DIR *dh;
|
|
||||||
dev_t dev;
|
dev_t dev;
|
||||||
std::string filepath;
|
std::string filepath;
|
||||||
|
|
||||||
dh = fs::opendir(basepath_);
|
dfd = fs::open_dir_ro(basepath_);
|
||||||
if(dh == NULL)
|
if(dfd == -1)
|
||||||
return -errno;
|
return errno;
|
||||||
|
|
||||||
dev = fs::devid(dh);
|
DEFER{ fs::close(dfd); };
|
||||||
|
|
||||||
|
dev = fs::devid(dfd);
|
||||||
|
|
||||||
rv = 0;
|
rv = 0;
|
||||||
err = 0;
|
for(;;)
|
||||||
for(struct dirent *de = fs::readdir(dh); de && !rv; de = fs::readdir(dh))
|
|
||||||
{
|
{
|
||||||
std::uint64_t namelen;
|
long nread;
|
||||||
|
char buf[32 * 1024];
|
||||||
|
|
||||||
namelen = l::dirent_exact_namelen(de);
|
nread = fs::getdents_64(dfd,buf,sizeof(buf));
|
||||||
|
if(nread == -1)
|
||||||
|
return errno;
|
||||||
|
if(nread == 0)
|
||||||
|
break;
|
||||||
|
|
||||||
{
|
linux_dirent64_t *d;
|
||||||
std::lock_guard<std::mutex> lk(names_mutex_);
|
std::lock_guard<std::mutex> lk(mutex_);
|
||||||
rv = names_.put(de->d_name,namelen);
|
for(long pos = 0; pos < nread; pos += d->reclen)
|
||||||
if(rv == 0)
|
{
|
||||||
continue;
|
std::uint64_t namelen;
|
||||||
}
|
|
||||||
|
|
||||||
filepath = fs::path::make(basepath_,de->d_name);
|
d = (linux_dirent64_t*)&buf[pos];
|
||||||
de->d_ino = fs::inode::calc(filepath,
|
|
||||||
DTTOIF(de->d_type),
|
|
||||||
dev,
|
|
||||||
de->d_ino);
|
|
||||||
|
|
||||||
{
|
namelen = DIRENT_NAMELEN(d);
|
||||||
std::lock_guard<std::mutex> lk(dirents_mutex_);
|
|
||||||
rv = fuse_dirents_add(buf_,de,namelen);
|
|
||||||
if(rv == 0)
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
err = -ENOMEM;
|
rv = names_.put(d->name,namelen);
|
||||||
|
if(rv == 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
filepath = fs::path::make(basepath_,d->name);
|
||||||
|
d->ino = fs::inode::calc(filepath,
|
||||||
|
DTTOIF(d->type),
|
||||||
|
dev,
|
||||||
|
d->ino);
|
||||||
|
|
||||||
|
rv = fuse_dirents_add_linux(buf_,d,namelen);
|
||||||
|
if(rv >= 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
return ENOMEM;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fs::closedir(dh);
|
return 0;
|
||||||
|
|
||||||
return err;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static
|
static
|
||||||
std::vector<int>
|
int
|
||||||
concurrent_readdir(ThreadPool &tp_,
|
concurrent_readdir(ThreadPool &tp_,
|
||||||
const Branches::CPtr &branches_,
|
const Branches::CPtr &branches_,
|
||||||
const char *dirname_,
|
const char *dirname_,
|
||||||
|
@ -125,11 +148,10 @@ namespace l
|
||||||
gid_t const gid_)
|
gid_t const gid_)
|
||||||
{
|
{
|
||||||
HashSet names;
|
HashSet names;
|
||||||
std::mutex names_mutex;
|
std::mutex mutex;
|
||||||
std::mutex dirents_mutex;
|
|
||||||
std::vector<int> rv;
|
|
||||||
std::vector<std::future<int>> futures;
|
std::vector<std::future<int>> futures;
|
||||||
|
|
||||||
|
futures.reserve(branches_->size());
|
||||||
for(auto const &branch : *branches_)
|
for(auto const &branch : *branches_)
|
||||||
{
|
{
|
||||||
auto func = [&,dirname_,buf_,uid_,gid_]()
|
auto func = [&,dirname_,buf_,uid_,gid_]()
|
||||||
|
@ -139,7 +161,7 @@ namespace l
|
||||||
|
|
||||||
basepath = fs::path::make(branch.path,dirname_);
|
basepath = fs::path::make(branch.path,dirname_);
|
||||||
|
|
||||||
return l::readdir(basepath,names,names_mutex,buf_,dirents_mutex);
|
return l::readdir(basepath,names,buf_,mutex);
|
||||||
};
|
};
|
||||||
|
|
||||||
auto rv = tp_.enqueue_task(func);
|
auto rv = tp_.enqueue_task(func);
|
||||||
|
@ -147,26 +169,11 @@ namespace l
|
||||||
futures.emplace_back(std::move(rv));
|
futures.emplace_back(std::move(rv));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Error error;
|
||||||
for(auto &future : futures)
|
for(auto &future : futures)
|
||||||
rv.push_back(future.get());
|
error = future.get();
|
||||||
|
|
||||||
return rv;
|
return -error;
|
||||||
}
|
|
||||||
|
|
||||||
static
|
|
||||||
int
|
|
||||||
calc_rv(std::vector<int> rvs_)
|
|
||||||
{
|
|
||||||
for(auto rv : rvs_)
|
|
||||||
{
|
|
||||||
if(rv == 0)
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(rvs_.empty())
|
|
||||||
return -ENOENT;
|
|
||||||
|
|
||||||
return rvs_[0];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static
|
static
|
||||||
|
@ -178,13 +185,9 @@ namespace l
|
||||||
uid_t const uid_,
|
uid_t const uid_,
|
||||||
gid_t const gid_)
|
gid_t const gid_)
|
||||||
{
|
{
|
||||||
std::vector<int> rvs;
|
|
||||||
|
|
||||||
fuse_dirents_reset(buf_);
|
fuse_dirents_reset(buf_);
|
||||||
|
|
||||||
rvs = l::concurrent_readdir(tp_,branches_,dirname_,buf_,uid_,gid_);
|
return l::concurrent_readdir(tp_,branches_,dirname_,buf_,uid_,gid_);
|
||||||
|
|
||||||
return l::calc_rv(rvs);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -29,7 +29,8 @@ namespace FUSE
|
||||||
class ReadDirCOR final : public FUSE::ReadDirBase
|
class ReadDirCOR final : public FUSE::ReadDirBase
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
ReadDirCOR(unsigned concurrency);
|
ReadDirCOR(unsigned concurrency,
|
||||||
|
unsigned max_queue_depth);
|
||||||
~ReadDirCOR();
|
~ReadDirCOR();
|
||||||
|
|
||||||
int operator()(fuse_file_info_t const *ffi,
|
int operator()(fuse_file_info_t const *ffi,
|
||||||
|
|
|
@ -28,13 +28,15 @@
|
||||||
#include "fs_readdir.hpp"
|
#include "fs_readdir.hpp"
|
||||||
#include "fs_stat.hpp"
|
#include "fs_stat.hpp"
|
||||||
#include "hashset.hpp"
|
#include "hashset.hpp"
|
||||||
|
#include "scope_guard.hpp"
|
||||||
#include "ugid.hpp"
|
#include "ugid.hpp"
|
||||||
|
|
||||||
#include "fuse_dirents.h"
|
#include "fuse_dirents.h"
|
||||||
|
|
||||||
|
|
||||||
FUSE::ReadDirCOSR::ReadDirCOSR(unsigned concurrency_)
|
FUSE::ReadDirCOSR::ReadDirCOSR(unsigned concurrency_,
|
||||||
: _tp(concurrency_,concurrency_,"readdir.cosr")
|
unsigned max_queue_depth_)
|
||||||
|
: _tp(concurrency_,max_queue_depth_,"readdir.cosr")
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -46,8 +48,40 @@ FUSE::ReadDirCOSR::~ReadDirCOSR()
|
||||||
|
|
||||||
namespace l
|
namespace l
|
||||||
{
|
{
|
||||||
|
struct DirRV
|
||||||
|
{
|
||||||
|
DIR *dir;
|
||||||
|
int err;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Error
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
int _err;
|
||||||
|
|
||||||
|
public:
|
||||||
|
Error()
|
||||||
|
: _err(ENOENT)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
operator int()
|
||||||
|
{
|
||||||
|
return _err;
|
||||||
|
}
|
||||||
|
|
||||||
|
Error&
|
||||||
|
operator=(int const v_)
|
||||||
|
{
|
||||||
|
if(_err != 0)
|
||||||
|
_err = v_;
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
static
|
static
|
||||||
inline
|
|
||||||
uint64_t
|
uint64_t
|
||||||
dirent_exact_namelen(const struct dirent *d_)
|
dirent_exact_namelen(const struct dirent *d_)
|
||||||
{
|
{
|
||||||
|
@ -62,25 +96,31 @@ namespace l
|
||||||
|
|
||||||
static
|
static
|
||||||
inline
|
inline
|
||||||
std::vector<std::future<DIR*>>
|
std::vector<std::future<DirRV>>
|
||||||
opendir(ThreadPool &tp_,
|
opendir(ThreadPool &tp_,
|
||||||
const Branches::CPtr &branches_,
|
const Branches::CPtr &branches_,
|
||||||
char const *dirname_,
|
char const *dirname_,
|
||||||
uid_t const uid_,
|
uid_t const uid_,
|
||||||
gid_t const gid_)
|
gid_t const gid_)
|
||||||
{
|
{
|
||||||
std::vector<std::future<DIR*>> futures;
|
std::vector<std::future<DirRV>> futures;
|
||||||
|
|
||||||
|
futures.reserve(branches_->size());
|
||||||
for(auto const &branch : *branches_)
|
for(auto const &branch : *branches_)
|
||||||
{
|
{
|
||||||
auto func = [&branch,dirname_,uid_,gid_]()
|
auto func = [&branch,dirname_,uid_,gid_]()
|
||||||
{
|
{
|
||||||
|
DirRV rv;
|
||||||
std::string basepath;
|
std::string basepath;
|
||||||
ugid::Set const ugid(uid_,gid_);
|
ugid::Set const ugid(uid_,gid_);
|
||||||
|
|
||||||
basepath = fs::path::make(branch.path,dirname_);
|
basepath = fs::path::make(branch.path,dirname_);
|
||||||
|
|
||||||
return fs::opendir(basepath);
|
errno = 0;
|
||||||
|
rv.dir = fs::opendir(basepath);
|
||||||
|
rv.err = errno;
|
||||||
|
|
||||||
|
return rv;
|
||||||
};
|
};
|
||||||
|
|
||||||
auto rv = tp_.enqueue_task(func);
|
auto rv = tp_.enqueue_task(func);
|
||||||
|
@ -94,29 +134,31 @@ namespace l
|
||||||
static
|
static
|
||||||
inline
|
inline
|
||||||
int
|
int
|
||||||
readdir(std::vector<std::future<DIR*>> &dh_futures_,
|
readdir(std::vector<std::future<DirRV>> &dh_futures_,
|
||||||
char const *dirname_,
|
char const *dirname_,
|
||||||
fuse_dirents_t *buf_)
|
fuse_dirents_t *buf_)
|
||||||
{
|
{
|
||||||
int err;
|
Error error;
|
||||||
HashSet names;
|
HashSet names;
|
||||||
std::string fullpath;
|
std::string fullpath;
|
||||||
|
|
||||||
err = 0;
|
|
||||||
for(auto &dh_future : dh_futures_)
|
for(auto &dh_future : dh_futures_)
|
||||||
{
|
{
|
||||||
int rv;
|
int rv;
|
||||||
DIR *dh;
|
|
||||||
dev_t dev;
|
dev_t dev;
|
||||||
|
DirRV dirrv;
|
||||||
|
|
||||||
dh = dh_future.get();
|
dirrv = dh_future.get();
|
||||||
if(dh == NULL)
|
error = dirrv.err;
|
||||||
|
if(dirrv.dir == NULL)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
dev = fs::devid(dh);
|
DEFER { fs::closedir(dirrv.dir); };
|
||||||
|
|
||||||
|
dev = fs::devid(dirrv.dir);
|
||||||
|
|
||||||
rv = 0;
|
rv = 0;
|
||||||
for(struct dirent *de = fs::readdir(dh); de && !rv; de = fs::readdir(dh))
|
for(dirent *de = fs::readdir(dirrv.dir); de && !rv; de = fs::readdir(dirrv.dir))
|
||||||
{
|
{
|
||||||
std::uint64_t namelen;
|
std::uint64_t namelen;
|
||||||
|
|
||||||
|
@ -136,13 +178,11 @@ namespace l
|
||||||
if(rv == 0)
|
if(rv == 0)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
err = -ENOMEM;
|
error = ENOMEM;
|
||||||
}
|
}
|
||||||
|
|
||||||
fs::closedir(dh);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return err;
|
return -error;
|
||||||
}
|
}
|
||||||
|
|
||||||
static
|
static
|
||||||
|
@ -156,12 +196,12 @@ namespace l
|
||||||
gid_t const gid_)
|
gid_t const gid_)
|
||||||
{
|
{
|
||||||
int rv;
|
int rv;
|
||||||
std::vector<std::future<DIR*>> dh_futures;
|
std::vector<std::future<DirRV>> futures;
|
||||||
|
|
||||||
fuse_dirents_reset(buf_);
|
fuse_dirents_reset(buf_);
|
||||||
|
|
||||||
dh_futures = l::opendir(tp_,branches_,dirname_,uid_,gid_);
|
futures = l::opendir(tp_,branches_,dirname_,uid_,gid_);
|
||||||
rv = l::readdir(dh_futures,dirname_,buf_);
|
rv = l::readdir(futures,dirname_,buf_);
|
||||||
|
|
||||||
return rv;
|
return rv;
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,7 +28,8 @@ namespace FUSE
|
||||||
class ReadDirCOSR final : public FUSE::ReadDirBase
|
class ReadDirCOSR final : public FUSE::ReadDirBase
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
ReadDirCOSR(unsigned concurrency);
|
ReadDirCOSR(unsigned concurrency,
|
||||||
|
unsigned max_queue_depth);
|
||||||
~ReadDirCOSR();
|
~ReadDirCOSR();
|
||||||
|
|
||||||
int operator()(fuse_file_info_t const *ffi,
|
int operator()(fuse_file_info_t const *ffi,
|
||||||
|
|
|
@ -28,6 +28,7 @@
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <set>
|
#include <set>
|
||||||
|
|
||||||
|
#define DEFAULT_MAX_QUEUE_DEPTH 3
|
||||||
|
|
||||||
namespace l
|
namespace l
|
||||||
{
|
{
|
||||||
|
@ -35,13 +36,20 @@ namespace l
|
||||||
void
|
void
|
||||||
read_cfg(std::string const str_,
|
read_cfg(std::string const str_,
|
||||||
std::string &type_,
|
std::string &type_,
|
||||||
int &concurrency_)
|
unsigned &concurrency_,
|
||||||
|
unsigned &max_queue_depth_)
|
||||||
{
|
{
|
||||||
char type[16];
|
char type[16];
|
||||||
int concurrency;
|
int concurrency;
|
||||||
|
int max_queue_depth;
|
||||||
|
|
||||||
concurrency = 0;
|
concurrency = 0;
|
||||||
std::sscanf(str_.c_str(),"%15[a-z]:%d",type,&concurrency);
|
max_queue_depth = 0;
|
||||||
|
std::sscanf(str_.c_str(),
|
||||||
|
"%15[a-z]:%d:%d",
|
||||||
|
type,
|
||||||
|
&concurrency,
|
||||||
|
&max_queue_depth);
|
||||||
|
|
||||||
if(concurrency == 0)
|
if(concurrency == 0)
|
||||||
concurrency = std::thread::hardware_concurrency();
|
concurrency = std::thread::hardware_concurrency();
|
||||||
|
@ -51,22 +59,29 @@ namespace l
|
||||||
if(concurrency == 0)
|
if(concurrency == 0)
|
||||||
concurrency = 1;
|
concurrency = 1;
|
||||||
|
|
||||||
type_ = type;
|
if(max_queue_depth == 0)
|
||||||
concurrency_ = concurrency;
|
max_queue_depth = DEFAULT_MAX_QUEUE_DEPTH;
|
||||||
|
|
||||||
|
max_queue_depth *= concurrency;
|
||||||
|
|
||||||
|
type_ = type;
|
||||||
|
concurrency_ = concurrency;
|
||||||
|
max_queue_depth_ = max_queue_depth;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
FUSE::ReadDirFactory::valid(std::string const str_)
|
FUSE::ReadDirFactory::valid(std::string const str_)
|
||||||
{
|
{
|
||||||
int concurrency;
|
unsigned concurrency;
|
||||||
|
unsigned max_queue_depth;
|
||||||
std::string type;
|
std::string type;
|
||||||
static const std::set<std::string> types =
|
static const std::set<std::string> types =
|
||||||
{
|
{
|
||||||
"seq", "cosr", "cor"
|
"seq", "cosr", "cor"
|
||||||
};
|
};
|
||||||
|
|
||||||
l::read_cfg(str_,type,concurrency);
|
l::read_cfg(str_,type,concurrency,max_queue_depth);
|
||||||
|
|
||||||
if(types.find(type) == types.end())
|
if(types.find(type) == types.end())
|
||||||
return false;
|
return false;
|
||||||
|
@ -79,20 +94,21 @@ FUSE::ReadDirFactory::valid(std::string const str_)
|
||||||
std::shared_ptr<FUSE::ReadDirBase>
|
std::shared_ptr<FUSE::ReadDirBase>
|
||||||
FUSE::ReadDirFactory::make(std::string const str_)
|
FUSE::ReadDirFactory::make(std::string const str_)
|
||||||
{
|
{
|
||||||
int concurrency;
|
unsigned concurrency;
|
||||||
|
unsigned max_queue_depth;
|
||||||
std::string type;
|
std::string type;
|
||||||
|
|
||||||
if(!valid(str_))
|
if(!valid(str_))
|
||||||
return {};
|
return {};
|
||||||
|
|
||||||
l::read_cfg(str_,type,concurrency);
|
l::read_cfg(str_,type,concurrency,max_queue_depth);
|
||||||
|
|
||||||
if(type == "seq")
|
if(type == "seq")
|
||||||
return std::make_shared<FUSE::ReadDirSeq>();
|
return std::make_shared<FUSE::ReadDirSeq>();
|
||||||
if(type == "cosr")
|
if(type == "cosr")
|
||||||
return std::make_shared<FUSE::ReadDirCOSR>(concurrency);
|
return std::make_shared<FUSE::ReadDirCOSR>(concurrency,max_queue_depth);
|
||||||
if(type == "cor")
|
if(type == "cor")
|
||||||
return std::make_shared<FUSE::ReadDirCOR>(concurrency);
|
return std::make_shared<FUSE::ReadDirCOR>(concurrency,max_queue_depth);
|
||||||
|
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,135 +0,0 @@
|
||||||
/*
|
|
||||||
Copyright (c) 2020, Antonio SJ Musumeci <trapexit@spawn.link>
|
|
||||||
|
|
||||||
Permission to use, copy, modify, and/or distribute this software for any
|
|
||||||
purpose with or without fee is hereby granted, provided that the above
|
|
||||||
copyright notice and this permission notice appear in all copies.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
||||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
||||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
||||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
||||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
||||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
||||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "branches.hpp"
|
|
||||||
#include "errno.hpp"
|
|
||||||
#include "fs_close.hpp"
|
|
||||||
#include "fs_devid.hpp"
|
|
||||||
#include "fs_getdents64.hpp"
|
|
||||||
#include "fs_inode.hpp"
|
|
||||||
#include "fs_open.hpp"
|
|
||||||
#include "fs_path.hpp"
|
|
||||||
#include "fs_stat.hpp"
|
|
||||||
#include "hashset.hpp"
|
|
||||||
#include "linux_dirent64.h"
|
|
||||||
#include "mempools.hpp"
|
|
||||||
|
|
||||||
#include "fuse.h"
|
|
||||||
#include "fuse_dirents.h"
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include <stddef.h>
|
|
||||||
|
|
||||||
using std::string;
|
|
||||||
using std::vector;
|
|
||||||
|
|
||||||
namespace l
|
|
||||||
{
|
|
||||||
static
|
|
||||||
int
|
|
||||||
close_free_ret_enomem(int fd_,
|
|
||||||
void *buf_)
|
|
||||||
{
|
|
||||||
fs::close(fd_);
|
|
||||||
g_DENTS_BUF_POOL.free(buf_);
|
|
||||||
return -ENOMEM;
|
|
||||||
}
|
|
||||||
|
|
||||||
static
|
|
||||||
int
|
|
||||||
readdir(const Branches::CPtr &branches_,
|
|
||||||
const char *dirname_,
|
|
||||||
fuse_dirents_t *buf_)
|
|
||||||
{
|
|
||||||
int rv;
|
|
||||||
dev_t dev;
|
|
||||||
char *buf;
|
|
||||||
HashSet names;
|
|
||||||
string basepath;
|
|
||||||
string fullpath;
|
|
||||||
uint64_t namelen;
|
|
||||||
struct linux_dirent64 *d;
|
|
||||||
|
|
||||||
fuse_dirents_reset(buf_);
|
|
||||||
|
|
||||||
buf = (char*)g_DENTS_BUF_POOL.alloc();
|
|
||||||
if(buf == NULL)
|
|
||||||
return -ENOMEM;
|
|
||||||
|
|
||||||
for(const auto &branch : *branches_)
|
|
||||||
{
|
|
||||||
int dirfd;
|
|
||||||
int64_t nread;
|
|
||||||
|
|
||||||
basepath = fs::path::make(branch.path,dirname_);
|
|
||||||
|
|
||||||
dirfd = fs::open_dir_ro(basepath);
|
|
||||||
if(dirfd == -1)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
dev = fs::devid(dirfd);
|
|
||||||
|
|
||||||
for(;;)
|
|
||||||
{
|
|
||||||
nread = fs::getdents_64(dirfd,buf,g_DENTS_BUF_POOL.size());
|
|
||||||
if(nread == -1)
|
|
||||||
break;
|
|
||||||
if(nread == 0)
|
|
||||||
break;
|
|
||||||
|
|
||||||
for(int64_t pos = 0; pos < nread; pos += d->reclen)
|
|
||||||
{
|
|
||||||
d = (struct linux_dirent64*)(buf + pos);
|
|
||||||
namelen = strlen(d->name);
|
|
||||||
|
|
||||||
rv = names.put(d->name,namelen);
|
|
||||||
if(rv == 0)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
fullpath = fs::path::make(dirname_,d->name);
|
|
||||||
d->ino = fs::inode::calc(fullpath.c_str(),
|
|
||||||
fullpath.size(),
|
|
||||||
DTTOIF(d->type),
|
|
||||||
dev,
|
|
||||||
d->ino);
|
|
||||||
|
|
||||||
rv = fuse_dirents_add_linux(buf_,d,namelen);
|
|
||||||
if(rv)
|
|
||||||
return close_free_ret_enomem(dirfd,buf);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fs::close(dirfd);
|
|
||||||
}
|
|
||||||
|
|
||||||
g_DENTS_BUF_POOL.free(buf);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace FUSE
|
|
||||||
{
|
|
||||||
int
|
|
||||||
readdir_linux(const Branches::CPtr &branches_,
|
|
||||||
const char *dirname_,
|
|
||||||
fuse_dirents_t *buf_)
|
|
||||||
{
|
|
||||||
return l::readdir(branches_,dirname_,buf_);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,34 +0,0 @@
|
||||||
/*
|
|
||||||
ISC License
|
|
||||||
|
|
||||||
Copyright (c) 2020, Antonio SJ Musumeci <trapexit@spawn.link>
|
|
||||||
|
|
||||||
Permission to use, copy, modify, and/or distribute this software for any
|
|
||||||
purpose with or without fee is hereby granted, provided that the above
|
|
||||||
copyright notice and this permission notice appear in all copies.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
||||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
||||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
||||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
||||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
||||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
||||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "branches.hpp"
|
|
||||||
|
|
||||||
#include "fuse.h"
|
|
||||||
|
|
||||||
#include <cstdint>
|
|
||||||
|
|
||||||
|
|
||||||
namespace FUSE
|
|
||||||
{
|
|
||||||
int
|
|
||||||
readdir_linux(const Branches::CPtr &branches,
|
|
||||||
const char *dirname,
|
|
||||||
fuse_dirents_t *buf);
|
|
||||||
}
|
|
|
@ -1,7 +1,7 @@
|
||||||
/*
|
/*
|
||||||
ISC License
|
ISC License
|
||||||
|
|
||||||
Copyright (c) 2020, Antonio SJ Musumeci <trapexit@spawn.link>
|
Copyright (c) 2023, Antonio SJ Musumeci <trapexit@spawn.link>
|
||||||
|
|
||||||
Permission to use, copy, modify, and/or distribute this software for any
|
Permission to use, copy, modify, and/or distribute this software for any
|
||||||
purpose with or without fee is hereby granted, provided that the above
|
purpose with or without fee is hereby granted, provided that the above
|
||||||
|
@ -16,17 +16,14 @@
|
||||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "errno.hpp"
|
#include "fuse_readdir_plus.hpp"
|
||||||
|
|
||||||
#include "fuse.h"
|
#include <errno.h>
|
||||||
|
|
||||||
|
|
||||||
namespace FUSE
|
int
|
||||||
|
FUSE::readdir_plus(const fuse_file_info_t *ffi_,
|
||||||
|
fuse_dirents_t *buf_)
|
||||||
{
|
{
|
||||||
int
|
return -ENOTSUP;
|
||||||
readdir_plus(const fuse_file_info_t *ffi_,
|
|
||||||
fuse_dirents_t *buf_)
|
|
||||||
{
|
|
||||||
return -ENOTSUP;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
Copyright (c) 2016, Antonio SJ Musumeci <trapexit@spawn.link>
|
Copyright (c) 2023, Antonio SJ Musumeci <trapexit@spawn.link>
|
||||||
|
|
||||||
Permission to use, copy, modify, and/or distribute this software for any
|
Permission to use, copy, modify, and/or distribute this software for any
|
||||||
purpose with or without fee is hereby granted, provided that the above
|
purpose with or without fee is hereby granted, provided that the above
|
||||||
|
@ -21,7 +21,6 @@
|
||||||
|
|
||||||
namespace FUSE
|
namespace FUSE
|
||||||
{
|
{
|
||||||
int
|
int readdir_plus(fuse_file_info_t const *ffi,
|
||||||
readdir_plus(const fuse_file_info_t *ffi,
|
fuse_dirents_t *buf);
|
||||||
fuse_dirents_t *buf);
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,151 +0,0 @@
|
||||||
/*
|
|
||||||
Copyright (c) 2019, Antonio SJ Musumeci <trapexit@spawn.link>
|
|
||||||
|
|
||||||
Permission to use, copy, modify, and/or distribute this software for any
|
|
||||||
purpose with or without fee is hereby granted, provided that the above
|
|
||||||
copyright notice and this permission notice appear in all copies.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
||||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
||||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
||||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
||||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
||||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
||||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "branches.hpp"
|
|
||||||
#include "errno.hpp"
|
|
||||||
#include "fs_close.hpp"
|
|
||||||
#include "fs_devid.hpp"
|
|
||||||
#include "fs_fstatat.hpp"
|
|
||||||
#include "fs_getdents64.hpp"
|
|
||||||
#include "fs_inode.hpp"
|
|
||||||
#include "fs_open.hpp"
|
|
||||||
#include "fs_path.hpp"
|
|
||||||
#include "fs_stat.hpp"
|
|
||||||
#include "hashset.hpp"
|
|
||||||
#include "linux_dirent64.h"
|
|
||||||
#include "mempools.hpp"
|
|
||||||
|
|
||||||
#include "fuse.h"
|
|
||||||
#include "fuse_dirents.h"
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include <stddef.h>
|
|
||||||
|
|
||||||
using std::string;
|
|
||||||
using std::vector;
|
|
||||||
|
|
||||||
namespace l
|
|
||||||
{
|
|
||||||
static
|
|
||||||
int
|
|
||||||
close_free_ret_enomem(int fd_,
|
|
||||||
void *buf_)
|
|
||||||
{
|
|
||||||
fs::close(fd_);
|
|
||||||
g_DENTS_BUF_POOL.free(buf_);
|
|
||||||
return -ENOMEM;
|
|
||||||
}
|
|
||||||
|
|
||||||
static
|
|
||||||
int
|
|
||||||
readdir_plus(const Branches::CPtr &branches_,
|
|
||||||
const char *dirname_,
|
|
||||||
const uint64_t entry_timeout_,
|
|
||||||
const uint64_t attr_timeout_,
|
|
||||||
fuse_dirents_t *buf_)
|
|
||||||
{
|
|
||||||
int rv;
|
|
||||||
dev_t dev;
|
|
||||||
char *buf;
|
|
||||||
HashSet names;
|
|
||||||
string basepath;
|
|
||||||
string fullpath;
|
|
||||||
uint64_t namelen;
|
|
||||||
struct stat st;
|
|
||||||
fuse_entry_t entry;
|
|
||||||
struct linux_dirent64 *d;
|
|
||||||
|
|
||||||
fuse_dirents_reset(buf_);
|
|
||||||
|
|
||||||
buf = (char*)g_DENTS_BUF_POOL.alloc();
|
|
||||||
|
|
||||||
entry.nodeid = 0;
|
|
||||||
entry.generation = 0;
|
|
||||||
entry.entry_valid = entry_timeout_;
|
|
||||||
entry.attr_valid = attr_timeout_;
|
|
||||||
entry.entry_valid_nsec = 0;
|
|
||||||
entry.attr_valid_nsec = 0;
|
|
||||||
for(auto &branch : *branches_)
|
|
||||||
{
|
|
||||||
int dirfd;
|
|
||||||
int64_t nread;
|
|
||||||
|
|
||||||
basepath = fs::path::make(branch.path,dirname_);
|
|
||||||
|
|
||||||
dirfd = fs::open_dir_ro(basepath);
|
|
||||||
if(dirfd == -1)
|
|
||||||
continue;
|
|
||||||
dev = fs::devid(dirfd);
|
|
||||||
|
|
||||||
for(;;)
|
|
||||||
{
|
|
||||||
nread = fs::getdents_64(dirfd,buf,g_DENTS_BUF_POOL.size());
|
|
||||||
if(nread == -1)
|
|
||||||
break;
|
|
||||||
if(nread == 0)
|
|
||||||
break;
|
|
||||||
|
|
||||||
for(int64_t pos = 0; pos < nread; pos += d->reclen)
|
|
||||||
{
|
|
||||||
d = (struct linux_dirent64*)(buf + pos);
|
|
||||||
namelen = (strlen(d->name) + 1);
|
|
||||||
|
|
||||||
rv = names.put(d->name,namelen);
|
|
||||||
if(rv == 0)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
rv = fs::fstatat_nofollow(dirfd,d->name,&st);
|
|
||||||
if(rv == -1)
|
|
||||||
{
|
|
||||||
memset(&st,0,sizeof(st));
|
|
||||||
st.st_ino = d->ino;
|
|
||||||
st.st_dev = dev;
|
|
||||||
st.st_mode = DTTOIF(d->type);
|
|
||||||
}
|
|
||||||
|
|
||||||
fullpath = fs::path::make(dirname_,d->name);
|
|
||||||
fs::inode::calc(fullpath,&st);
|
|
||||||
d->ino = st.st_ino;
|
|
||||||
|
|
||||||
rv = fuse_dirents_add_linux_plus(buf_,d,namelen,&entry,&st);
|
|
||||||
if(rv)
|
|
||||||
return close_free_ret_enomem(dirfd,buf);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fs::close(dirfd);
|
|
||||||
}
|
|
||||||
|
|
||||||
g_DENTS_BUF_POOL.free(buf);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace FUSE
|
|
||||||
{
|
|
||||||
int
|
|
||||||
readdir_plus_linux(const Branches::CPtr &branches_,
|
|
||||||
const char *dirname_,
|
|
||||||
const uint64_t entry_timeout_,
|
|
||||||
const uint64_t attr_timeout_,
|
|
||||||
fuse_dirents_t *buf_)
|
|
||||||
{
|
|
||||||
return l::readdir_plus(branches_,dirname_,entry_timeout_,attr_timeout_,buf_);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,36 +0,0 @@
|
||||||
/*
|
|
||||||
ISC License
|
|
||||||
|
|
||||||
Copyright (c) 2020, Antonio SJ Musumeci <trapexit@spawn.link>
|
|
||||||
|
|
||||||
Permission to use, copy, modify, and/or distribute this software for any
|
|
||||||
purpose with or without fee is hereby granted, provided that the above
|
|
||||||
copyright notice and this permission notice appear in all copies.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
||||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
||||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
||||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
||||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
||||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
||||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "branches.hpp"
|
|
||||||
|
|
||||||
#include "fuse.h"
|
|
||||||
|
|
||||||
#include <cstdint>
|
|
||||||
|
|
||||||
|
|
||||||
namespace FUSE
|
|
||||||
{
|
|
||||||
int
|
|
||||||
readdir_plus_linux(const Branches::CPtr &branches,
|
|
||||||
const char *dirname,
|
|
||||||
const uint64_t entry_timeout,
|
|
||||||
const uint64_t attr_timeout,
|
|
||||||
fuse_dirents_t *buf);
|
|
||||||
}
|
|
|
@ -1,142 +0,0 @@
|
||||||
/*
|
|
||||||
Copyright (c) 2019, Antonio SJ Musumeci <trapexit@spawn.link>
|
|
||||||
|
|
||||||
Permission to use, copy, modify, and/or distribute this software for any
|
|
||||||
purpose with or without fee is hereby granted, provided that the above
|
|
||||||
copyright notice and this permission notice appear in all copies.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
||||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
||||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
||||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
||||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
||||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
||||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#define _DEFAULT_SOURCE
|
|
||||||
|
|
||||||
#include "branches.hpp"
|
|
||||||
#include "errno.hpp"
|
|
||||||
#include "fs_closedir.hpp"
|
|
||||||
#include "fs_devid.hpp"
|
|
||||||
#include "fs_dirfd.hpp"
|
|
||||||
#include "fs_fstatat.hpp"
|
|
||||||
#include "fs_inode.hpp"
|
|
||||||
#include "fs_opendir.hpp"
|
|
||||||
#include "fs_path.hpp"
|
|
||||||
#include "fs_readdir.hpp"
|
|
||||||
#include "fs_stat.hpp"
|
|
||||||
#include "hashset.hpp"
|
|
||||||
|
|
||||||
#include "fuse.h"
|
|
||||||
#include "fuse_dirents.h"
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include <dirent.h>
|
|
||||||
|
|
||||||
using std::string;
|
|
||||||
using std::vector;
|
|
||||||
|
|
||||||
namespace l
|
|
||||||
{
|
|
||||||
static
|
|
||||||
uint64_t
|
|
||||||
dirent_exact_namelen(const struct dirent *d_)
|
|
||||||
{
|
|
||||||
#ifdef _D_EXACT_NAMLEN
|
|
||||||
return _D_EXACT_NAMLEN(d_);
|
|
||||||
#elif defined _DIRENT_HAVE_D_NAMLEN
|
|
||||||
return d_->d_namlen;
|
|
||||||
#else
|
|
||||||
return strlen(d_->d_name);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
static
|
|
||||||
int
|
|
||||||
readdir_plus(const Branches::CPtr &branches_,
|
|
||||||
const char *dirname_,
|
|
||||||
const uint64_t entry_timeout_,
|
|
||||||
const uint64_t attr_timeout_,
|
|
||||||
fuse_dirents_t *buf_)
|
|
||||||
{
|
|
||||||
dev_t dev;
|
|
||||||
HashSet names;
|
|
||||||
string basepath;
|
|
||||||
string fullpath;
|
|
||||||
struct stat st;
|
|
||||||
uint64_t namelen;
|
|
||||||
fuse_entry_t entry;
|
|
||||||
|
|
||||||
fuse_dirents_reset(buf_);
|
|
||||||
|
|
||||||
entry.nodeid = 0;
|
|
||||||
entry.generation = 0;
|
|
||||||
entry.entry_valid = entry_timeout_;
|
|
||||||
entry.attr_valid = attr_timeout_;
|
|
||||||
entry.entry_valid_nsec = 0;
|
|
||||||
entry.attr_valid_nsec = 0;
|
|
||||||
for(auto &branch : *branches_)
|
|
||||||
{
|
|
||||||
int rv;
|
|
||||||
int dirfd;
|
|
||||||
DIR *dh;
|
|
||||||
|
|
||||||
basepath = fs::path::make(branch.path,dirname_);
|
|
||||||
|
|
||||||
dh = fs::opendir(basepath);
|
|
||||||
if(!dh)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
dirfd = fs::dirfd(dh);
|
|
||||||
dev = fs::devid(dirfd);
|
|
||||||
|
|
||||||
rv = 0;
|
|
||||||
for(struct dirent *de = fs::readdir(dh); de && !rv; de = fs::readdir(dh))
|
|
||||||
{
|
|
||||||
namelen = l::dirent_exact_namelen(de);
|
|
||||||
|
|
||||||
rv = names.put(de->d_name,namelen);
|
|
||||||
if(rv == 0)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
rv = fs::fstatat_nofollow(dirfd,de->d_name,&st);
|
|
||||||
if(rv == -1)
|
|
||||||
{
|
|
||||||
memset(&st,0,sizeof(st));
|
|
||||||
st.st_ino = de->d_ino;
|
|
||||||
st.st_dev = dev;
|
|
||||||
st.st_mode = DTTOIF(de->d_type);
|
|
||||||
}
|
|
||||||
|
|
||||||
fullpath = fs::path::make(dirname_,de->d_name);
|
|
||||||
fs::inode::calc(fullpath,&st);
|
|
||||||
de->d_ino = st.st_ino;
|
|
||||||
|
|
||||||
rv = fuse_dirents_add_plus(buf_,de,namelen,&entry,&st);
|
|
||||||
if(rv)
|
|
||||||
return (fs::closedir(dh),-ENOMEM);
|
|
||||||
}
|
|
||||||
|
|
||||||
fs::closedir(dh);
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace FUSE
|
|
||||||
{
|
|
||||||
int
|
|
||||||
readdir_plus_posix(const Branches::CPtr &branches_,
|
|
||||||
const char *dirname_,
|
|
||||||
const uint64_t entry_timeout_,
|
|
||||||
const uint64_t attr_timeout_,
|
|
||||||
fuse_dirents_t *buf_)
|
|
||||||
{
|
|
||||||
return l::readdir_plus(branches_,dirname_,entry_timeout_,attr_timeout_,buf_);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,36 +0,0 @@
|
||||||
/*
|
|
||||||
ISC License
|
|
||||||
|
|
||||||
Copyright (c) 2020, Antonio SJ Musumeci <trapexit@spawn.link>
|
|
||||||
|
|
||||||
Permission to use, copy, modify, and/or distribute this software for any
|
|
||||||
purpose with or without fee is hereby granted, provided that the above
|
|
||||||
copyright notice and this permission notice appear in all copies.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
||||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
||||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
||||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
||||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
||||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
||||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "branches.hpp"
|
|
||||||
|
|
||||||
#include "fuse.h"
|
|
||||||
|
|
||||||
#include <cstdint>
|
|
||||||
|
|
||||||
|
|
||||||
namespace FUSE
|
|
||||||
{
|
|
||||||
int
|
|
||||||
readdir_plus_posix(const Branches::CPtr &branches,
|
|
||||||
const char *dirname,
|
|
||||||
const uint64_t entry_timeout,
|
|
||||||
const uint64_t attr_timeout,
|
|
||||||
fuse_dirents_t *buf);
|
|
||||||
}
|
|
|
@ -31,17 +31,44 @@
|
||||||
#include "fs_readdir.hpp"
|
#include "fs_readdir.hpp"
|
||||||
#include "fs_stat.hpp"
|
#include "fs_stat.hpp"
|
||||||
#include "hashset.hpp"
|
#include "hashset.hpp"
|
||||||
|
#include "scope_guard.hpp"
|
||||||
#include "ugid.hpp"
|
#include "ugid.hpp"
|
||||||
|
|
||||||
#include "fuse.h"
|
#include "fuse.h"
|
||||||
#include "fuse_dirents.h"
|
#include "fuse_dirents.h"
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
|
|
||||||
namespace l
|
namespace l
|
||||||
{
|
{
|
||||||
|
struct Error
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
int _err;
|
||||||
|
|
||||||
|
public:
|
||||||
|
Error()
|
||||||
|
: _err(ENOENT)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
operator int()
|
||||||
|
{
|
||||||
|
return _err;
|
||||||
|
}
|
||||||
|
|
||||||
|
Error&
|
||||||
|
operator=(int v_)
|
||||||
|
{
|
||||||
|
if(_err != 0)
|
||||||
|
_err = v_;
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
static
|
static
|
||||||
uint64_t
|
uint64_t
|
||||||
dirent_exact_namelen(const struct dirent *d_)
|
dirent_exact_namelen(const struct dirent *d_)
|
||||||
|
@ -61,13 +88,14 @@ namespace l
|
||||||
const char *dirname_,
|
const char *dirname_,
|
||||||
fuse_dirents_t *buf_)
|
fuse_dirents_t *buf_)
|
||||||
{
|
{
|
||||||
|
Error error;
|
||||||
HashSet names;
|
HashSet names;
|
||||||
std::string basepath;
|
std::string basepath;
|
||||||
std::string fullpath;
|
std::string fullpath;
|
||||||
|
|
||||||
fuse_dirents_reset(buf_);
|
fuse_dirents_reset(buf_);
|
||||||
|
|
||||||
for(const auto &branch : *branches_)
|
for(auto const &branch : *branches_)
|
||||||
{
|
{
|
||||||
int rv;
|
int rv;
|
||||||
DIR *dh;
|
DIR *dh;
|
||||||
|
@ -75,14 +103,18 @@ namespace l
|
||||||
|
|
||||||
basepath = fs::path::make(branch.path,dirname_);
|
basepath = fs::path::make(branch.path,dirname_);
|
||||||
|
|
||||||
|
errno = 0;
|
||||||
dh = fs::opendir(basepath);
|
dh = fs::opendir(basepath);
|
||||||
|
error = errno;
|
||||||
if(!dh)
|
if(!dh)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
DEFER{ fs::closedir(dh); };
|
||||||
|
|
||||||
dev = fs::devid(dh);
|
dev = fs::devid(dh);
|
||||||
|
|
||||||
rv = 0;
|
rv = 0;
|
||||||
for(struct dirent *de = fs::readdir(dh); de && !rv; de = fs::readdir(dh))
|
for(dirent *de = fs::readdir(dh); de && !rv; de = fs::readdir(dh))
|
||||||
{
|
{
|
||||||
std::uint64_t namelen;
|
std::uint64_t namelen;
|
||||||
|
|
||||||
|
@ -100,13 +132,11 @@ namespace l
|
||||||
|
|
||||||
rv = fuse_dirents_add(buf_,de,namelen);
|
rv = fuse_dirents_add(buf_,de,namelen);
|
||||||
if(rv)
|
if(rv)
|
||||||
return (fs::closedir(dh),-ENOMEM);
|
return -ENOMEM;
|
||||||
}
|
}
|
||||||
|
|
||||||
fs::closedir(dh);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return -error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
369
src/scope_guard.hpp
Normal file
369
src/scope_guard.hpp
Normal file
|
@ -0,0 +1,369 @@
|
||||||
|
// _____ _____ _ _____
|
||||||
|
// / ____| / ____| | | / ____|_ _
|
||||||
|
// | (___ ___ ___ _ __ ___ | | __ _ _ __ _ _ __ __| | | | _| |_ _| |_
|
||||||
|
// \___ \ / __/ _ \| '_ \ / _ \ | | |_ | | | |/ _` | '__/ _` | | | |_ _|_ _|
|
||||||
|
// ____) | (_| (_) | |_) | __/ | |__| | |_| | (_| | | | (_| | | |____|_| |_|
|
||||||
|
// |_____/ \___\___/| .__/ \___| \_____|\__,_|\__,_|_| \__,_| \_____|
|
||||||
|
// | | https://github.com/Neargye/scope_guard
|
||||||
|
// |_| version 0.9.1
|
||||||
|
//
|
||||||
|
// Licensed under the MIT License <http://opensource.org/licenses/MIT>.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
// Copyright (c) 2018 - 2021 Daniil Goncharov <neargye@gmail.com>.
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
|
// in the Software without restriction, including without limitation the rights
|
||||||
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
// copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in all
|
||||||
|
// copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
// SOFTWARE.
|
||||||
|
|
||||||
|
#ifndef NEARGYE_SCOPE_GUARD_HPP
|
||||||
|
#define NEARGYE_SCOPE_GUARD_HPP
|
||||||
|
|
||||||
|
#define SCOPE_GUARD_VERSION_MAJOR 0
|
||||||
|
#define SCOPE_GUARD_VERSION_MINOR 9
|
||||||
|
#define SCOPE_GUARD_VERSION_PATCH 1
|
||||||
|
|
||||||
|
#include <type_traits>
|
||||||
|
#if (defined(_MSC_VER) && _MSC_VER >= 1900) || ((defined(__clang__) || defined(__GNUC__)) && __cplusplus >= 201700L)
|
||||||
|
#include <exception>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// scope_guard throwable settings:
|
||||||
|
// SCOPE_GUARD_NO_THROW_CONSTRUCTIBLE requires nothrow constructible action.
|
||||||
|
// SCOPE_GUARD_MAY_THROW_ACTION action may throw exceptions.
|
||||||
|
// SCOPE_GUARD_NO_THROW_ACTION requires noexcept action.
|
||||||
|
// SCOPE_GUARD_SUPPRESS_THROW_ACTION exceptions during action will be suppressed.
|
||||||
|
// SCOPE_GUARD_CATCH_HANDLER exceptions handler. If SCOPE_GUARD_SUPPRESS_THROW_ACTIONS is not defined, it will do nothing.
|
||||||
|
|
||||||
|
#if !defined(SCOPE_GUARD_MAY_THROW_ACTION) && !defined(SCOPE_GUARD_NO_THROW_ACTION) && !defined(SCOPE_GUARD_SUPPRESS_THROW_ACTION)
|
||||||
|
# define SCOPE_GUARD_MAY_THROW_ACTION
|
||||||
|
#elif (defined(SCOPE_GUARD_MAY_THROW_ACTION) + defined(SCOPE_GUARD_NO_THROW_ACTION) + defined(SCOPE_GUARD_SUPPRESS_THROW_ACTION)) > 1
|
||||||
|
# error Only one of SCOPE_GUARD_MAY_THROW_ACTION and SCOPE_GUARD_NO_THROW_ACTION and SCOPE_GUARD_SUPPRESS_THROW_ACTION may be defined.
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if !defined(SCOPE_GUARD_CATCH_HANDLER)
|
||||||
|
# define SCOPE_GUARD_CATCH_HANDLER /* Suppress exception.*/
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace scope_guard {
|
||||||
|
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
#if defined(SCOPE_GUARD_SUPPRESS_THROW_ACTION) && (defined(__cpp_exceptions) || defined(__EXCEPTIONS) || (_HAS_EXCEPTIONS))
|
||||||
|
# define NEARGYE_NOEXCEPT(...) noexcept
|
||||||
|
# define NEARGYE_TRY try {
|
||||||
|
# define NEARGYE_CATCH } catch (...) { SCOPE_GUARD_CATCH_HANDLER }
|
||||||
|
#else
|
||||||
|
# define NEARGYE_NOEXCEPT(...) noexcept(__VA_ARGS__)
|
||||||
|
# define NEARGYE_TRY
|
||||||
|
# define NEARGYE_CATCH
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define NEARGYE_MOV(...) static_cast<typename std::remove_reference<decltype(__VA_ARGS__)>::type&&>(__VA_ARGS__)
|
||||||
|
#define NEARGYE_FWD(...) static_cast<decltype(__VA_ARGS__)&&>(__VA_ARGS__)
|
||||||
|
|
||||||
|
// NEARGYE_NODISCARD encourages the compiler to issue a warning if the return value is discarded.
|
||||||
|
#if !defined(NEARGYE_NODISCARD)
|
||||||
|
# if defined(__clang__)
|
||||||
|
# if (__clang_major__ * 10 + __clang_minor__) >= 39 && __cplusplus >= 201703L
|
||||||
|
# define NEARGYE_NODISCARD [[nodiscard]]
|
||||||
|
# else
|
||||||
|
# define NEARGYE_NODISCARD __attribute__((__warn_unused_result__))
|
||||||
|
# endif
|
||||||
|
# elif defined(__GNUC__)
|
||||||
|
# if __GNUC__ >= 7 && __cplusplus >= 201703L
|
||||||
|
# define NEARGYE_NODISCARD [[nodiscard]]
|
||||||
|
# else
|
||||||
|
# define NEARGYE_NODISCARD __attribute__((__warn_unused_result__))
|
||||||
|
# endif
|
||||||
|
# elif defined(_MSC_VER)
|
||||||
|
# if _MSC_VER >= 1911 && defined(_MSVC_LANG) && _MSVC_LANG >= 201703L
|
||||||
|
# define NEARGYE_NODISCARD [[nodiscard]]
|
||||||
|
# elif defined(_Check_return_)
|
||||||
|
# define NEARGYE_NODISCARD _Check_return_
|
||||||
|
# else
|
||||||
|
# define NEARGYE_NODISCARD
|
||||||
|
# endif
|
||||||
|
# else
|
||||||
|
# define NEARGYE_NODISCARD
|
||||||
|
# endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(_MSC_VER) && _MSC_VER < 1900
|
||||||
|
inline int uncaught_exceptions() noexcept {
|
||||||
|
return *(reinterpret_cast<int*>(static_cast<char*>(static_cast<void*>(_getptd())) + (sizeof(void*) == 8 ? 0x100 : 0x90)));
|
||||||
|
}
|
||||||
|
#elif (defined(__clang__) || defined(__GNUC__)) && __cplusplus < 201700L
|
||||||
|
struct __cxa_eh_globals;
|
||||||
|
extern "C" __cxa_eh_globals* __cxa_get_globals() noexcept;
|
||||||
|
inline int uncaught_exceptions() noexcept {
|
||||||
|
return static_cast<int>(*(reinterpret_cast<unsigned int*>(static_cast<char*>(static_cast<void*>(__cxa_get_globals())) + sizeof(void*))));
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
inline int uncaught_exceptions() noexcept {
|
||||||
|
return std::uncaught_exceptions();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
class on_exit_policy {
|
||||||
|
bool execute_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit on_exit_policy(bool execute) noexcept : execute_{execute} {}
|
||||||
|
|
||||||
|
void dismiss() noexcept {
|
||||||
|
execute_ = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool should_execute() const noexcept {
|
||||||
|
return execute_;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class on_fail_policy {
|
||||||
|
int ec_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit on_fail_policy(bool execute) noexcept : ec_{execute ? uncaught_exceptions() : -1} {}
|
||||||
|
|
||||||
|
void dismiss() noexcept {
|
||||||
|
ec_ = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool should_execute() const noexcept {
|
||||||
|
return ec_ != -1 && ec_ < uncaught_exceptions();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class on_success_policy {
|
||||||
|
int ec_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit on_success_policy(bool execute) noexcept : ec_{execute ? uncaught_exceptions() : -1} {}
|
||||||
|
|
||||||
|
void dismiss() noexcept {
|
||||||
|
ec_ = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool should_execute() const noexcept {
|
||||||
|
return ec_ != -1 && ec_ >= uncaught_exceptions();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T, typename = void>
|
||||||
|
struct is_noarg_returns_void_action
|
||||||
|
: std::false_type {};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct is_noarg_returns_void_action<T, decltype((std::declval<T>())())>
|
||||||
|
: std::true_type {};
|
||||||
|
|
||||||
|
template <typename T, bool = is_noarg_returns_void_action<T>::value>
|
||||||
|
struct is_nothrow_invocable_action
|
||||||
|
: std::false_type {};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct is_nothrow_invocable_action<T, true>
|
||||||
|
: std::integral_constant<bool, noexcept((std::declval<T>())())> {};
|
||||||
|
|
||||||
|
template <typename F, typename P>
|
||||||
|
class scope_guard {
|
||||||
|
using A = typename std::decay<F>::type;
|
||||||
|
|
||||||
|
static_assert(is_noarg_returns_void_action<A>::value,
|
||||||
|
"scope_guard requires no-argument action, that returns void.");
|
||||||
|
static_assert(std::is_same<P, on_exit_policy>::value || std::is_same<P, on_fail_policy>::value || std::is_same<P, on_success_policy>::value,
|
||||||
|
"scope_guard requires on_exit_policy, on_fail_policy or on_success_policy.");
|
||||||
|
#if defined(SCOPE_GUARD_NO_THROW_ACTION)
|
||||||
|
static_assert(is_nothrow_invocable_action<A>::value,
|
||||||
|
"scope_guard requires noexcept invocable action.");
|
||||||
|
#endif
|
||||||
|
#if defined(SCOPE_GUARD_NO_THROW_CONSTRUCTIBLE)
|
||||||
|
static_assert(std::is_nothrow_move_constructible<A>::value,
|
||||||
|
"scope_guard requires nothrow constructible action.");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
P policy_;
|
||||||
|
A action_;
|
||||||
|
|
||||||
|
void* operator new(std::size_t) = delete;
|
||||||
|
void operator delete(void*) = delete;
|
||||||
|
|
||||||
|
public:
|
||||||
|
scope_guard() = delete;
|
||||||
|
scope_guard(const scope_guard&) = delete;
|
||||||
|
scope_guard& operator=(const scope_guard&) = delete;
|
||||||
|
scope_guard& operator=(scope_guard&&) = delete;
|
||||||
|
|
||||||
|
scope_guard(scope_guard&& other) noexcept(std::is_nothrow_move_constructible<A>::value)
|
||||||
|
: policy_{false},
|
||||||
|
action_{NEARGYE_MOV(other.action_)} {
|
||||||
|
policy_ = NEARGYE_MOV(other.policy_);
|
||||||
|
other.policy_.dismiss();
|
||||||
|
}
|
||||||
|
|
||||||
|
scope_guard(const A& action) = delete;
|
||||||
|
scope_guard(A& action) = delete;
|
||||||
|
|
||||||
|
explicit scope_guard(A&& action) noexcept(std::is_nothrow_move_constructible<A>::value)
|
||||||
|
: policy_{true},
|
||||||
|
action_{NEARGYE_MOV(action)} {}
|
||||||
|
|
||||||
|
void dismiss() noexcept {
|
||||||
|
policy_.dismiss();
|
||||||
|
}
|
||||||
|
|
||||||
|
~scope_guard() NEARGYE_NOEXCEPT(is_nothrow_invocable_action<A>::value) {
|
||||||
|
if (policy_.should_execute()) {
|
||||||
|
NEARGYE_TRY
|
||||||
|
action_();
|
||||||
|
NEARGYE_CATCH
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename F>
|
||||||
|
using scope_exit = scope_guard<F, on_exit_policy>;
|
||||||
|
|
||||||
|
template <typename F, typename std::enable_if<is_noarg_returns_void_action<F>::value, int>::type = 0>
|
||||||
|
NEARGYE_NODISCARD scope_exit<F> make_scope_exit(F&& action) noexcept(noexcept(scope_exit<F>{NEARGYE_FWD(action)})) {
|
||||||
|
return scope_exit<F>{NEARGYE_FWD(action)};
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename F>
|
||||||
|
using scope_fail = scope_guard<F, on_fail_policy>;
|
||||||
|
|
||||||
|
template <typename F, typename std::enable_if<is_noarg_returns_void_action<F>::value, int>::type = 0>
|
||||||
|
NEARGYE_NODISCARD scope_fail<F> make_scope_fail(F&& action) noexcept(noexcept(scope_fail<F>{NEARGYE_FWD(action)})) {
|
||||||
|
return scope_fail<F>{NEARGYE_FWD(action)};
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename F>
|
||||||
|
using scope_success = scope_guard<F, on_success_policy>;
|
||||||
|
|
||||||
|
template <typename F, typename std::enable_if<is_noarg_returns_void_action<F>::value, int>::type = 0>
|
||||||
|
NEARGYE_NODISCARD scope_success<F> make_scope_success(F&& action) noexcept(noexcept(scope_success<F>{NEARGYE_FWD(action)})) {
|
||||||
|
return scope_success<F>{NEARGYE_FWD(action)};
|
||||||
|
}
|
||||||
|
|
||||||
|
struct scope_exit_tag {};
|
||||||
|
|
||||||
|
template <typename F, typename std::enable_if<is_noarg_returns_void_action<F>::value, int>::type = 0>
|
||||||
|
scope_exit<F> operator<<(scope_exit_tag, F&& action) noexcept(noexcept(scope_exit<F>{NEARGYE_FWD(action)})) {
|
||||||
|
return scope_exit<F>{NEARGYE_FWD(action)};
|
||||||
|
}
|
||||||
|
|
||||||
|
struct scope_fail_tag {};
|
||||||
|
|
||||||
|
template <typename F, typename std::enable_if<is_noarg_returns_void_action<F>::value, int>::type = 0>
|
||||||
|
scope_fail<F> operator<<(scope_fail_tag, F&& action) noexcept(noexcept(scope_fail<F>{NEARGYE_FWD(action)})) {
|
||||||
|
return scope_fail<F>{NEARGYE_FWD(action)};
|
||||||
|
}
|
||||||
|
|
||||||
|
struct scope_success_tag {};
|
||||||
|
|
||||||
|
template <typename F, typename std::enable_if<is_noarg_returns_void_action<F>::value, int>::type = 0>
|
||||||
|
scope_success<F> operator<<(scope_success_tag, F&& action) noexcept(noexcept(scope_success<F>{NEARGYE_FWD(action)})) {
|
||||||
|
return scope_success<F>{NEARGYE_FWD(action)};
|
||||||
|
}
|
||||||
|
|
||||||
|
#undef NEARGYE_MOV
|
||||||
|
#undef NEARGYE_FWD
|
||||||
|
#undef NEARGYE_NOEXCEPT
|
||||||
|
#undef NEARGYE_TRY
|
||||||
|
#undef NEARGYE_CATCH
|
||||||
|
#undef NEARGYE_NODISCARD
|
||||||
|
|
||||||
|
} // namespace scope_guard::detail
|
||||||
|
|
||||||
|
using detail::make_scope_exit;
|
||||||
|
using detail::make_scope_fail;
|
||||||
|
using detail::make_scope_success;
|
||||||
|
|
||||||
|
} // namespace scope_guard
|
||||||
|
|
||||||
|
// NEARGYE_MAYBE_UNUSED suppresses compiler warnings on unused entities, if any.
|
||||||
|
#if !defined(NEARGYE_MAYBE_UNUSED)
|
||||||
|
# if defined(__clang__)
|
||||||
|
# if (__clang_major__ * 10 + __clang_minor__) >= 39 && __cplusplus >= 201703L
|
||||||
|
# define NEARGYE_MAYBE_UNUSED [[maybe_unused]]
|
||||||
|
# else
|
||||||
|
# define NEARGYE_MAYBE_UNUSED __attribute__((__unused__))
|
||||||
|
# endif
|
||||||
|
# elif defined(__GNUC__)
|
||||||
|
# if __GNUC__ >= 7 && __cplusplus >= 201703L
|
||||||
|
# define NEARGYE_MAYBE_UNUSED [[maybe_unused]]
|
||||||
|
# else
|
||||||
|
# define NEARGYE_MAYBE_UNUSED __attribute__((__unused__))
|
||||||
|
# endif
|
||||||
|
# elif defined(_MSC_VER)
|
||||||
|
# if _MSC_VER >= 1911 && defined(_MSVC_LANG) && _MSVC_LANG >= 201703L
|
||||||
|
# define NEARGYE_MAYBE_UNUSED [[maybe_unused]]
|
||||||
|
# else
|
||||||
|
# define NEARGYE_MAYBE_UNUSED __pragma(warning(suppress : 4100 4101 4189))
|
||||||
|
# endif
|
||||||
|
# else
|
||||||
|
# define NEARGYE_MAYBE_UNUSED
|
||||||
|
# endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if !defined(NEARGYE_STR_CONCAT)
|
||||||
|
# define NEARGYE_STR_CONCAT_(s1, s2) s1##s2
|
||||||
|
# define NEARGYE_STR_CONCAT(s1, s2) NEARGYE_STR_CONCAT_(s1, s2)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if !defined(NEARGYE_COUNTER)
|
||||||
|
# if defined(__COUNTER__)
|
||||||
|
# define NEARGYE_COUNTER __COUNTER__
|
||||||
|
# elif defined(__LINE__)
|
||||||
|
# define NEARGYE_COUNTER __LINE__
|
||||||
|
# endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(SCOPE_GUARD_NO_THROW_ACTION)
|
||||||
|
# define NEARGYE_MAKE_SCOPE_GUARD_ACTION [&]() noexcept -> void
|
||||||
|
#else
|
||||||
|
# define NEARGYE_MAKE_SCOPE_GUARD_ACTION [&]() -> void
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define NEARGYE_MAKE_SCOPE_EXIT ::scope_guard::detail::scope_exit_tag{} << NEARGYE_MAKE_SCOPE_GUARD_ACTION
|
||||||
|
#define NEARGYE_MAKE_SCOPE_FAIL ::scope_guard::detail::scope_fail_tag{} << NEARGYE_MAKE_SCOPE_GUARD_ACTION
|
||||||
|
#define NEARGYE_MAKE_SCOPE_SUCCESS ::scope_guard::detail::scope_success_tag{} << NEARGYE_MAKE_SCOPE_GUARD_ACTION
|
||||||
|
|
||||||
|
#define NEARGYE_SCOPE_GUARD_WITH_(g, i) for (int i = 1; i--; g)
|
||||||
|
#define NEARGYE_SCOPE_GUARD_WITH(g) NEARGYE_SCOPE_GUARD_WITH_(g, NEARGYE_STR_CONCAT(NEARGYE_INTERNAL_OBJECT_, NEARGYE_COUNTER))
|
||||||
|
|
||||||
|
// SCOPE_EXIT executing action on scope exit.
|
||||||
|
#define MAKE_SCOPE_EXIT(name) auto name = NEARGYE_MAKE_SCOPE_EXIT
|
||||||
|
#define SCOPE_EXIT NEARGYE_MAYBE_UNUSED const MAKE_SCOPE_EXIT(NEARGYE_STR_CONCAT(NEARGYE_SCOPE_EXIT_, NEARGYE_COUNTER))
|
||||||
|
#define WITH_SCOPE_EXIT(guard) NEARGYE_SCOPE_GUARD_WITH(NEARGYE_MAKE_SCOPE_EXIT{ guard })
|
||||||
|
|
||||||
|
// SCOPE_FAIL executing action on scope exit when an exception has been thrown before scope exit.
|
||||||
|
#define MAKE_SCOPE_FAIL(name) auto name = NEARGYE_MAKE_SCOPE_FAIL
|
||||||
|
#define SCOPE_FAIL NEARGYE_MAYBE_UNUSED const MAKE_SCOPE_FAIL(NEARGYE_STR_CONCAT(NEARGYE_SCOPE_FAIL_, NEARGYE_COUNTER))
|
||||||
|
#define WITH_SCOPE_FAIL(guard) NEARGYE_SCOPE_GUARD_WITH(NEARGYE_MAKE_SCOPE_FAIL{ guard })
|
||||||
|
|
||||||
|
// SCOPE_SUCCESS executing action on scope exit when no exceptions have been thrown before scope exit.
|
||||||
|
#define MAKE_SCOPE_SUCCESS(name) auto name = NEARGYE_MAKE_SCOPE_SUCCESS
|
||||||
|
#define SCOPE_SUCCESS NEARGYE_MAYBE_UNUSED const MAKE_SCOPE_SUCCESS(NEARGYE_STR_CONCAT(NEARGYE_SCOPE_SUCCESS_, NEARGYE_COUNTER))
|
||||||
|
#define WITH_SCOPE_SUCCESS(guard) NEARGYE_SCOPE_GUARD_WITH(NEARGYE_MAKE_SCOPE_SUCCESS{ guard })
|
||||||
|
|
||||||
|
// DEFER executing action on scope exit.
|
||||||
|
#define MAKE_DEFER(name) MAKE_SCOPE_EXIT(name)
|
||||||
|
#define DEFER SCOPE_EXIT
|
||||||
|
#define WITH_DEFER(guard) WITH_SCOPE_EXIT(guard)
|
||||||
|
|
||||||
|
#endif // NEARGYE_SCOPE_GUARD_HPP
|
Loading…
Reference in New Issue
Block a user