Browse Source

Merge pull request #1262 from trapexit/readdir-errs

Rework thread pool queue depth impl
pull/1263/head
trapexit 1 year ago
committed by GitHub
parent
commit
8534ee7ceb
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 4
      libfuse/include/fuse_dirents.h
  2. 5
      libfuse/include/linux_dirent64.h
  3. 73
      libfuse/include/thread_pool.hpp
  4. 4
      libfuse/lib/fuse_dirents.c
  5. 7
      libfuse/lib/fuse_loop.cpp
  6. 145
      src/fuse_readdir_cor.cpp
  7. 3
      src/fuse_readdir_cor.hpp
  8. 82
      src/fuse_readdir_cosr.cpp
  9. 3
      src/fuse_readdir_cosr.hpp
  10. 32
      src/fuse_readdir_factory.cpp
  11. 135
      src/fuse_readdir_linux.cpp
  12. 34
      src/fuse_readdir_linux.hpp
  13. 15
      src/fuse_readdir_plus.cpp
  14. 5
      src/fuse_readdir_plus.hpp
  15. 151
      src/fuse_readdir_plus_linux.cpp
  16. 36
      src/fuse_readdir_plus_linux.hpp
  17. 142
      src/fuse_readdir_plus_posix.cpp
  18. 36
      src/fuse_readdir_plus_posix.hpp
  19. 44
      src/fuse_readdir_seq.cpp
  20. 369
      src/scope_guard.hpp

4
libfuse/include/fuse_dirents.h

@ -64,10 +64,10 @@ int fuse_dirents_add_plus(fuse_dirents_t *d,
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);

5
libfuse/include/linux_dirent64.h

@ -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;

73
libfuse/include/thread_pool.hpp

@ -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(),
std::size_t const queue_depth_ = 1,
ThreadPool(unsigned const thread_count_ = std::thread::hardware_concurrency(),
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_,
_name.c_str());
_max_queue_depth,
((_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'",
_threads.size(),
_name.c_str());
"threadpool (%s): destroying %lu threads",
_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};
while(true)
timespec ts = {0,1000};
for(unsigned i = 0; i < 1000000; i++)
{ {
if(_queue.try_enqueue(ptok_,f_))
return;
if(_queue_depth.load(std::memory_order_acquire) < _max_queue_depth)
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};
while(true)
timespec ts = {0,1000};
for(unsigned i = 0; i < 1000000; i++)
{ {
if(_queue.try_enqueue(f_))
return;
if(_queue_depth.load(std::memory_order_acquire) < _max_queue_depth)
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};
while(true)
timespec ts = {0,1000};
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;

4
libfuse/lib/fuse_dirents.c

@ -305,7 +305,7 @@ 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;
@ -337,7 +337,7 @@ 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_)

7
libfuse/lib/fuse_loop.cpp

@ -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++)

145
src/fuse_readdir_cor.cpp

@ -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_)
: _tp(concurrency_,concurrency_,"readdir.cor")
FUSE::ReadDirCOR::ReadDirCOR(unsigned concurrency_,
unsigned max_queue_depth_)
: _tp(concurrency_,max_queue_depth_,"readdir.cor")
{ {
} }
@ -44,79 +48,98 @@ FUSE::ReadDirCOR::~ReadDirCOR()
namespace l namespace l
{ {
static
inline
uint64_t
dirent_exact_namelen(const struct dirent *d_)
struct Error
{
private:
int _err;
public:
Error()
: _err(ENOENT)
{
}
operator int()
{ {
#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
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;
DIR *dh;
int dfd;
dev_t dev; dev_t dev;
std::string filepath; std::string filepath;
dh = fs::opendir(basepath_);
if(dh == NULL)
return -errno;
dfd = fs::open_dir_ro(basepath_);
if(dfd == -1)
return errno;
DEFER{ fs::close(dfd); };
dev = fs::devid(dh);
dev = fs::devid(dfd);
rv = 0; rv = 0;
err = 0;
for(struct dirent *de = fs::readdir(dh); de && !rv; de = fs::readdir(dh))
for(;;)
{
long nread;
char buf[32 * 1024];
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(mutex_);
for(long pos = 0; pos < nread; pos += d->reclen)
{ {
std::uint64_t namelen; std::uint64_t namelen;
namelen = l::dirent_exact_namelen(de);
d = (linux_dirent64_t*)&buf[pos];
{
std::lock_guard<std::mutex> lk(names_mutex_);
rv = names_.put(de->d_name,namelen);
namelen = DIRENT_NAMELEN(d);
rv = names_.put(d->name,namelen);
if(rv == 0) if(rv == 0)
continue; continue;
}
filepath = fs::path::make(basepath_,de->d_name);
de->d_ino = fs::inode::calc(filepath,
DTTOIF(de->d_type),
filepath = fs::path::make(basepath_,d->name);
d->ino = fs::inode::calc(filepath,
DTTOIF(d->type),
dev, dev,
de->d_ino);
d->ino);
{
std::lock_guard<std::mutex> lk(dirents_mutex_);
rv = fuse_dirents_add(buf_,de,namelen);
if(rv == 0)
rv = fuse_dirents_add_linux(buf_,d,namelen);
if(rv >= 0)
continue; continue;
}
err = -ENOMEM;
return ENOMEM;
}
} }
fs::closedir(dh);
return err;
return 0;
} }
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 dirents_mutex;
std::vector<int> rv;
std::mutex mutex;
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());
return rv;
}
static
int
calc_rv(std::vector<int> rvs_)
{
for(auto rv : rvs_)
{
if(rv == 0)
return 0;
}
if(rvs_.empty())
return -ENOENT;
error = future.get();
return rvs_[0];
return -error;
} }
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::calc_rv(rvs);
return l::concurrent_readdir(tp_,branches_,dirname_,buf_,uid_,gid_);
} }
} }

3
src/fuse_readdir_cor.hpp

@ -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,

82
src/fuse_readdir_cosr.cpp

@ -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_)
: _tp(concurrency_,concurrency_,"readdir.cosr")
FUSE::ReadDirCOSR::ReadDirCOSR(unsigned concurrency_,
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();
if(dh == NULL)
dirrv = dh_future.get();
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_);
rv = l::readdir(dh_futures,dirname_,buf_);
futures = l::opendir(tp_,branches_,dirname_,uid_,gid_);
rv = l::readdir(futures,dirname_,buf_);
return rv; return rv;
} }

3
src/fuse_readdir_cosr.hpp

@ -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,

32
src/fuse_readdir_factory.cpp

@ -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;
if(max_queue_depth == 0)
max_queue_depth = DEFAULT_MAX_QUEUE_DEPTH;
max_queue_depth *= concurrency;
type_ = type; type_ = type;
concurrency_ = concurrency; 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 {};
} }

135
src/fuse_readdir_linux.cpp

@ -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_);
}
}

34
src/fuse_readdir_linux.hpp

@ -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);
}

15
src/fuse_readdir_plus.cpp

@ -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
readdir_plus(const fuse_file_info_t *ffi_,
int
FUSE::readdir_plus(const fuse_file_info_t *ffi_,
fuse_dirents_t *buf_) fuse_dirents_t *buf_)
{
{
return -ENOTSUP; return -ENOTSUP;
}
} }

5
src/fuse_readdir_plus.hpp

@ -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
readdir_plus(const fuse_file_info_t *ffi,
int readdir_plus(fuse_file_info_t const *ffi,
fuse_dirents_t *buf); fuse_dirents_t *buf);
} }

151
src/fuse_readdir_plus_linux.cpp

@ -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_);
}
}

36
src/fuse_readdir_plus_linux.hpp

@ -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);
}

142
src/fuse_readdir_plus_posix.cpp

@ -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_);
}
}

36
src/fuse_readdir_plus_posix.hpp

@ -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);
}

44
src/fuse_readdir_seq.cpp

@ -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

@ -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…
Cancel
Save