mirror of https://github.com/trapexit/mergerfs.git
Antonio SJ Musumeci
2 years ago
8 changed files with 455 additions and 27 deletions
-
9libfuse/Makefile
-
3libfuse/include/fuse_lowlevel.h
-
8libfuse/lib/fuse_i.h
-
22libfuse/lib/fuse_loop_mt.cpp
-
5libfuse/lib/fuse_lowlevel.c
-
14libfuse/lib/fuse_session.c
-
161libfuse/lib/pool.hpp
-
256libfuse/lib/queue.hpp
@ -0,0 +1,161 @@ |
|||
#pragma once
|
|||
|
|||
#include <tuple>
|
|||
#include <atomic>
|
|||
#include <vector>
|
|||
#include <thread>
|
|||
#include <memory>
|
|||
#include <future>
|
|||
#include <utility>
|
|||
#include <stdexcept>
|
|||
#include <functional>
|
|||
#include <type_traits>
|
|||
#include "queue.hpp"
|
|||
|
|||
|
|||
|
|||
class simple_thread_pool |
|||
{ |
|||
public: |
|||
explicit simple_thread_pool(std::size_t thread_count = std::thread::hardware_concurrency()) |
|||
{ |
|||
if(!thread_count) |
|||
throw std::invalid_argument("bad thread count! must be non-zero!"); |
|||
|
|||
auto worker = [this]() |
|||
{ |
|||
while(true) |
|||
{ |
|||
proc_t f; |
|||
if(!m_queue.pop(f)) |
|||
break; |
|||
f(); |
|||
} |
|||
}; |
|||
|
|||
m_threads.reserve(thread_count); |
|||
while(thread_count--) |
|||
m_threads.emplace_back(worker); |
|||
} |
|||
|
|||
~simple_thread_pool() |
|||
{ |
|||
m_queue.unblock(); |
|||
for(auto& thread : m_threads) |
|||
thread.join(); |
|||
} |
|||
|
|||
template<typename F, typename... Args> |
|||
void enqueue_work(F&& f, Args&&... args) |
|||
{ |
|||
m_queue.push([p = std::forward<F>(f), t = std::make_tuple(std::forward<Args>(args)...)]() { std::apply(p, t); }); |
|||
} |
|||
|
|||
template<typename F, typename... Args> |
|||
[[nodiscard]] auto enqueue_task(F&& f, Args&&... args) -> std::future<std::invoke_result_t<F, Args...>> |
|||
{ |
|||
using task_return_type = std::invoke_result_t<F, Args...>; |
|||
using task_type = std::packaged_task<task_return_type()>; |
|||
|
|||
auto task = std::make_shared<task_type>(std::bind(std::forward<F>(f), std::forward<Args>(args)...)); |
|||
auto result = task->get_future(); |
|||
|
|||
m_queue.push([=]() { (*task)(); }); |
|||
|
|||
return result; |
|||
} |
|||
|
|||
private: |
|||
using proc_t = std::function<void(void)>; |
|||
using queue_t = unbounded_queue<proc_t>; |
|||
queue_t m_queue; |
|||
|
|||
using threads_t = std::vector<std::thread>; |
|||
threads_t m_threads; |
|||
}; |
|||
|
|||
|
|||
|
|||
class thread_pool |
|||
{ |
|||
public: |
|||
explicit thread_pool(std::size_t thread_count = std::thread::hardware_concurrency()) |
|||
: m_queues(thread_count), m_count(thread_count) |
|||
{ |
|||
if(!thread_count) |
|||
throw std::invalid_argument("bad thread count! must be non-zero!"); |
|||
|
|||
auto worker = [this](auto i) |
|||
{ |
|||
while(true) |
|||
{ |
|||
proc_t f; |
|||
for(std::size_t n = 0; n < m_count * K; ++n) |
|||
if(m_queues[(i + n) % m_count].try_pop(f)) |
|||
break; |
|||
if(!f && !m_queues[i].pop(f)) |
|||
break; |
|||
f(); |
|||
} |
|||
}; |
|||
|
|||
m_threads.reserve(thread_count); |
|||
for(std::size_t i = 0; i < thread_count; ++i) |
|||
m_threads.emplace_back(worker, i); |
|||
} |
|||
|
|||
~thread_pool() |
|||
{ |
|||
for(auto& queue : m_queues) |
|||
queue.unblock(); |
|||
for(auto& thread : m_threads) |
|||
thread.join(); |
|||
} |
|||
|
|||
template<typename F, typename... Args> |
|||
void enqueue_work(F&& f, Args&&... args) |
|||
{ |
|||
auto work = [p = std::forward<F>(f), t = std::make_tuple(std::forward<Args>(args)...)]() { std::apply(p, t); }; |
|||
auto i = m_index++; |
|||
|
|||
for(std::size_t n = 0; n < m_count * K; ++n) |
|||
if(m_queues[(i + n) % m_count].try_push(work)) |
|||
return; |
|||
|
|||
m_queues[i % m_count].push(std::move(work)); |
|||
} |
|||
|
|||
template<typename F, typename... Args> |
|||
[[nodiscard]] auto enqueue_task(F&& f, Args&&... args) -> std::future<std::invoke_result_t<F, Args...>> |
|||
{ |
|||
using task_return_type = std::invoke_result_t<F, Args...>; |
|||
using task_type = std::packaged_task<task_return_type()>; |
|||
|
|||
auto task = std::make_shared<task_type>(std::bind(std::forward<F>(f), std::forward<Args>(args)...)); |
|||
auto work = [=]() { (*task)(); }; |
|||
auto result = task->get_future(); |
|||
auto i = m_index++; |
|||
|
|||
for(auto n = 0; n < m_count * K; ++n) |
|||
if(m_queues[(i + n) % m_count].try_push(work)) |
|||
return result; |
|||
|
|||
m_queues[i % m_count].push(std::move(work)); |
|||
|
|||
return result; |
|||
} |
|||
|
|||
private: |
|||
using proc_t = std::function<void(void)>; |
|||
using queue_t = unbounded_queue<proc_t>; |
|||
using queues_t = std::vector<queue_t>; |
|||
queues_t m_queues; |
|||
|
|||
using threads_t = std::vector<std::thread>; |
|||
threads_t m_threads; |
|||
|
|||
const std::size_t m_count; |
|||
std::atomic_uint m_index = 0; |
|||
|
|||
inline static const unsigned int K = 2; |
|||
}; |
@ -0,0 +1,256 @@ |
|||
#pragma once
|
|||
|
|||
#include <mutex>
|
|||
#include <queue>
|
|||
#include <utility>
|
|||
#include <stdexcept>
|
|||
#include <condition_variable>
|
|||
|
|||
|
|||
|
|||
template<typename T> |
|||
class unbounded_queue |
|||
{ |
|||
public: |
|||
explicit unbounded_queue(bool block = true) |
|||
: m_block{ block } {} |
|||
|
|||
void push(const T& item) |
|||
{ |
|||
{ |
|||
std::scoped_lock guard(m_queue_lock); |
|||
m_queue.push(item); |
|||
} |
|||
m_condition.notify_one(); |
|||
} |
|||
|
|||
void push(T&& item) |
|||
{ |
|||
{ |
|||
std::scoped_lock guard(m_queue_lock); |
|||
m_queue.push(std::move(item)); |
|||
} |
|||
m_condition.notify_one(); |
|||
} |
|||
|
|||
template<typename... Args> |
|||
void emplace(Args&&... args) |
|||
{ |
|||
{ |
|||
std::scoped_lock guard(m_queue_lock); |
|||
m_queue.emplace(std::forward<Args>(args)...); |
|||
} |
|||
m_condition.notify_one(); |
|||
} |
|||
|
|||
bool try_push(const T& item) |
|||
{ |
|||
{ |
|||
std::unique_lock lock(m_queue_lock, std::try_to_lock); |
|||
if(!lock) |
|||
return false; |
|||
m_queue.push(item); |
|||
} |
|||
m_condition.notify_one(); |
|||
return true; |
|||
} |
|||
|
|||
bool try_push(T&& item) |
|||
{ |
|||
{ |
|||
std::unique_lock lock(m_queue_lock, std::try_to_lock); |
|||
if(!lock) |
|||
return false; |
|||
m_queue.push(std::move(item)); |
|||
} |
|||
m_condition.notify_one(); |
|||
return true; |
|||
} |
|||
|
|||
bool pop(T& item) |
|||
{ |
|||
std::unique_lock guard(m_queue_lock); |
|||
m_condition.wait(guard, [&]() { return !m_queue.empty() || !m_block; }); |
|||
if(m_queue.empty()) |
|||
return false; |
|||
item = std::move(m_queue.front()); |
|||
m_queue.pop(); |
|||
return true; |
|||
} |
|||
|
|||
bool try_pop(T& item) |
|||
{ |
|||
std::unique_lock lock(m_queue_lock, std::try_to_lock); |
|||
if(!lock || m_queue.empty()) |
|||
return false; |
|||
item = std::move(m_queue.front()); |
|||
m_queue.pop(); |
|||
return true; |
|||
} |
|||
|
|||
std::size_t size() const |
|||
{ |
|||
std::scoped_lock guard(m_queue_lock); |
|||
return m_queue.size(); |
|||
} |
|||
|
|||
bool empty() const |
|||
{ |
|||
std::scoped_lock guard(m_queue_lock); |
|||
return m_queue.empty(); |
|||
} |
|||
|
|||
void block() |
|||
{ |
|||
std::scoped_lock guard(m_queue_lock); |
|||
m_block = true; |
|||
} |
|||
|
|||
void unblock() |
|||
{ |
|||
{ |
|||
std::scoped_lock guard(m_queue_lock); |
|||
m_block = false; |
|||
} |
|||
m_condition.notify_all(); |
|||
} |
|||
|
|||
bool blocking() const |
|||
{ |
|||
std::scoped_lock guard(m_queue_lock); |
|||
return m_block; |
|||
} |
|||
|
|||
private: |
|||
using queue_t = std::queue<T>; |
|||
queue_t m_queue; |
|||
|
|||
bool m_block; |
|||
|
|||
mutable std::mutex m_queue_lock; |
|||
std::condition_variable m_condition; |
|||
}; |
|||
|
|||
|
|||
|
|||
template<typename T> |
|||
class bounded_queue |
|||
{ |
|||
public: |
|||
explicit bounded_queue(std::size_t max_size, bool block = true) |
|||
: m_block{ block }, m_max_size{ max_size } |
|||
{ |
|||
if(!m_max_size) |
|||
throw std::invalid_argument("bad queue max-size! must be non-zero!"); |
|||
} |
|||
|
|||
bool push(const T& item) |
|||
{ |
|||
{ |
|||
std::unique_lock guard(m_queue_lock); |
|||
m_condition_push.wait(guard, [&]() { return m_queue.size() < m_max_size || !m_block; }); |
|||
if(m_queue.size() == m_max_size) |
|||
return false; |
|||
m_queue.push(item); |
|||
} |
|||
m_condition_pop.notify_one(); |
|||
return true; |
|||
} |
|||
|
|||
bool push(T&& item) |
|||
{ |
|||
{ |
|||
std::unique_lock guard(m_queue_lock); |
|||
m_condition_push.wait(guard, [&]() { return m_queue.size() < m_max_size || !m_block; }); |
|||
if(m_queue.size() == m_max_size) |
|||
return false; |
|||
m_queue.push(std::move(item)); |
|||
} |
|||
m_condition_pop.notify_one(); |
|||
return true; |
|||
} |
|||
|
|||
template<typename... Args> |
|||
bool emplace(Args&&... args) |
|||
{ |
|||
{ |
|||
std::unique_lock guard(m_queue_lock); |
|||
m_condition_push.wait(guard, [&]() { return m_queue.size() < m_max_size || !m_block; }); |
|||
if(m_queue.size() == m_max_size) |
|||
return false; |
|||
m_queue.emplace(std::forward<Args>(args)...); |
|||
} |
|||
m_condition_pop.notify_one(); |
|||
return true; |
|||
} |
|||
|
|||
bool pop(T& item) |
|||
{ |
|||
{ |
|||
std::unique_lock guard(m_queue_lock); |
|||
m_condition_pop.wait(guard, [&]() { return !m_queue.empty() || !m_block; }); |
|||
if(m_queue.empty()) |
|||
return false; |
|||
item = std::move(m_queue.front()); |
|||
m_queue.pop(); |
|||
} |
|||
m_condition_push.notify_one(); |
|||
return true; |
|||
} |
|||
|
|||
std::size_t size() const |
|||
{ |
|||
std::scoped_lock guard(m_queue_lock); |
|||
return m_queue.size(); |
|||
} |
|||
|
|||
std::size_t capacity() const |
|||
{ |
|||
return m_max_size; |
|||
} |
|||
|
|||
bool empty() const |
|||
{ |
|||
std::scoped_lock guard(m_queue_lock); |
|||
return m_queue.empty(); |
|||
} |
|||
|
|||
bool full() const |
|||
{ |
|||
std::scoped_lock lock(m_queue_lock); |
|||
return m_queue.size() == capacity(); |
|||
} |
|||
|
|||
void block() |
|||
{ |
|||
std::scoped_lock guard(m_queue_lock); |
|||
m_block = true; |
|||
} |
|||
|
|||
void unblock() |
|||
{ |
|||
{ |
|||
std::scoped_lock guard(m_queue_lock); |
|||
m_block = false; |
|||
} |
|||
m_condition_push.notify_all(); |
|||
m_condition_pop.notify_all(); |
|||
} |
|||
|
|||
bool blocking() const |
|||
{ |
|||
std::scoped_lock guard(m_queue_lock); |
|||
return m_block; |
|||
} |
|||
|
|||
private: |
|||
using queue_t = std::queue<T>; |
|||
queue_t m_queue; |
|||
|
|||
bool m_block; |
|||
const std::size_t m_max_size; |
|||
|
|||
mutable std::mutex m_queue_lock; |
|||
std::condition_variable m_condition_push; |
|||
std::condition_variable m_condition_pop; |
|||
}; |
Write
Preview
Loading…
Cancel
Save
Reference in new issue