C++高性能分布式服务器框架
从零开始重写sylar C++高性能分布式服务器框架
ucontext_t *uc_link // Pointer to the context that is resumed when this context returns.
sigset_t uc_sigmask // The set of signals that are blocked when this context is active.
stack_t uc_stack // The stack used by this context.
mcontext_t uc_mcontext // A machine-specific representation of the saved context.
int getcontext(ucontext_t *ucp);
- 获取当前上下文
int setcontext(const ucontext_t *ucp);
- 恢复 ucp 指向的上下文,但这个函数不会返回,而是会跳转到 ucp 上下文对应的函数中执行,相当于变相调用了函数。
void makecontext(ucontext_t *ucp, void (*func)(), int argc, ...);
- 修改由 getcontext 获取到的上下文指针 ucp,将其与一个函数 func 进行绑定,支持指定 func 运行时的参数。
- 在调用 makecontext 之前,必须手动给 ucp 分配一段内存空间,存储在 ucp->uc_stack 中,这段内存空间将作为 func 函数运行时的栈空间。
- 同时也可以指定 ucp->uc_link,表示函数运行结束后恢复 uc_link 指向的上下文。
- 如果不赋值 uc_link,那 func 函数结束时必须调用 setcontext 或 swapcontext 以重新指定一个有效的上下文,否则程序就跑飞了。
- makecontext 执行完后,ucp 就与函数 func 绑定了,调用 setcontext 或 swapcontext 激活 ucp 时,func 就会被运行。
int swapcontext(ucontext_t *restrict oucp, const ucontext_t *restrict ucp);
- 恢复 ucp 指向的上下文,同时将当前的上下文存储到 oucp 中。
- 和 setcontext 一样,swapcontext 也不会返回,而是会跳转到 ucp 上下文对应的函数中执行,相当于调用了函数。
- swapcontext 是 sylar 非对称协程实现的关键,线程主协程和子协程用这个接口进行上下文切换。
/**
* @filename fiber.h
* @brief 协程模块
* @author L-ge
* @version 0.1
* @modify 2022-06-26
*/
#ifndef __SYLAR_FIBER_H__
#define __SYLAR_FIBER_H__
#include
#include
#include
namespace sylar
{
/**
* @brief 协程类
*/
class Fiber : public std::enable_shared_from_this
{
public:
typedef std::shared_ptr ptr;
// 协程运行状态
enum State
{
INIT, // 初始化状态
HOLD, // 暂停状态
EXEC, // 执行中状态
TERM, // 结束状态
READY, // 可执行(就绪)状态
EXCEPT, // 异常状态
};
private:
Fiber();
public:
Fiber(std::function cb, size_t stacksize = 0, bool use_caller = false);
~Fiber();
void reset(std::function cb);
void swapIn();
void swapOut();
void call();
void back();
uint64_t getId() const { return m_id; }
void setState(State s) { m_state = s; }
State getState() const { return m_state; }
public:
static void SetThis(Fiber* f);
static Fiber::ptr GetThis();
static void YieldToReady();
static void YieldToHold();
static uint64_t TotalFibers();
static uint64_t GetFiberId();
static void MainFunc();
static void CallerMainFunc();
private:
/// 协程id
uint64_t m_id = 0;
/// 协程运行栈大小
uint32_t m_stacksize = 0;
/// 协程状态
State m_state = INIT;
/// 协程上下文
ucontext_t m_ctx;
/// 协程运行栈指针
void* m_stack = nullptr;
/// 协程运行函数
std::function m_cb;
};
}
#endif
#include "fiber.h"
#include "config.h"
#include "scheduler.h"
#include "macro.h"
#include
namespace sylar
{
static Logger::ptr g_logger = SYLAR_LOG_NAME("system");
static std::atomic s_fiber_id{0}; // 用于生成协程ID
static std::atomic s_fiber_count{0}; // 用于统计协程数
static thread_local Fiber* t_fiber = nullptr; // 当前线程正在运行的协程
static thread_local Fiber::ptr t_threadFiber = nullptr; // 当前线程的主协程
static ConfigVar::ptr g_fiber_stack_size =
Config::Lookup("fiber.stack_size", 128*1024, "fiber stack size");
class MallocStackAllocator
{
public:
static void* Alloc(size_t size)
{
return malloc(size);
}
static void Dealloc(void* vp, size_t size)
{
return free(vp);
}
};
using StackAllocator = MallocStackAllocator;
void Fiber::SetThis(Fiber* f)
{
t_fiber = f;
}
/**
* @brief 返回当前线程正在执行的协程。
* 如果当前线程尚未创建协程,则创建该线程的第一个协程,且该协程为当前线程的主协程,其他协程都通过这个协程来调度。
*/
Fiber::ptr Fiber::GetThis()
{
if(t_fiber)
{
return t_fiber->shared_from_this();
}
Fiber::ptr main_fiber(new Fiber);
SYLAR_ASSERT(t_fiber == main_fiber.get());
t_threadFiber = main_fiber;
return t_fiber->shared_from_this();
}
/**
* @brief 将当前协程切换到后台,并设置为 READY 状态
*/
void Fiber::YieldToReady()
{
Fiber::ptr cur = GetThis();
SYLAR_ASSERT(cur->m_state == EXEC);
cur->m_state = READY;
cur->swapOut();
}
/**
* @brief 将当前协程切换到后台,并设置为 HOLD 状态
*/
void Fiber::YieldToHold()
{
Fiber::ptr cur = GetThis();
SYLAR_ASSERT(cur->m_state == EXEC);
cur->swapOut();
}
uint64_t Fiber::TotalFibers()
{
return s_fiber_count;
}
uint64_t Fiber::GetFiberId()
{
if(t_fiber)
{
t_fiber->getId();
}
return 0;
}
/**
* @brief 协程执行函数,执行完成返回到线程的主协程
*/
void Fiber::MainFunc()
{
Fiber::ptr cur = GetThis();
SYLAR_ASSERT(cur);
try
{
cur->m_cb();
cur->m_cb = nullptr;
cur->m_state = TERM;
}
catch(std::exception& ex)
{
cur->m_state = EXCEPT;
SYLAR_LOG_ERROR(g_logger) << "Fiber Except: " << ex.what()
<< " fiber_id=" << cur->getId()
<< std::endl
<< sylar::BacktraceToString();
}
catch(...)
{
cur->m_state = EXCEPT;
SYLAR_LOG_ERROR(g_logger) << "Fiber Except"
<< " fiber_id=" << cur->getId()
<< std::endl
<< sylar::BacktraceToString();
}
auto raw_ptr = cur.get();
cur.reset(); // 引用计数减1
raw_ptr->swapOut();
SYLAR_ASSERT2(false, "never reach fiber_id=" + std::to_string(raw_ptr->getId()));
}
/**
* @brief 协程执行函数,执行完成返回到线程调度协程
* 协程的调度是运行在 caller 线程的子协程中,
* 因此这里返回到的是 caller 线程的主协程中。
*/
void Fiber::CallerMainFunc()
{
Fiber::ptr cur = GetThis();
try
{
cur->m_cb();
cur->m_cb = nullptr;
cur->m_state = TERM;
}
catch(std::exception& ex)
{
cur->m_state = EXCEPT;
SYLAR_LOG_ERROR(g_logger) << "Fiber Except: " << ex.what()
<< " fiber_id=" << cur->getId()
<< std::endl
<< sylar::BacktraceToString();
}
catch(...)
{
cur->m_state = EXCEPT;
SYLAR_LOG_ERROR(g_logger) << "Fiber Except"
<< " fiber_id=" << cur->getId()
<< std::endl
<< sylar::BacktraceToString();
}
auto raw_ptr = cur.get();
cur.reset();
raw_ptr->back();
SYLAR_ASSERT2(false, "never reach fiber_id=" + std::to_string(raw_ptr->getId()));
}
/**
* @brief 只用于创建线程的第一个协程,也就是线程主函数对应的协程
*/
Fiber::Fiber()
{
m_state = EXEC;
SetThis(this);
if(getcontext(&m_ctx))
{
SYLAR_ASSERT2(false, "getcontext");
}
++s_fiber_count;
SYLAR_LOG_DEBUG(g_logger) << "Fiber::Fiber main";
}
Fiber::Fiber(std::function cb, size_t stacksize, bool use_caller)
: m_id(++s_fiber_id)
, m_cb(cb)
{
++s_fiber_count;
m_stacksize = stacksize ? stacksize : g_fiber_stack_size->getValue();
m_stack = StackAllocator::Alloc(m_stacksize);
if(getcontext(&m_ctx))
{
SYLAR_ASSERT2(false, "getcontext");
}
m_ctx.uc_link = nullptr;
m_ctx.uc_stack.ss_sp = m_stack;
m_ctx.uc_stack.ss_size = m_stacksize;
if(!use_caller)
{
makecontext(&m_ctx, &Fiber::MainFunc, 0);
}
else
{
makecontext(&m_ctx, &Fiber::CallerMainFunc, 0);
}
SYLAR_LOG_DEBUG(g_logger) << "Fiber::Fiber id=" << m_id;
}
Fiber::~Fiber()
{
--s_fiber_count;
if(m_stack)
{
StackAllocator::Dealloc(m_stack, m_stacksize);
}
else
{
SYLAR_ASSERT(!m_cb);
SYLAR_ASSERT(m_state == EXEC);
Fiber* cur = t_fiber;
if(cur == this)
{
SetThis(nullptr);
}
}
SYLAR_LOG_DEBUG(g_logger) << "Fiber::~Fiber id=" << m_id
<< " total=" << s_fiber_count;
}
/**
* @brief 重置协程,即重复利用已结束的协程,复用其栈空间,创建新协程
*/
void Fiber::reset(std::function cb)
{
m_cb = cb;
if(getcontext(&m_ctx))
{
SYLAR_ASSERT2(false, "getcontext");
}
m_ctx.uc_link = nullptr;
m_ctx.uc_stack.ss_sp = m_stack;
m_ctx.uc_stack.ss_size = m_stacksize;
makecontext(&m_ctx, &Fiber::MainFunc, 0);
m_state = INIT;
}
/**
* @brief 将当前协程切换到运行状态
*/
void Fiber::swapIn()
{
SetThis(this);
m_state = EXEC;
if(swapcontext(&Scheduler::GetMainFiber()->m_ctx, &m_ctx))
{
SYLAR_ASSERT2(false, "swapcontext");
}
}
/**
* @brief 将当前协程切换到后台
*/
void Fiber::swapOut()
{
SetThis(Scheduler::GetMainFiber());
if(swapcontext(&m_ctx, &Scheduler::GetMainFiber()->m_ctx))
{
SYLAR_ASSERT2(false, "swapcontext");
}
}
/**
* @brief 将当前线程切换到执行状态
* 当前线程的主协程切换到当前协程
*/
void Fiber::call()
{
SetThis(this);
m_state = EXEC;
if(swapcontext(&t_threadFiber->m_ctx, &m_ctx))
{
SYLAR_ASSERT2(false, "swapcontext");
}
}
/**
* @brief 将当前线程切换到后台
* 当前协程切换为当前线程的主协程
*/
void Fiber::back()
{
SetThis(this);
if(swapcontext(&m_ctx, &t_threadFiber->m_ctx))
{
SYLAR_ASSERT2(false, "swapcontext");
}
}
}
基于redis的参数查询服务