mirror of https://github.com/trapexit/mergerfs.git
Browse Source
Merge pull request #1262 from trapexit/readdir-errs
Merge pull request #1262 from trapexit/readdir-errs
Rework thread pool queue depth implpull/1263/head
trapexit
1 year ago
committed by
GitHub
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 662 additions and 715 deletions
-
4libfuse/include/fuse_dirents.h
-
5libfuse/include/linux_dirent64.h
-
73libfuse/include/thread_pool.hpp
-
4libfuse/lib/fuse_dirents.c
-
7libfuse/lib/fuse_loop.cpp
-
145src/fuse_readdir_cor.cpp
-
3src/fuse_readdir_cor.hpp
-
82src/fuse_readdir_cosr.cpp
-
3src/fuse_readdir_cosr.hpp
-
32src/fuse_readdir_factory.cpp
-
135src/fuse_readdir_linux.cpp
-
34src/fuse_readdir_linux.hpp
-
15src/fuse_readdir_plus.cpp
-
5src/fuse_readdir_plus.hpp
-
151src/fuse_readdir_plus_linux.cpp
-
36src/fuse_readdir_plus_linux.hpp
-
142src/fuse_readdir_plus_posix.cpp
-
36src/fuse_readdir_plus_posix.hpp
-
44src/fuse_readdir_seq.cpp
-
369src/scope_guard.hpp
@ -1,135 +0,0 @@ |
|||
/*
|
|||
Copyright (c) 2020, Antonio SJ Musumeci <trapexit@spawn.link> |
|||
|
|||
Permission to use, copy, modify, and/or distribute this software for any |
|||
purpose with or without fee is hereby granted, provided that the above |
|||
copyright notice and this permission notice appear in all copies. |
|||
|
|||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
|||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
|||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
|||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
|||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
|||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
|||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
|||
*/ |
|||
|
|||
#include "branches.hpp"
|
|||
#include "errno.hpp"
|
|||
#include "fs_close.hpp"
|
|||
#include "fs_devid.hpp"
|
|||
#include "fs_getdents64.hpp"
|
|||
#include "fs_inode.hpp"
|
|||
#include "fs_open.hpp"
|
|||
#include "fs_path.hpp"
|
|||
#include "fs_stat.hpp"
|
|||
#include "hashset.hpp"
|
|||
#include "linux_dirent64.h"
|
|||
#include "mempools.hpp"
|
|||
|
|||
#include "fuse.h"
|
|||
#include "fuse_dirents.h"
|
|||
|
|||
#include <string>
|
|||
#include <vector>
|
|||
|
|||
#include <stddef.h>
|
|||
|
|||
using std::string; |
|||
using std::vector; |
|||
|
|||
namespace l |
|||
{ |
|||
static |
|||
int |
|||
close_free_ret_enomem(int fd_, |
|||
void *buf_) |
|||
{ |
|||
fs::close(fd_); |
|||
g_DENTS_BUF_POOL.free(buf_); |
|||
return -ENOMEM; |
|||
} |
|||
|
|||
static |
|||
int |
|||
readdir(const Branches::CPtr &branches_, |
|||
const char *dirname_, |
|||
fuse_dirents_t *buf_) |
|||
{ |
|||
int rv; |
|||
dev_t dev; |
|||
char *buf; |
|||
HashSet names; |
|||
string basepath; |
|||
string fullpath; |
|||
uint64_t namelen; |
|||
struct linux_dirent64 *d; |
|||
|
|||
fuse_dirents_reset(buf_); |
|||
|
|||
buf = (char*)g_DENTS_BUF_POOL.alloc(); |
|||
if(buf == NULL) |
|||
return -ENOMEM; |
|||
|
|||
for(const auto &branch : *branches_) |
|||
{ |
|||
int dirfd; |
|||
int64_t nread; |
|||
|
|||
basepath = fs::path::make(branch.path,dirname_); |
|||
|
|||
dirfd = fs::open_dir_ro(basepath); |
|||
if(dirfd == -1) |
|||
continue; |
|||
|
|||
dev = fs::devid(dirfd); |
|||
|
|||
for(;;) |
|||
{ |
|||
nread = fs::getdents_64(dirfd,buf,g_DENTS_BUF_POOL.size()); |
|||
if(nread == -1) |
|||
break; |
|||
if(nread == 0) |
|||
break; |
|||
|
|||
for(int64_t pos = 0; pos < nread; pos += d->reclen) |
|||
{ |
|||
d = (struct linux_dirent64*)(buf + pos); |
|||
namelen = strlen(d->name); |
|||
|
|||
rv = names.put(d->name,namelen); |
|||
if(rv == 0) |
|||
continue; |
|||
|
|||
fullpath = fs::path::make(dirname_,d->name); |
|||
d->ino = fs::inode::calc(fullpath.c_str(), |
|||
fullpath.size(), |
|||
DTTOIF(d->type), |
|||
dev, |
|||
d->ino); |
|||
|
|||
rv = fuse_dirents_add_linux(buf_,d,namelen); |
|||
if(rv) |
|||
return close_free_ret_enomem(dirfd,buf); |
|||
} |
|||
} |
|||
|
|||
fs::close(dirfd); |
|||
} |
|||
|
|||
g_DENTS_BUF_POOL.free(buf); |
|||
|
|||
return 0; |
|||
} |
|||
} |
|||
|
|||
namespace FUSE |
|||
{ |
|||
int |
|||
readdir_linux(const Branches::CPtr &branches_, |
|||
const char *dirname_, |
|||
fuse_dirents_t *buf_) |
|||
{ |
|||
return l::readdir(branches_,dirname_,buf_); |
|||
} |
|||
} |
@ -1,34 +0,0 @@ |
|||
/*
|
|||
ISC License |
|||
|
|||
Copyright (c) 2020, Antonio SJ Musumeci <trapexit@spawn.link> |
|||
|
|||
Permission to use, copy, modify, and/or distribute this software for any |
|||
purpose with or without fee is hereby granted, provided that the above |
|||
copyright notice and this permission notice appear in all copies. |
|||
|
|||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
|||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
|||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
|||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
|||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
|||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
|||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
|||
*/ |
|||
|
|||
#pragma once
|
|||
|
|||
#include "branches.hpp"
|
|||
|
|||
#include "fuse.h"
|
|||
|
|||
#include <cstdint>
|
|||
|
|||
|
|||
namespace FUSE |
|||
{ |
|||
int |
|||
readdir_linux(const Branches::CPtr &branches, |
|||
const char *dirname, |
|||
fuse_dirents_t *buf); |
|||
} |
@ -1,151 +0,0 @@ |
|||
/*
|
|||
Copyright (c) 2019, Antonio SJ Musumeci <trapexit@spawn.link> |
|||
|
|||
Permission to use, copy, modify, and/or distribute this software for any |
|||
purpose with or without fee is hereby granted, provided that the above |
|||
copyright notice and this permission notice appear in all copies. |
|||
|
|||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
|||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
|||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
|||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
|||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
|||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
|||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
|||
*/ |
|||
|
|||
#include "branches.hpp"
|
|||
#include "errno.hpp"
|
|||
#include "fs_close.hpp"
|
|||
#include "fs_devid.hpp"
|
|||
#include "fs_fstatat.hpp"
|
|||
#include "fs_getdents64.hpp"
|
|||
#include "fs_inode.hpp"
|
|||
#include "fs_open.hpp"
|
|||
#include "fs_path.hpp"
|
|||
#include "fs_stat.hpp"
|
|||
#include "hashset.hpp"
|
|||
#include "linux_dirent64.h"
|
|||
#include "mempools.hpp"
|
|||
|
|||
#include "fuse.h"
|
|||
#include "fuse_dirents.h"
|
|||
|
|||
#include <string>
|
|||
#include <vector>
|
|||
|
|||
#include <stddef.h>
|
|||
|
|||
using std::string; |
|||
using std::vector; |
|||
|
|||
namespace l |
|||
{ |
|||
static |
|||
int |
|||
close_free_ret_enomem(int fd_, |
|||
void *buf_) |
|||
{ |
|||
fs::close(fd_); |
|||
g_DENTS_BUF_POOL.free(buf_); |
|||
return -ENOMEM; |
|||
} |
|||
|
|||
static |
|||
int |
|||
readdir_plus(const Branches::CPtr &branches_, |
|||
const char *dirname_, |
|||
const uint64_t entry_timeout_, |
|||
const uint64_t attr_timeout_, |
|||
fuse_dirents_t *buf_) |
|||
{ |
|||
int rv; |
|||
dev_t dev; |
|||
char *buf; |
|||
HashSet names; |
|||
string basepath; |
|||
string fullpath; |
|||
uint64_t namelen; |
|||
struct stat st; |
|||
fuse_entry_t entry; |
|||
struct linux_dirent64 *d; |
|||
|
|||
fuse_dirents_reset(buf_); |
|||
|
|||
buf = (char*)g_DENTS_BUF_POOL.alloc(); |
|||
|
|||
entry.nodeid = 0; |
|||
entry.generation = 0; |
|||
entry.entry_valid = entry_timeout_; |
|||
entry.attr_valid = attr_timeout_; |
|||
entry.entry_valid_nsec = 0; |
|||
entry.attr_valid_nsec = 0; |
|||
for(auto &branch : *branches_) |
|||
{ |
|||
int dirfd; |
|||
int64_t nread; |
|||
|
|||
basepath = fs::path::make(branch.path,dirname_); |
|||
|
|||
dirfd = fs::open_dir_ro(basepath); |
|||
if(dirfd == -1) |
|||
continue; |
|||
dev = fs::devid(dirfd); |
|||
|
|||
for(;;) |
|||
{ |
|||
nread = fs::getdents_64(dirfd,buf,g_DENTS_BUF_POOL.size()); |
|||
if(nread == -1) |
|||
break; |
|||
if(nread == 0) |
|||
break; |
|||
|
|||
for(int64_t pos = 0; pos < nread; pos += d->reclen) |
|||
{ |
|||
d = (struct linux_dirent64*)(buf + pos); |
|||
namelen = (strlen(d->name) + 1); |
|||
|
|||
rv = names.put(d->name,namelen); |
|||
if(rv == 0) |
|||
continue; |
|||
|
|||
rv = fs::fstatat_nofollow(dirfd,d->name,&st); |
|||
if(rv == -1) |
|||
{ |
|||
memset(&st,0,sizeof(st)); |
|||
st.st_ino = d->ino; |
|||
st.st_dev = dev; |
|||
st.st_mode = DTTOIF(d->type); |
|||
} |
|||
|
|||
fullpath = fs::path::make(dirname_,d->name); |
|||
fs::inode::calc(fullpath,&st); |
|||
d->ino = st.st_ino; |
|||
|
|||
rv = fuse_dirents_add_linux_plus(buf_,d,namelen,&entry,&st); |
|||
if(rv) |
|||
return close_free_ret_enomem(dirfd,buf); |
|||
} |
|||
} |
|||
|
|||
fs::close(dirfd); |
|||
} |
|||
|
|||
g_DENTS_BUF_POOL.free(buf); |
|||
|
|||
return 0; |
|||
} |
|||
} |
|||
|
|||
namespace FUSE |
|||
{ |
|||
int |
|||
readdir_plus_linux(const Branches::CPtr &branches_, |
|||
const char *dirname_, |
|||
const uint64_t entry_timeout_, |
|||
const uint64_t attr_timeout_, |
|||
fuse_dirents_t *buf_) |
|||
{ |
|||
return l::readdir_plus(branches_,dirname_,entry_timeout_,attr_timeout_,buf_); |
|||
} |
|||
} |
@ -1,36 +0,0 @@ |
|||
/*
|
|||
ISC License |
|||
|
|||
Copyright (c) 2020, Antonio SJ Musumeci <trapexit@spawn.link> |
|||
|
|||
Permission to use, copy, modify, and/or distribute this software for any |
|||
purpose with or without fee is hereby granted, provided that the above |
|||
copyright notice and this permission notice appear in all copies. |
|||
|
|||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
|||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
|||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
|||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
|||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
|||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
|||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
|||
*/ |
|||
|
|||
#pragma once
|
|||
|
|||
#include "branches.hpp"
|
|||
|
|||
#include "fuse.h"
|
|||
|
|||
#include <cstdint>
|
|||
|
|||
|
|||
namespace FUSE |
|||
{ |
|||
int |
|||
readdir_plus_linux(const Branches::CPtr &branches, |
|||
const char *dirname, |
|||
const uint64_t entry_timeout, |
|||
const uint64_t attr_timeout, |
|||
fuse_dirents_t *buf); |
|||
} |
@ -1,142 +0,0 @@ |
|||
/*
|
|||
Copyright (c) 2019, Antonio SJ Musumeci <trapexit@spawn.link> |
|||
|
|||
Permission to use, copy, modify, and/or distribute this software for any |
|||
purpose with or without fee is hereby granted, provided that the above |
|||
copyright notice and this permission notice appear in all copies. |
|||
|
|||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
|||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
|||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
|||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
|||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
|||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
|||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
|||
*/ |
|||
|
|||
#define _DEFAULT_SOURCE
|
|||
|
|||
#include "branches.hpp"
|
|||
#include "errno.hpp"
|
|||
#include "fs_closedir.hpp"
|
|||
#include "fs_devid.hpp"
|
|||
#include "fs_dirfd.hpp"
|
|||
#include "fs_fstatat.hpp"
|
|||
#include "fs_inode.hpp"
|
|||
#include "fs_opendir.hpp"
|
|||
#include "fs_path.hpp"
|
|||
#include "fs_readdir.hpp"
|
|||
#include "fs_stat.hpp"
|
|||
#include "hashset.hpp"
|
|||
|
|||
#include "fuse.h"
|
|||
#include "fuse_dirents.h"
|
|||
|
|||
#include <string>
|
|||
#include <vector>
|
|||
|
|||
#include <dirent.h>
|
|||
|
|||
using std::string; |
|||
using std::vector; |
|||
|
|||
namespace l |
|||
{ |
|||
static |
|||
uint64_t |
|||
dirent_exact_namelen(const struct dirent *d_) |
|||
{ |
|||
#ifdef _D_EXACT_NAMLEN
|
|||
return _D_EXACT_NAMLEN(d_); |
|||
#elif defined _DIRENT_HAVE_D_NAMLEN
|
|||
return d_->d_namlen; |
|||
#else
|
|||
return strlen(d_->d_name); |
|||
#endif
|
|||
} |
|||
|
|||
static |
|||
int |
|||
readdir_plus(const Branches::CPtr &branches_, |
|||
const char *dirname_, |
|||
const uint64_t entry_timeout_, |
|||
const uint64_t attr_timeout_, |
|||
fuse_dirents_t *buf_) |
|||
{ |
|||
dev_t dev; |
|||
HashSet names; |
|||
string basepath; |
|||
string fullpath; |
|||
struct stat st; |
|||
uint64_t namelen; |
|||
fuse_entry_t entry; |
|||
|
|||
fuse_dirents_reset(buf_); |
|||
|
|||
entry.nodeid = 0; |
|||
entry.generation = 0; |
|||
entry.entry_valid = entry_timeout_; |
|||
entry.attr_valid = attr_timeout_; |
|||
entry.entry_valid_nsec = 0; |
|||
entry.attr_valid_nsec = 0; |
|||
for(auto &branch : *branches_) |
|||
{ |
|||
int rv; |
|||
int dirfd; |
|||
DIR *dh; |
|||
|
|||
basepath = fs::path::make(branch.path,dirname_); |
|||
|
|||
dh = fs::opendir(basepath); |
|||
if(!dh) |
|||
continue; |
|||
|
|||
dirfd = fs::dirfd(dh); |
|||
dev = fs::devid(dirfd); |
|||
|
|||
rv = 0; |
|||
for(struct dirent *de = fs::readdir(dh); de && !rv; de = fs::readdir(dh)) |
|||
{ |
|||
namelen = l::dirent_exact_namelen(de); |
|||
|
|||
rv = names.put(de->d_name,namelen); |
|||
if(rv == 0) |
|||
continue; |
|||
|
|||
rv = fs::fstatat_nofollow(dirfd,de->d_name,&st); |
|||
if(rv == -1) |
|||
{ |
|||
memset(&st,0,sizeof(st)); |
|||
st.st_ino = de->d_ino; |
|||
st.st_dev = dev; |
|||
st.st_mode = DTTOIF(de->d_type); |
|||
} |
|||
|
|||
fullpath = fs::path::make(dirname_,de->d_name); |
|||
fs::inode::calc(fullpath,&st); |
|||
de->d_ino = st.st_ino; |
|||
|
|||
rv = fuse_dirents_add_plus(buf_,de,namelen,&entry,&st); |
|||
if(rv) |
|||
return (fs::closedir(dh),-ENOMEM); |
|||
} |
|||
|
|||
fs::closedir(dh); |
|||
} |
|||
|
|||
return 0; |
|||
} |
|||
} |
|||
|
|||
namespace FUSE |
|||
{ |
|||
int |
|||
readdir_plus_posix(const Branches::CPtr &branches_, |
|||
const char *dirname_, |
|||
const uint64_t entry_timeout_, |
|||
const uint64_t attr_timeout_, |
|||
fuse_dirents_t *buf_) |
|||
{ |
|||
return l::readdir_plus(branches_,dirname_,entry_timeout_,attr_timeout_,buf_); |
|||
} |
|||
} |
@ -1,36 +0,0 @@ |
|||
/*
|
|||
ISC License |
|||
|
|||
Copyright (c) 2020, Antonio SJ Musumeci <trapexit@spawn.link> |
|||
|
|||
Permission to use, copy, modify, and/or distribute this software for any |
|||
purpose with or without fee is hereby granted, provided that the above |
|||
copyright notice and this permission notice appear in all copies. |
|||
|
|||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
|||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
|||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
|||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
|||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
|||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
|||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
|||
*/ |
|||
|
|||
#pragma once
|
|||
|
|||
#include "branches.hpp"
|
|||
|
|||
#include "fuse.h"
|
|||
|
|||
#include <cstdint>
|
|||
|
|||
|
|||
namespace FUSE |
|||
{ |
|||
int |
|||
readdir_plus_posix(const Branches::CPtr &branches, |
|||
const char *dirname, |
|||
const uint64_t entry_timeout, |
|||
const uint64_t attr_timeout, |
|||
fuse_dirents_t *buf); |
|||
} |
@ -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
|
Write
Preview
Loading…
Cancel
Save
Reference in new issue