Rework thread pool queue depth impl

This commit is contained in:
Antonio SJ Musumeci 2023-09-24 17:55:09 -05:00
parent 8635456818
commit 620cab2948
20 changed files with 661 additions and 714 deletions

View File

@ -64,10 +64,10 @@ int fuse_dirents_add_plus(fuse_dirents_t *d,
const fuse_entry_t *entry,
const struct stat *st);
int fuse_dirents_add_linux(fuse_dirents_t *d,
const struct linux_dirent64 *de,
const linux_dirent64_t *de,
const uint64_t namelen);
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 fuse_entry_t *entry,
const struct stat *st);

View File

@ -2,7 +2,10 @@
#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;
int64_t off;

View File

@ -2,6 +2,7 @@
#include "moodycamel/blockingconcurrentqueue.h"
#include <algorithm>
#include <atomic>
#include <csignal>
#include <cstring>
@ -15,6 +16,7 @@
#include <syslog.h>
struct ThreadPoolTraits : public moodycamel::ConcurrentQueueDefaultTraits
{
static const int MAX_SEMA_SPINS = 1;
@ -29,17 +31,20 @@ private:
public:
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_ = {})
: _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_))
{
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_,
queue_depth_,
_name.c_str());
_max_queue_depth,
((_max_queue_depth != max_queue_depth_) ? " (adjusted)" : ""));
sigset_t oldset;
sigset_t newset;
@ -57,7 +62,8 @@ public:
if(rv != 0)
{
syslog(LOG_WARNING,
"threadpool: error spawning thread - %d (%s)",
"threadpool (%s): error spawning thread - %d (%s)",
_name.c_str(),
rv,
strerror(rv));
continue;
@ -78,9 +84,9 @@ public:
~ThreadPool()
{
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); };
for(std::size_t i = 0; i < _threads.size(); i++)
@ -114,6 +120,7 @@ private:
ThreadPool *btp = static_cast<ThreadPool*>(arg_);
ThreadPool::Func func;
ThreadPool::Queue &q = btp->_queue;
std::atomic<unsigned> &queue_depth = btp->_queue_depth;
moodycamel::ConsumerToken ctok(btp->_queue);
while(true)
@ -121,6 +128,8 @@ private:
q.wait_dequeue(ctok,func);
func();
queue_depth.fetch_sub(1,std::memory_order_release);
}
return NULL;
@ -146,7 +155,8 @@ public:
if(rv != 0)
{
syslog(LOG_WARNING,
"threadpool: error spawning thread - %d (%s)",
"threadpool (%s): error spawning thread - %d (%s)",
_name.c_str(),
rv,
strerror(rv));
return -rv;
@ -161,7 +171,7 @@ public:
}
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());
@ -201,7 +211,7 @@ public:
char name[16];
pthread_getname_np(t,name,sizeof(name));
syslog(LOG_DEBUG,
"threadpool: 1 thread removed from pool '%s' named '%s'",
"threadpool (%s): 1 thread removed named '%s'",
_name.c_str(),
name);
@ -238,28 +248,32 @@ public:
enqueue_work(moodycamel::ProducerToken &ptok_,
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);
ts.tv_nsec += 10;
}
_queue.enqueue(ptok_,f_);
_queue_depth.fetch_add(1,std::memory_order_release);
}
template<typename FuncType>
void
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);
ts.tv_nsec += 10;
}
_queue.enqueue(f_);
_queue_depth.fetch_add(1,std::memory_order_release);
}
template<typename FuncType>
@ -272,21 +286,24 @@ public:
auto promise = std::make_shared<Promise>();
auto future = promise->get_future();
auto work = [=]()
{
auto rv = f_();
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;
::nanosleep(&ts,NULL);
ts.tv_nsec += 10;
}
_queue.enqueue(work);
_queue_depth.fetch_add(1,std::memory_order_release);
return future;
}
@ -307,6 +324,8 @@ public:
private:
Queue _queue;
std::atomic<unsigned> _queue_depth;
unsigned const _max_queue_depth;
private:
std::string const _name;

View File

@ -305,7 +305,7 @@ fuse_dirents_add_plus(fuse_dirents_t *d_,
int
fuse_dirents_add_linux(fuse_dirents_t *d_,
const struct linux_dirent64 *dirent_,
const linux_dirent64_t *dirent_,
const uint64_t namelen_)
{
fuse_dirent_t *d;
@ -337,7 +337,7 @@ fuse_dirents_add_linux(fuse_dirents_t *d_,
int
fuse_dirents_add_linux_plus(fuse_dirents_t *d_,
const struct linux_dirent64 *dirent_,
const linux_dirent64_t *dirent_,
const uint64_t namelen_,
const fuse_entry_t *entry_,
const struct stat *st_)

View File

@ -490,10 +490,13 @@ fuse_session_loop_mt(struct fuse_session *se_,
if(process_thread_count > 0)
process_tp = std::make_shared<ThreadPool>(process_thread_count,
process_thread_queue_depth,
(process_thread_count *
process_thread_queue_depth),
"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)
{
for(auto i = 0; i < read_thread_count; i++)

View File

@ -19,20 +19,24 @@
#include "config.hpp"
#include "dirinfo.hpp"
#include "errno.hpp"
#include "fs_closedir.hpp"
#include "fs_close.hpp"
#include "fs_devid.hpp"
#include "fs_getdents64.hpp"
#include "fs_inode.hpp"
#include "fs_opendir.hpp"
#include "fs_open.hpp"
#include "fs_path.hpp"
#include "fs_readdir.hpp"
#include "hashset.hpp"
#include "scope_guard.hpp"
#include "ugid.hpp"
#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
{
static
inline
uint64_t
dirent_exact_namelen(const struct dirent *d_)
struct Error
{
private:
int _err;
public:
Error()
: _err(ENOENT)
{
#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
}
operator int()
{
return _err;
}
Error&
operator=(int v_)
{
if(_err != 0)
_err = v_;
return *this;
}
};
static
inline
int
readdir(std::string basepath_,
HashSet &names_,
std::mutex &names_mutex_,
fuse_dirents_t *buf_,
std::mutex &dirents_mutex_)
std::mutex &mutex_)
{
int rv;
int err;
DIR *dh;
int dfd;
dev_t dev;
std::string filepath;
dh = fs::opendir(basepath_);
if(dh == NULL)
return -errno;
dfd = fs::open_dir_ro(basepath_);
if(dfd == -1)
return errno;
dev = fs::devid(dh);
DEFER{ fs::close(dfd); };
dev = fs::devid(dfd);
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;
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)
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,
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;
return ENOMEM;
}
}
err = -ENOMEM;
}
fs::closedir(dh);
return err;
return 0;
}
static
std::vector<int>
int
concurrent_readdir(ThreadPool &tp_,
const Branches::CPtr &branches_,
const char *dirname_,
@ -125,11 +148,10 @@ namespace l
gid_t const gid_)
{
HashSet names;
std::mutex names_mutex;
std::mutex dirents_mutex;
std::vector<int> rv;
std::mutex mutex;
std::vector<std::future<int>> futures;
futures.reserve(branches_->size());
for(auto const &branch : *branches_)
{
auto func = [&,dirname_,buf_,uid_,gid_]()
@ -139,7 +161,7 @@ namespace l
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);
@ -147,26 +169,11 @@ namespace l
futures.emplace_back(std::move(rv));
}
Error error;
for(auto &future : futures)
rv.push_back(future.get());
error = 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;
return rvs_[0];
return -error;
}
static
@ -178,13 +185,9 @@ namespace l
uid_t const uid_,
gid_t const gid_)
{
std::vector<int> rvs;
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_);
}
}

View File

@ -29,7 +29,8 @@ namespace FUSE
class ReadDirCOR final : public FUSE::ReadDirBase
{
public:
ReadDirCOR(unsigned concurrency);
ReadDirCOR(unsigned concurrency,
unsigned max_queue_depth);
~ReadDirCOR();
int operator()(fuse_file_info_t const *ffi,

View File

@ -28,13 +28,15 @@
#include "fs_readdir.hpp"
#include "fs_stat.hpp"
#include "hashset.hpp"
#include "scope_guard.hpp"
#include "ugid.hpp"
#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
{
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
inline
uint64_t
dirent_exact_namelen(const struct dirent *d_)
{
@ -62,25 +96,31 @@ namespace l
static
inline
std::vector<std::future<DIR*>>
std::vector<std::future<DirRV>>
opendir(ThreadPool &tp_,
const Branches::CPtr &branches_,
char const *dirname_,
uid_t const uid_,
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_)
{
auto func = [&branch,dirname_,uid_,gid_]()
{
DirRV rv;
std::string basepath;
ugid::Set const ugid(uid_,gid_);
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);
@ -94,29 +134,31 @@ namespace l
static
inline
int
readdir(std::vector<std::future<DIR*>> &dh_futures_,
readdir(std::vector<std::future<DirRV>> &dh_futures_,
char const *dirname_,
fuse_dirents_t *buf_)
{
int err;
Error error;
HashSet names;
std::string fullpath;
err = 0;
for(auto &dh_future : dh_futures_)
{
int rv;
DIR *dh;
dev_t dev;
DirRV dirrv;
dh = dh_future.get();
if(dh == NULL)
dirrv = dh_future.get();
error = dirrv.err;
if(dirrv.dir == NULL)
continue;
dev = fs::devid(dh);
DEFER { fs::closedir(dirrv.dir); };
dev = fs::devid(dirrv.dir);
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;
@ -136,13 +178,11 @@ namespace l
if(rv == 0)
continue;
err = -ENOMEM;
error = ENOMEM;
}
}
fs::closedir(dh);
}
return err;
return -error;
}
static
@ -156,12 +196,12 @@ namespace l
gid_t const gid_)
{
int rv;
std::vector<std::future<DIR*>> dh_futures;
std::vector<std::future<DirRV>> futures;
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;
}

View File

@ -28,7 +28,8 @@ namespace FUSE
class ReadDirCOSR final : public FUSE::ReadDirBase
{
public:
ReadDirCOSR(unsigned concurrency);
ReadDirCOSR(unsigned concurrency,
unsigned max_queue_depth);
~ReadDirCOSR();
int operator()(fuse_file_info_t const *ffi,

View File

@ -28,6 +28,7 @@
#include <cstdlib>
#include <set>
#define DEFAULT_MAX_QUEUE_DEPTH 3
namespace l
{
@ -35,13 +36,20 @@ namespace l
void
read_cfg(std::string const str_,
std::string &type_,
int &concurrency_)
unsigned &concurrency_,
unsigned &max_queue_depth_)
{
char type[16];
int concurrency;
int max_queue_depth;
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)
concurrency = std::thread::hardware_concurrency();
@ -51,22 +59,29 @@ namespace l
if(concurrency == 0)
concurrency = 1;
if(max_queue_depth == 0)
max_queue_depth = DEFAULT_MAX_QUEUE_DEPTH;
max_queue_depth *= concurrency;
type_ = type;
concurrency_ = concurrency;
max_queue_depth_ = max_queue_depth;
}
}
bool
FUSE::ReadDirFactory::valid(std::string const str_)
{
int concurrency;
unsigned concurrency;
unsigned max_queue_depth;
std::string type;
static const std::set<std::string> types =
{
"seq", "cosr", "cor"
};
l::read_cfg(str_,type,concurrency);
l::read_cfg(str_,type,concurrency,max_queue_depth);
if(types.find(type) == types.end())
return false;
@ -79,20 +94,21 @@ FUSE::ReadDirFactory::valid(std::string const str_)
std::shared_ptr<FUSE::ReadDirBase>
FUSE::ReadDirFactory::make(std::string const str_)
{
int concurrency;
unsigned concurrency;
unsigned max_queue_depth;
std::string type;
if(!valid(str_))
return {};
l::read_cfg(str_,type,concurrency);
l::read_cfg(str_,type,concurrency,max_queue_depth);
if(type == "seq")
return std::make_shared<FUSE::ReadDirSeq>();
if(type == "cosr")
return std::make_shared<FUSE::ReadDirCOSR>(concurrency);
return std::make_shared<FUSE::ReadDirCOSR>(concurrency,max_queue_depth);
if(type == "cor")
return std::make_shared<FUSE::ReadDirCOR>(concurrency);
return std::make_shared<FUSE::ReadDirCOR>(concurrency,max_queue_depth);
return {};
}

View File

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

View File

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

View File

@ -1,7 +1,7 @@
/*
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
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.
*/
#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_,
FUSE::readdir_plus(const fuse_file_info_t *ffi_,
fuse_dirents_t *buf_)
{
return -ENOTSUP;
}
}

View File

@ -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
purpose with or without fee is hereby granted, provided that the above
@ -21,7 +21,6 @@
namespace FUSE
{
int
readdir_plus(const fuse_file_info_t *ffi,
int readdir_plus(fuse_file_info_t const *ffi,
fuse_dirents_t *buf);
}

View File

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

View File

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

View File

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

View File

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

View File

@ -31,17 +31,44 @@
#include "fs_readdir.hpp"
#include "fs_stat.hpp"
#include "hashset.hpp"
#include "scope_guard.hpp"
#include "ugid.hpp"
#include "fuse.h"
#include "fuse_dirents.h"
#include <string>
#include <vector>
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
uint64_t
dirent_exact_namelen(const struct dirent *d_)
@ -61,13 +88,14 @@ namespace l
const char *dirname_,
fuse_dirents_t *buf_)
{
Error error;
HashSet names;
std::string basepath;
std::string fullpath;
fuse_dirents_reset(buf_);
for(const auto &branch : *branches_)
for(auto const &branch : *branches_)
{
int rv;
DIR *dh;
@ -75,14 +103,18 @@ namespace l
basepath = fs::path::make(branch.path,dirname_);
errno = 0;
dh = fs::opendir(basepath);
error = errno;
if(!dh)
continue;
DEFER{ fs::closedir(dh); };
dev = fs::devid(dh);
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;
@ -100,13 +132,11 @@ namespace l
rv = fuse_dirents_add(buf_,de,namelen);
if(rv)
return (fs::closedir(dh),-ENOMEM);
return -ENOMEM;
}
}
fs::closedir(dh);
}
return 0;
return -error;
}
}

369
src/scope_guard.hpp Normal file
View 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