ClickHouse/src/Common/Fiber.h

149 lines
3.5 KiB
C++
Raw Normal View History

2020-12-10 09:30:43 +00:00
#pragma once
2021-09-01 13:46:23 +00:00
/// defines.h should be included before fiber.hpp
/// BOOST_USE_ASAN, BOOST_USE_TSAN and BOOST_USE_UCONTEXT should be correctly defined for sanitizers.
2021-10-02 07:13:14 +00:00
#include <base/defines.h>
2020-12-10 09:30:43 +00:00
#include <boost/context/fiber.hpp>
2023-05-22 18:22:05 +00:00
#include <map>
2020-12-10 09:30:43 +00:00
/// Class wrapper for boost::context::fiber.
/// It tracks current executing fiber for thread and
/// supports storing fiber-specific data
/// that will be destroyed on fiber destructor.
class Fiber
{
2023-05-22 18:22:05 +00:00
private:
using Impl = boost::context::fiber;
2023-05-22 18:22:05 +00:00
using FiberPtr = Fiber *;
template <typename T> friend class FiberLocal;
2023-05-22 18:22:05 +00:00
public:
template <typename StackAlloc, typename Fn>
2023-05-22 18:22:05 +00:00
Fiber(StackAlloc && salloc, Fn && fn) : impl(std::allocator_arg_t(), std::forward<StackAlloc>(salloc), RoutineImpl(std::forward<Fn>(fn)))
{
}
Fiber() = default;
Fiber(Fiber && other) = default;
Fiber & operator=(Fiber && other) = default;
Fiber(const Fiber &) = delete;
Fiber & operator =(const Fiber &) = delete;
explicit operator bool() const
{
return impl.operator bool();
}
void resume()
{
/// Update information about current executing fiber.
2023-05-22 18:22:05 +00:00
FiberPtr & current_fiber = getCurrentFiber();
FiberPtr parent_fiber = current_fiber;
current_fiber = this;
impl = std::move(impl).resume();
2023-05-22 18:22:05 +00:00
/// Restore parent fiber.
current_fiber = parent_fiber;
}
static FiberPtr & getCurrentFiber()
{
thread_local static FiberPtr current_fiber;
return current_fiber;
}
2023-05-22 18:22:05 +00:00
private:
template <typename Fn>
struct RoutineImpl
{
2023-05-22 18:22:05 +00:00
struct SuspendCallback
{
Impl & impl;
void operator()()
{
impl = std::move(impl).resume();
}
};
explicit RoutineImpl(Fn && fn_) : fn(std::move(fn_))
{
}
Impl operator()(Impl && sink)
{
SuspendCallback suspend_callback{sink};
fn(suspend_callback);
return std::move(sink);
}
Fn fn;
};
2023-05-22 18:22:05 +00:00
/// Special wrapper to store data in uniquer_ptr.
struct DataWrapper
{
2023-05-22 18:22:05 +00:00
virtual ~DataWrapper() = default;
};
2023-05-22 18:22:05 +00:00
using DataPtr = std::unique_ptr<DataWrapper>;
/// Get reference to fiber-specific data by key
/// (the pointer to the structure that uses this data).
DataPtr & getLocalData(void * key)
{
2023-05-22 18:22:05 +00:00
return local_data[key];
}
2023-05-22 18:22:05 +00:00
Impl && release()
{
2023-05-22 18:22:05 +00:00
return std::move(impl);
}
Impl impl;
2023-05-22 18:22:05 +00:00
std::map<void *, DataPtr> local_data;
};
/// Implementation for fiber local variable.
2023-05-22 18:22:05 +00:00
/// If we are in fiber, it returns fiber local data,
/// otherwise it returns it's single field.
/// Fiber local data is destroyed in Fiber destructor.
2023-05-22 14:06:46 +00:00
/// Implementation is similar to boost::fiber::fiber_specific_ptr
/// (we cannot use it because we don't use boost::fiber API.
template <typename T>
class FiberLocal
{
public:
T & operator*()
{
return get();
}
T * operator->()
{
return &get();
}
private:
2023-05-22 18:22:05 +00:00
struct DataWrapperImpl : public Fiber::DataWrapper
{
2023-05-22 18:22:05 +00:00
T impl;
};
2023-05-22 18:22:05 +00:00
T & get()
{
2023-05-22 18:22:05 +00:00
Fiber * current_fiber = Fiber::getCurrentFiber();
if (!current_fiber)
return main_instance;
2023-05-22 18:22:05 +00:00
Fiber::DataPtr & ptr = current_fiber->getLocalData(this);
/// Initialize instance on first request.
if (!ptr)
2023-05-22 18:22:05 +00:00
ptr = std::make_unique<DataWrapperImpl>();
2023-05-22 18:22:05 +00:00
return dynamic_cast<DataWrapperImpl *>(ptr.get())->impl;
}
2023-05-22 14:06:46 +00:00
2023-05-22 18:22:05 +00:00
T main_instance;
};