mirror of https://github.com/trapexit/mergerfs.git
Antonio SJ Musumeci
1 year ago
9 changed files with 468 additions and 379 deletions
-
16libfuse/lib/fuse_loop.cpp
-
310libfuse/lib/thread_pool.hpp
-
161libfuse/lib/unbounded_queue.hpp
-
6src/fuse_readdir_cor.cpp
-
6src/fuse_readdir_cor.hpp
-
6src/fuse_readdir_cosr.cpp
-
5src/fuse_readdir_cosr.hpp
-
157src/thread_pool.hpp
-
172src/unbounded_thread_pool.hpp
@ -0,0 +1,310 @@ |
|||
#pragma once
|
|||
|
|||
#include "moodycamel/blockingconcurrentqueue.h"
|
|||
#include "syslog.h"
|
|||
|
|||
#include <atomic>
|
|||
#include <csignal>
|
|||
#include <cstring>
|
|||
#include <future>
|
|||
#include <memory>
|
|||
#include <mutex>
|
|||
#include <stdexcept>
|
|||
#include <string>
|
|||
#include <thread>
|
|||
#include <vector>
|
|||
|
|||
|
|||
struct ThreadPoolTraits : public moodycamel::ConcurrentQueueDefaultTraits |
|||
{ |
|||
static const int MAX_SEMA_SPINS = 1; |
|||
}; |
|||
|
|||
|
|||
class ThreadPool |
|||
{ |
|||
private: |
|||
using Func = std::function<void(void)>; |
|||
using Queue = moodycamel::BlockingConcurrentQueue<Func,ThreadPoolTraits>; |
|||
|
|||
public: |
|||
explicit |
|||
ThreadPool(std::size_t const thread_count_ = std::thread::hardware_concurrency(), |
|||
std::size_t const queue_depth_ = 1, |
|||
std::string const name_ = {}) |
|||
: _queue(queue_depth_,thread_count_,thread_count_), |
|||
_name(get_thread_name(name_)) |
|||
{ |
|||
syslog_debug("threadpool: spawning %zu threads of queue depth %zu named '%s'", |
|||
thread_count_, |
|||
queue_depth_, |
|||
_name.c_str()); |
|||
|
|||
sigset_t oldset; |
|||
sigset_t newset; |
|||
|
|||
sigfillset(&newset); |
|||
pthread_sigmask(SIG_BLOCK,&newset,&oldset); |
|||
|
|||
_threads.reserve(thread_count_); |
|||
for(std::size_t i = 0; i < thread_count_; ++i) |
|||
{ |
|||
int rv; |
|||
pthread_t t; |
|||
|
|||
rv = pthread_create(&t,NULL,ThreadPool::start_routine,this); |
|||
if(rv != 0) |
|||
{ |
|||
syslog_warning("threadpool: error spawning thread - %d (%s)", |
|||
rv, |
|||
strerror(rv)); |
|||
continue; |
|||
} |
|||
|
|||
if(!_name.empty()) |
|||
pthread_setname_np(t,_name.c_str()); |
|||
|
|||
_threads.push_back(t); |
|||
} |
|||
|
|||
pthread_sigmask(SIG_SETMASK,&oldset,NULL); |
|||
|
|||
if(_threads.empty()) |
|||
throw std::runtime_error("threadpool: failed to spawn any threads"); |
|||
} |
|||
|
|||
~ThreadPool() |
|||
{ |
|||
syslog_debug("threadpool: destroying %zu threads named '%s'", |
|||
_threads.size(), |
|||
_name.c_str()); |
|||
|
|||
for(auto t : _threads) |
|||
pthread_cancel(t); |
|||
|
|||
Func f; |
|||
while(_queue.try_dequeue(f)) |
|||
continue; |
|||
|
|||
for(auto t : _threads) |
|||
pthread_join(t,NULL); |
|||
} |
|||
|
|||
private: |
|||
static |
|||
std::string |
|||
get_thread_name(std::string const name_) |
|||
{ |
|||
if(!name_.empty()) |
|||
return name_; |
|||
|
|||
char name[16]; |
|||
pthread_getname_np(pthread_self(),name,sizeof(name)); |
|||
|
|||
return name; |
|||
} |
|||
|
|||
static |
|||
void* |
|||
start_routine(void *arg_) |
|||
{ |
|||
ThreadPool *btp = static_cast<ThreadPool*>(arg_); |
|||
ThreadPool::Func func; |
|||
ThreadPool::Queue &q = btp->_queue; |
|||
moodycamel::ConsumerToken ctok(btp->_queue); |
|||
|
|||
while(true) |
|||
{ |
|||
q.wait_dequeue(ctok,func); |
|||
|
|||
func(); |
|||
} |
|||
|
|||
return NULL; |
|||
} |
|||
|
|||
public: |
|||
int |
|||
add_thread(std::string const name_ = {}) |
|||
{ |
|||
int rv; |
|||
pthread_t t; |
|||
sigset_t oldset; |
|||
sigset_t newset; |
|||
std::string name; |
|||
|
|||
name = (name_.empty() ? _name : name_); |
|||
|
|||
sigfillset(&newset); |
|||
pthread_sigmask(SIG_BLOCK,&newset,&oldset); |
|||
rv = pthread_create(&t,NULL,ThreadPool::start_routine,this); |
|||
pthread_sigmask(SIG_SETMASK,&oldset,NULL); |
|||
|
|||
if(rv != 0) |
|||
{ |
|||
syslog_warning("threadpool: error spawning thread - %d (%s)", |
|||
rv, |
|||
strerror(rv)); |
|||
return -rv; |
|||
} |
|||
|
|||
if(!name.empty()) |
|||
pthread_setname_np(t,name.c_str()); |
|||
|
|||
{ |
|||
std::lock_guard<std::mutex> lg(_threads_mutex); |
|||
_threads.push_back(t); |
|||
} |
|||
|
|||
syslog_debug("threadpool: 1 thread added to pool '%s' named '%s'", |
|||
_name.c_str(), |
|||
name.c_str()); |
|||
|
|||
return 0; |
|||
} |
|||
|
|||
int |
|||
remove_thread(void) |
|||
{ |
|||
{ |
|||
std::lock_guard<std::mutex> lg(_threads_mutex); |
|||
if(_threads.size() <= 1) |
|||
return -EINVAL; |
|||
} |
|||
|
|||
std::promise<pthread_t> promise; |
|||
auto func = [&]() |
|||
{ |
|||
pthread_t t; |
|||
|
|||
t = pthread_self(); |
|||
promise.set_value(t); |
|||
|
|||
{ |
|||
std::lock_guard<std::mutex> lg(_threads_mutex); |
|||
|
|||
for(auto i = _threads.begin(); i != _threads.end(); ++i) |
|||
{ |
|||
if(*i != t) |
|||
continue; |
|||
|
|||
_threads.erase(i); |
|||
break; |
|||
} |
|||
} |
|||
|
|||
char name[16]; |
|||
pthread_getname_np(t,name,sizeof(name)); |
|||
syslog_debug("threadpool: 1 thread removed from pool '%s' named '%s'", |
|||
_name.c_str(), |
|||
name); |
|||
|
|||
pthread_exit(NULL); |
|||
}; |
|||
|
|||
enqueue_work(func); |
|||
pthread_join(promise.get_future().get(),NULL); |
|||
|
|||
return 0; |
|||
} |
|||
|
|||
int |
|||
set_threads(std::size_t const count_) |
|||
{ |
|||
int diff; |
|||
{ |
|||
std::lock_guard<std::mutex> lg(_threads_mutex); |
|||
|
|||
diff = ((int)count_ - (int)_threads.size()); |
|||
} |
|||
|
|||
syslog_debug("diff: %d",diff); |
|||
for(auto i = diff; i > 0; --i) |
|||
add_thread(); |
|||
for(auto i = diff; i < 0; ++i) |
|||
remove_thread(); |
|||
|
|||
return diff; |
|||
} |
|||
|
|||
public: |
|||
template<typename FuncType> |
|||
void |
|||
enqueue_work(moodycamel::ProducerToken &ptok_, |
|||
FuncType &&f_) |
|||
{ |
|||
timespec ts = {0,10}; |
|||
while(true) |
|||
{ |
|||
if(_queue.try_enqueue(ptok_,f_)) |
|||
return; |
|||
::nanosleep(&ts,NULL); |
|||
ts.tv_nsec += 10; |
|||
} |
|||
} |
|||
|
|||
template<typename FuncType> |
|||
void |
|||
enqueue_work(FuncType &&f_) |
|||
{ |
|||
timespec ts = {0,10}; |
|||
while(true) |
|||
{ |
|||
if(_queue.try_enqueue(f_)) |
|||
return; |
|||
::nanosleep(&ts,NULL); |
|||
ts.tv_nsec += 10; |
|||
} |
|||
} |
|||
|
|||
template<typename FuncType> |
|||
[[nodiscard]] |
|||
std::future<typename std::result_of<FuncType()>::type> |
|||
enqueue_task(FuncType&& f_) |
|||
{ |
|||
using TaskReturnType = typename std::result_of<FuncType()>::type; |
|||
using Promise = std::promise<TaskReturnType>; |
|||
|
|||
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) |
|||
{ |
|||
if(_queue.try_enqueue(work)) |
|||
break; |
|||
::nanosleep(&ts,NULL); |
|||
ts.tv_nsec += 10; |
|||
} |
|||
|
|||
return future; |
|||
} |
|||
|
|||
public: |
|||
std::vector<pthread_t> |
|||
threads() const |
|||
{ |
|||
std::lock_guard<std::mutex> lg(_threads_mutex); |
|||
|
|||
return _threads; |
|||
} |
|||
|
|||
moodycamel::ProducerToken |
|||
ptoken() |
|||
{ |
|||
return moodycamel::ProducerToken(_queue); |
|||
} |
|||
|
|||
private: |
|||
Queue _queue; |
|||
|
|||
private: |
|||
std::string const _name; |
|||
std::vector<pthread_t> _threads; |
|||
mutable std::mutex _threads_mutex; |
|||
}; |
@ -1,161 +0,0 @@ |
|||
#pragma once
|
|||
|
|||
#include <condition_variable>
|
|||
#include <mutex>
|
|||
#include <queue>
|
|||
#include <utility>
|
|||
|
|||
|
|||
template<typename T> |
|||
class UnboundedQueue |
|||
{ |
|||
public: |
|||
explicit |
|||
UnboundedQueue(bool block_ = true) |
|||
: _block(block_) |
|||
{ |
|||
} |
|||
|
|||
void |
|||
push(const T& item_) |
|||
{ |
|||
{ |
|||
std::lock_guard<std::mutex> guard(_queue_lock); |
|||
_queue.push(item_); |
|||
} |
|||
_condition.notify_one(); |
|||
} |
|||
|
|||
void |
|||
push(T&& item_) |
|||
{ |
|||
{ |
|||
std::lock_guard<std::mutex> guard(_queue_lock); |
|||
_queue.push(std::move(item_)); |
|||
} |
|||
|
|||
_condition.notify_one(); |
|||
} |
|||
|
|||
template<typename... Args> |
|||
void |
|||
emplace(Args&&... args_) |
|||
{ |
|||
{ |
|||
std::lock_guard<std::mutex> guard(_queue_lock); |
|||
_queue.emplace(std::forward<Args>(args_)...); |
|||
} |
|||
|
|||
_condition.notify_one(); |
|||
} |
|||
|
|||
bool |
|||
try_push(const T& item_) |
|||
{ |
|||
{ |
|||
std::unique_lock<std::mutex> lock(_queue_lock, std::try_to_lock); |
|||
if(!lock) |
|||
return false; |
|||
_queue.push(item_); |
|||
} |
|||
|
|||
_condition.notify_one(); |
|||
|
|||
return true; |
|||
} |
|||
|
|||
bool |
|||
try_push(T&& item_) |
|||
{ |
|||
{ |
|||
std::unique_lock<std::mutex> lock(_queue_lock, std::try_to_lock); |
|||
if(!lock) |
|||
return false; |
|||
_queue.push(std::move(item_)); |
|||
} |
|||
|
|||
_condition.notify_one(); |
|||
|
|||
return true; |
|||
} |
|||
|
|||
//TODO: push multiple T at once
|
|||
|
|||
bool |
|||
pop(T& item_) |
|||
{ |
|||
std::unique_lock<std::mutex> guard(_queue_lock); |
|||
|
|||
_condition.wait(guard, [&]() { return !_queue.empty() || !_block; }); |
|||
if(_queue.empty()) |
|||
return false; |
|||
|
|||
item_ = std::move(_queue.front()); |
|||
_queue.pop(); |
|||
|
|||
return true; |
|||
} |
|||
|
|||
bool |
|||
try_pop(T& item_) |
|||
{ |
|||
std::unique_lock<std::mutex> lock(_queue_lock, std::try_to_lock); |
|||
if(!lock || _queue.empty()) |
|||
return false; |
|||
|
|||
item_ = std::move(_queue.front()); |
|||
_queue.pop(); |
|||
|
|||
return true; |
|||
} |
|||
|
|||
std::size_t |
|||
size() const |
|||
{ |
|||
std::lock_guard<std::mutex> guard(_queue_lock); |
|||
|
|||
return _queue.size(); |
|||
} |
|||
|
|||
bool |
|||
empty() const |
|||
{ |
|||
std::lock_guard<std::mutex> guard(_queue_lock); |
|||
|
|||
return _queue.empty(); |
|||
} |
|||
|
|||
void |
|||
block() |
|||
{ |
|||
std::lock_guard<std::mutex> guard(_queue_lock); |
|||
_block = true; |
|||
} |
|||
|
|||
void |
|||
unblock() |
|||
{ |
|||
{ |
|||
std::lock_guard<std::mutex> guard(_queue_lock); |
|||
_block = false; |
|||
} |
|||
|
|||
_condition.notify_all(); |
|||
} |
|||
|
|||
bool |
|||
blocking() const |
|||
{ |
|||
std::lock_guard<std::mutex> guard(_queue_lock); |
|||
|
|||
return _block; |
|||
} |
|||
|
|||
private: |
|||
mutable std::mutex _queue_lock; |
|||
|
|||
private: |
|||
bool _block; |
|||
std::queue<T> _queue; |
|||
std::condition_variable _condition; |
|||
}; |
@ -1,172 +0,0 @@ |
|||
#pragma once
|
|||
|
|||
#include "moodycamel/blockingconcurrentqueue.h"
|
|||
#include "syslog.hpp"
|
|||
|
|||
#include <atomic>
|
|||
#include <csignal>
|
|||
#include <cstring>
|
|||
#include <future>
|
|||
#include <memory>
|
|||
#include <stdexcept>
|
|||
#include <string>
|
|||
#include <thread>
|
|||
#include <vector>
|
|||
|
|||
|
|||
struct UnboundedThreadPoolTraits : public moodycamel::ConcurrentQueueDefaultTraits |
|||
{ |
|||
static const int MAX_SEMA_SPINS = 1; |
|||
}; |
|||
|
|||
|
|||
class UnboundedThreadPool |
|||
{ |
|||
private: |
|||
using Func = std::function<void(void)>; |
|||
using Queue = moodycamel::BlockingConcurrentQueue<Func,UnboundedThreadPoolTraits>; |
|||
|
|||
public: |
|||
explicit |
|||
UnboundedThreadPool(std::size_t const thread_count_ = std::thread::hardware_concurrency(), |
|||
std::string const name_ = {}) |
|||
: _queue(thread_count_,thread_count_,thread_count_), |
|||
_done(false), |
|||
_name(name_) |
|||
{ |
|||
syslog_debug("threadpool: spawning %zu threads named '%s'", |
|||
thread_count_, |
|||
_name.c_str()); |
|||
|
|||
sigset_t oldset; |
|||
sigset_t newset; |
|||
|
|||
sigfillset(&newset); |
|||
pthread_sigmask(SIG_BLOCK,&newset,&oldset); |
|||
|
|||
_threads.reserve(thread_count_); |
|||
for(std::size_t i = 0; i < thread_count_; ++i) |
|||
{ |
|||
int rv; |
|||
pthread_t t; |
|||
|
|||
rv = pthread_create(&t,NULL,UnboundedThreadPool::start_routine,this); |
|||
if(rv != 0) |
|||
{ |
|||
syslog_warning("threadpool: error spawning thread - %d (%s)", |
|||
rv, |
|||
strerror(rv)); |
|||
continue; |
|||
} |
|||
|
|||
if(!_name.empty()) |
|||
pthread_setname_np(t,_name.c_str()); |
|||
|
|||
_threads.push_back(t); |
|||
} |
|||
|
|||
pthread_sigmask(SIG_SETMASK,&oldset,NULL); |
|||
|
|||
if(_threads.empty()) |
|||
throw std::runtime_error("threadpool: failed to spawn any threads"); |
|||
} |
|||
|
|||
~UnboundedThreadPool() |
|||
{ |
|||
syslog_debug("threadpool: destroying %zu threads named '%s'", |
|||
_threads.size(), |
|||
_name.c_str()); |
|||
|
|||
_done.store(true,std::memory_order_relaxed); |
|||
|
|||
for(auto t : _threads) |
|||
pthread_cancel(t); |
|||
|
|||
Func f; |
|||
while(_queue.try_dequeue(f)) |
|||
continue; |
|||
|
|||
for(auto t : _threads) |
|||
pthread_join(t,NULL); |
|||
} |
|||
|
|||
private: |
|||
static |
|||
void* |
|||
start_routine(void *arg_) |
|||
{ |
|||
UnboundedThreadPool *btp = static_cast<UnboundedThreadPool*>(arg_); |
|||
UnboundedThreadPool::Func func; |
|||
std::atomic<bool> &done = btp->_done; |
|||
UnboundedThreadPool::Queue &q = btp->_queue; |
|||
moodycamel::ConsumerToken ctok(btp->_queue); |
|||
|
|||
while(!done.load(std::memory_order_relaxed)) |
|||
{ |
|||
q.wait_dequeue(ctok,func); |
|||
|
|||
func(); |
|||
} |
|||
|
|||
return NULL; |
|||
} |
|||
|
|||
public: |
|||
template<typename FuncType> |
|||
void |
|||
enqueue_work(moodycamel::ProducerToken &ptok_, |
|||
FuncType &&f_) |
|||
{ |
|||
_queue.enqueue(ptok_,f_); |
|||
} |
|||
|
|||
template<typename FuncType> |
|||
void |
|||
enqueue_work(FuncType &&f_) |
|||
{ |
|||
_queue.enqueue(f_); |
|||
} |
|||
|
|||
template<typename FuncType> |
|||
[[nodiscard]] |
|||
std::future<typename std::result_of<FuncType()>::type> |
|||
enqueue_task(FuncType&& f_) |
|||
{ |
|||
using TaskReturnType = typename std::result_of<FuncType()>::type; |
|||
using Promise = std::promise<TaskReturnType>; |
|||
|
|||
auto promise = std::make_shared<Promise>(); |
|||
auto future = promise->get_future(); |
|||
auto work = [=]() |
|||
{ |
|||
auto rv = f_(); |
|||
promise->set_value(rv); |
|||
}; |
|||
|
|||
_queue.enqueue(work); |
|||
|
|||
return future; |
|||
} |
|||
|
|||
public: |
|||
std::vector<pthread_t> |
|||
threads() const |
|||
{ |
|||
return _threads; |
|||
} |
|||
|
|||
public: |
|||
Queue& |
|||
queue() |
|||
{ |
|||
return _queue; |
|||
} |
|||
|
|||
private: |
|||
Queue _queue; |
|||
|
|||
private: |
|||
std::atomic<bool> _done; |
|||
std::string const _name; |
|||
std::vector<pthread_t> _threads; |
|||
}; |
Write
Preview
Loading…
Cancel
Save
Reference in new issue