diff --git a/libfuse/include/fuse_dirents.h b/libfuse/include/fuse_dirents.h index 25abf198..e5b14095 100644 --- a/libfuse/include/fuse_dirents.h +++ b/libfuse/include/fuse_dirents.h @@ -63,14 +63,14 @@ int fuse_dirents_add_plus(fuse_dirents_t *d, const uint64_t namelen, const fuse_entry_t *entry, const struct stat *st); -int fuse_dirents_add_linux(fuse_dirents_t *d, - const struct linux_dirent64 *de, - const uint64_t namelen); -int fuse_dirents_add_linux_plus(fuse_dirents_t *d, - const struct linux_dirent64 *de, - const uint64_t namelen, - const fuse_entry_t *entry, - const struct stat *st); +int fuse_dirents_add_linux(fuse_dirents_t *d, + const linux_dirent64_t *de, + const uint64_t namelen); +int fuse_dirents_add_linux_plus(fuse_dirents_t *d, + const linux_dirent64_t *de, + const uint64_t namelen, + const fuse_entry_t *entry, + const struct stat *st); void *fuse_dirents_find(fuse_dirents_t *d, const uint64_t ino); diff --git a/libfuse/include/linux_dirent64.h b/libfuse/include/linux_dirent64.h index e8a9f78c..7550e746 100644 --- a/libfuse/include/linux_dirent64.h +++ b/libfuse/include/linux_dirent64.h @@ -2,7 +2,10 @@ #include -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; diff --git a/libfuse/include/thread_pool.hpp b/libfuse/include/thread_pool.hpp index 1a2aa539..864950c7 100644 --- a/libfuse/include/thread_pool.hpp +++ b/libfuse/include/thread_pool.hpp @@ -2,6 +2,7 @@ #include "moodycamel/blockingconcurrentqueue.h" +#include #include #include #include @@ -15,6 +16,7 @@ #include + 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(arg_); ThreadPool::Func func; ThreadPool::Queue &q = btp->_queue; + std::atomic &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 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 @@ -272,21 +286,24 @@ public: auto promise = std::make_shared(); auto future = promise->get_future(); - auto work = [=]() + + 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 _queue_depth; + unsigned const _max_queue_depth; private: std::string const _name; diff --git a/libfuse/lib/fuse_dirents.c b/libfuse/lib/fuse_dirents.c index c8e9fc66..8281459a 100644 --- a/libfuse/lib/fuse_dirents.c +++ b/libfuse/lib/fuse_dirents.c @@ -304,9 +304,9 @@ fuse_dirents_add_plus(fuse_dirents_t *d_, } int -fuse_dirents_add_linux(fuse_dirents_t *d_, - const struct linux_dirent64 *dirent_, - const uint64_t namelen_) +fuse_dirents_add_linux(fuse_dirents_t *d_, + const linux_dirent64_t *dirent_, + const uint64_t namelen_) { fuse_dirent_t *d; @@ -336,11 +336,11 @@ fuse_dirents_add_linux(fuse_dirents_t *d_, } int -fuse_dirents_add_linux_plus(fuse_dirents_t *d_, - const struct linux_dirent64 *dirent_, - const uint64_t namelen_, - const fuse_entry_t *entry_, - const struct stat *st_) +fuse_dirents_add_linux_plus(fuse_dirents_t *d_, + const linux_dirent64_t *dirent_, + const uint64_t namelen_, + const fuse_entry_t *entry_, + const struct stat *st_) { fuse_direntplus_t *d; diff --git a/libfuse/lib/fuse_loop.cpp b/libfuse/lib/fuse_loop.cpp index b702f338..4245bbee 100644 --- a/libfuse/lib/fuse_loop.cpp +++ b/libfuse/lib/fuse_loop.cpp @@ -490,10 +490,13 @@ fuse_session_loop_mt(struct fuse_session *se_, if(process_thread_count > 0) process_tp = std::make_shared(process_thread_count, - process_thread_queue_depth, + (process_thread_count * + process_thread_queue_depth), "fuse.process"); - read_tp = std::make_unique(read_thread_count,1,"fuse.read"); + read_tp = std::make_unique(read_thread_count, + read_thread_count, + "fuse.read"); if(process_tp) { for(auto i = 0; i < read_thread_count; i++) diff --git a/src/fuse_readdir_cor.cpp b/src/fuse_readdir_cor.cpp index c6c1526b..2b96c0d2 100644 --- a/src/fuse_readdir_cor.cpp +++ b/src/fuse_readdir_cor.cpp @@ -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 { -#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 - } + private: + int _err; + + public: + Error() + : _err(ENOENT) + { + } + + 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(;;) { - std::uint64_t namelen; + long nread; + char buf[32 * 1024]; - namelen = l::dirent_exact_namelen(de); + nread = fs::getdents_64(dfd,buf,sizeof(buf)); + if(nread == -1) + return errno; + if(nread == 0) + break; - { - std::lock_guard lk(names_mutex_); - rv = names_.put(de->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), - dev, - de->d_ino); + linux_dirent64_t *d; + std::lock_guard lk(mutex_); + for(long pos = 0; pos < nread; pos += d->reclen) + { + std::uint64_t namelen; - { - std::lock_guard lk(dirents_mutex_); - rv = fuse_dirents_add(buf_,de,namelen); - if(rv == 0) - continue; - } + d = (linux_dirent64_t*)&buf[pos]; - err = -ENOMEM; - } + namelen = DIRENT_NAMELEN(d); + + rv = names_.put(d->name,namelen); + if(rv == 0) + continue; + + filepath = fs::path::make(basepath_,d->name); + d->ino = fs::inode::calc(filepath, + DTTOIF(d->type), + dev, + d->ino); - fs::closedir(dh); + rv = fuse_dirents_add_linux(buf_,d,namelen); + if(rv >= 0) + continue; - return err; + return ENOMEM; + } + } + + return 0; } static - std::vector + 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 rv; + std::mutex mutex; std::vector> 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 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 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_); } } diff --git a/src/fuse_readdir_cor.hpp b/src/fuse_readdir_cor.hpp index 6da212fc..0f075cad 100644 --- a/src/fuse_readdir_cor.hpp +++ b/src/fuse_readdir_cor.hpp @@ -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, diff --git a/src/fuse_readdir_cosr.cpp b/src/fuse_readdir_cosr.cpp index 92f75ac8..6be0a97e 100644 --- a/src/fuse_readdir_cosr.cpp +++ b/src/fuse_readdir_cosr.cpp @@ -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::vector> opendir(ThreadPool &tp_, const Branches::CPtr &branches_, char const *dirname_, uid_t const uid_, gid_t const gid_) { - std::vector> futures; + std::vector> 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> &dh_futures_, - char const *dirname_, - fuse_dirents_t *buf_) + readdir(std::vector> &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> dh_futures; + std::vector> 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; } diff --git a/src/fuse_readdir_cosr.hpp b/src/fuse_readdir_cosr.hpp index 8149d36d..c2596385 100644 --- a/src/fuse_readdir_cosr.hpp +++ b/src/fuse_readdir_cosr.hpp @@ -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, diff --git a/src/fuse_readdir_factory.cpp b/src/fuse_readdir_factory.cpp index 3c784f33..38609930 100644 --- a/src/fuse_readdir_factory.cpp +++ b/src/fuse_readdir_factory.cpp @@ -28,6 +28,7 @@ #include #include +#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; - type_ = type; - concurrency_ = concurrency; + 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 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::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(); if(type == "cosr") - return std::make_shared(concurrency); + return std::make_shared(concurrency,max_queue_depth); if(type == "cor") - return std::make_shared(concurrency); + return std::make_shared(concurrency,max_queue_depth); return {}; } diff --git a/src/fuse_readdir_linux.cpp b/src/fuse_readdir_linux.cpp deleted file mode 100644 index 6f857b1b..00000000 --- a/src/fuse_readdir_linux.cpp +++ /dev/null @@ -1,135 +0,0 @@ -/* - Copyright (c) 2020, Antonio SJ Musumeci - - 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 -#include - -#include - -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_); - } -} diff --git a/src/fuse_readdir_linux.hpp b/src/fuse_readdir_linux.hpp deleted file mode 100644 index 26cfff6a..00000000 --- a/src/fuse_readdir_linux.hpp +++ /dev/null @@ -1,34 +0,0 @@ -/* - ISC License - - Copyright (c) 2020, Antonio SJ Musumeci - - 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 - - -namespace FUSE -{ - int - readdir_linux(const Branches::CPtr &branches, - const char *dirname, - fuse_dirents_t *buf); -} diff --git a/src/fuse_readdir_plus.cpp b/src/fuse_readdir_plus.cpp index 3b913fba..8c49c11b 100644 --- a/src/fuse_readdir_plus.cpp +++ b/src/fuse_readdir_plus.cpp @@ -1,7 +1,7 @@ /* ISC License - Copyright (c) 2020, Antonio SJ Musumeci + Copyright (c) 2023, Antonio SJ Musumeci 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 -namespace FUSE +int +FUSE::readdir_plus(const fuse_file_info_t *ffi_, + fuse_dirents_t *buf_) { - int - readdir_plus(const fuse_file_info_t *ffi_, - fuse_dirents_t *buf_) - { - return -ENOTSUP; - } + return -ENOTSUP; } diff --git a/src/fuse_readdir_plus.hpp b/src/fuse_readdir_plus.hpp index 8e298e61..c60ea677 100644 --- a/src/fuse_readdir_plus.hpp +++ b/src/fuse_readdir_plus.hpp @@ -1,5 +1,5 @@ /* - Copyright (c) 2016, Antonio SJ Musumeci + Copyright (c) 2023, Antonio SJ Musumeci 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, - fuse_dirents_t *buf); + int readdir_plus(fuse_file_info_t const *ffi, + fuse_dirents_t *buf); } diff --git a/src/fuse_readdir_plus_linux.cpp b/src/fuse_readdir_plus_linux.cpp deleted file mode 100644 index 880f158b..00000000 --- a/src/fuse_readdir_plus_linux.cpp +++ /dev/null @@ -1,151 +0,0 @@ -/* - Copyright (c) 2019, Antonio SJ Musumeci - - 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 -#include - -#include - -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_); - } -} diff --git a/src/fuse_readdir_plus_linux.hpp b/src/fuse_readdir_plus_linux.hpp deleted file mode 100644 index a0bca539..00000000 --- a/src/fuse_readdir_plus_linux.hpp +++ /dev/null @@ -1,36 +0,0 @@ -/* - ISC License - - Copyright (c) 2020, Antonio SJ Musumeci - - 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 - - -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); -} diff --git a/src/fuse_readdir_plus_posix.cpp b/src/fuse_readdir_plus_posix.cpp deleted file mode 100644 index 0d974e6e..00000000 --- a/src/fuse_readdir_plus_posix.cpp +++ /dev/null @@ -1,142 +0,0 @@ -/* - Copyright (c) 2019, Antonio SJ Musumeci - - 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 -#include - -#include - -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_); - } -} diff --git a/src/fuse_readdir_plus_posix.hpp b/src/fuse_readdir_plus_posix.hpp deleted file mode 100644 index 910cab5f..00000000 --- a/src/fuse_readdir_plus_posix.hpp +++ /dev/null @@ -1,36 +0,0 @@ -/* - ISC License - - Copyright (c) 2020, Antonio SJ Musumeci - - 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 - - -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); -} diff --git a/src/fuse_readdir_seq.cpp b/src/fuse_readdir_seq.cpp index f1811c28..9aa0c6f0 100644 --- a/src/fuse_readdir_seq.cpp +++ b/src/fuse_readdir_seq.cpp @@ -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 -#include 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; } } diff --git a/src/scope_guard.hpp b/src/scope_guard.hpp new file mode 100644 index 00000000..36ee88f9 --- /dev/null +++ b/src/scope_guard.hpp @@ -0,0 +1,369 @@ +// _____ _____ _ _____ +// / ____| / ____| | | / ____|_ _ +// | (___ ___ ___ _ __ ___ | | __ _ _ __ _ _ __ __| | | | _| |_ _| |_ +// \___ \ / __/ _ \| '_ \ / _ \ | | |_ | | | |/ _` | '__/ _` | | | |_ _|_ _| +// ____) | (_| (_) | |_) | __/ | |__| | |_| | (_| | | | (_| | | |____|_| |_| +// |_____/ \___\___/| .__/ \___| \_____|\__,_|\__,_|_| \__,_| \_____| +// | | https://github.com/Neargye/scope_guard +// |_| version 0.9.1 +// +// Licensed under the MIT License . +// SPDX-License-Identifier: MIT +// Copyright (c) 2018 - 2021 Daniil Goncharov . +// +// 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 +#if (defined(_MSC_VER) && _MSC_VER >= 1900) || ((defined(__clang__) || defined(__GNUC__)) && __cplusplus >= 201700L) +#include +#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::type&&>(__VA_ARGS__) +#define NEARGYE_FWD(...) static_cast(__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(static_cast(static_cast(_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(*(reinterpret_cast(static_cast(static_cast(__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 +struct is_noarg_returns_void_action + : std::false_type {}; + +template +struct is_noarg_returns_void_action())())> + : std::true_type {}; + +template ::value> +struct is_nothrow_invocable_action + : std::false_type {}; + +template +struct is_nothrow_invocable_action + : std::integral_constant())())> {}; + +template +class scope_guard { + using A = typename std::decay::type; + + static_assert(is_noarg_returns_void_action::value, + "scope_guard requires no-argument action, that returns void."); + static_assert(std::is_same::value || std::is_same::value || std::is_same::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::value, + "scope_guard requires noexcept invocable action."); +#endif +#if defined(SCOPE_GUARD_NO_THROW_CONSTRUCTIBLE) + static_assert(std::is_nothrow_move_constructible::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::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::value) + : policy_{true}, + action_{NEARGYE_MOV(action)} {} + + void dismiss() noexcept { + policy_.dismiss(); + } + + ~scope_guard() NEARGYE_NOEXCEPT(is_nothrow_invocable_action::value) { + if (policy_.should_execute()) { + NEARGYE_TRY + action_(); + NEARGYE_CATCH + } + } +}; + +template +using scope_exit = scope_guard; + +template ::value, int>::type = 0> +NEARGYE_NODISCARD scope_exit make_scope_exit(F&& action) noexcept(noexcept(scope_exit{NEARGYE_FWD(action)})) { + return scope_exit{NEARGYE_FWD(action)}; +} + +template +using scope_fail = scope_guard; + +template ::value, int>::type = 0> +NEARGYE_NODISCARD scope_fail make_scope_fail(F&& action) noexcept(noexcept(scope_fail{NEARGYE_FWD(action)})) { + return scope_fail{NEARGYE_FWD(action)}; +} + +template +using scope_success = scope_guard; + +template ::value, int>::type = 0> +NEARGYE_NODISCARD scope_success make_scope_success(F&& action) noexcept(noexcept(scope_success{NEARGYE_FWD(action)})) { + return scope_success{NEARGYE_FWD(action)}; +} + +struct scope_exit_tag {}; + +template ::value, int>::type = 0> +scope_exit operator<<(scope_exit_tag, F&& action) noexcept(noexcept(scope_exit{NEARGYE_FWD(action)})) { + return scope_exit{NEARGYE_FWD(action)}; +} + +struct scope_fail_tag {}; + +template ::value, int>::type = 0> +scope_fail operator<<(scope_fail_tag, F&& action) noexcept(noexcept(scope_fail{NEARGYE_FWD(action)})) { + return scope_fail{NEARGYE_FWD(action)}; +} + +struct scope_success_tag {}; + +template ::value, int>::type = 0> +scope_success operator<<(scope_success_tag, F&& action) noexcept(noexcept(scope_success{NEARGYE_FWD(action)})) { + return scope_success{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