调度
class Reader : public ReaderBase
template <typename MessageT>
bool Reader<MessageT>::Init() {
````略
std::function<void(const std::shared_ptr<MessageT>&)> func;
if (reader_func_ != nullptr) {
func = [this](const std::shared_ptr<MessageT>& msg) {
this->Enqueue(msg);
this->reader_func_(msg);
};
} else {
func = [this](const std::shared_ptr<MessageT>& msg) { this->Enqueue(msg); };
}
auto sched = scheduler::Instance();
croutine_name_ = role_attr_.node_name() + "_" + role_attr_.channel_name();
auto dv = std::make_shared<data::DataVisitor<MessageT>>(
role_attr_.channel_id(), pending_queue_size_);
// Using factory to wrap templates.
croutine::RoutineFactory factory =
croutine::CreateRoutineFactory<MessageT>(std::move(func), dv);
if (!sched->CreateTask(factory, croutine_name_)) {
AERROR << "Create Task Failed!";
init_.exchange(false);
return false;
}
````略
}
- auto dv = std::make_shared< data::DataVisitor>(
role_attr_.channel_id(), pending_queue_size_);
template <typename M0>//DataVisito构造函数
class DataVisitor<M0, NullType, NullType, NullType> : public DataVisitorBase
DataVisitor(uint64_t channel_id, uint32_t queue_size)
: buffer_(channel_id, new BufferType<M0>(queue_size)) {
DataDispatcher<M0>::Instance()->AddBuffer(buffer_);
data_notifier_->AddNotifier(buffer_.channel_id(), notifier_);
}
- croutine::RoutineFactory factory =
croutine::CreateRoutineFactory(std::move(func), dv);
template <typename M0, typename F>
RoutineFactory CreateRoutineFactory(
F&& f, const std::shared_ptr<data::DataVisitor<M0>>& dv) {
RoutineFactory factory;
factory.SetDataVisitor(dv);
factory.create_routine = [=]() {
return [=]() {
std::shared_ptr<M0> msg;
for (;;) {
CRoutine::GetCurrentRoutine()->set_state(RoutineState::DATA_WAIT);
if (dv->TryFetch(msg)) {
f(msg);
CRoutine::Yield(RoutineState::READY);
} else {
CRoutine::Yield();
}
}
};
};
return factory;
}
auto sched = scheduler::Instance();
namespace {
std::atomic<Scheduler*> instance = {nullptr};
std::mutex mutex;
}
Scheduler* Instance() {
Scheduler* obj = instance.load(std::memory_order_acquire);
if (obj == nullptr) {
std::lock_guard<std::mutex> lock(mutex);
obj = instance.load(std::memory_order_relaxed);
if (obj == nullptr) {
std::string policy("classic");
std::string conf("conf/");
conf.append(GlobalData::Instance()->ProcessGroup()).append(".conf");
auto cfg_file = GetAbsolutePath(WorkRoot(), conf);
apollo::cyber::proto::CyberConfig cfg;
if (PathExists(cfg_file) && GetProtoFromFile(cfg_file, &cfg)) {
policy = cfg.scheduler_conf().policy();
} else {
AWARN << "Pls make sure schedconf exist and which format is correct.\n";
}
if (!policy.compare("classic")) {
obj = new SchedulerClassic();
} else if (!policy.compare("choreography")) {
obj = new SchedulerChoreography();
} else {
AWARN << "Invalid scheduler policy: " << policy;
obj = new SchedulerClassic();
}
instance.store(obj, std::memory_order_release);
}
}
return obj;
}
obj = new SchedulerClassic();
class SchedulerClassic SchedulerClassic构造函数
SchedulerClassic::SchedulerClassic() {
// get sched config
std::string conf("conf/");
conf.append(GlobalData::Instance()->ProcessGroup()).append(".conf");
auto cfg_file = GetAbsolutePath(WorkRoot(), conf);
apollo::cyber::proto::CyberConfig cfg;
if (PathExists(cfg_file) && GetProtoFromFile(cfg_file, &cfg)) {
classic_conf_ = cfg.scheduler_conf().classic_conf();
for (auto& group : classic_conf_.groups()) {
auto& group_name = group.name();
for (auto task : group.tasks()) {
task.set_group_name(group_name);
cr_confs_[task.name()] = task;
}
}
} else {
// if do not set default_proc_num in scheduler conf
// give a default value
uint32_t proc_num = 2;
auto& global_conf = GlobalData::Instance()->Config();
if (global_conf.has_scheduler_conf() &&
global_conf.scheduler_conf().has_default_proc_num()) {
proc_num = global_conf.scheduler_conf().default_proc_num();
}
task_pool_size_ = proc_num;
auto sched_group = classic_conf_.add_groups();
sched_group->set_name(DEFAULT_GROUP_NAME);
sched_group->set_processor_num(proc_num);
}
CreateProcessor();
}
if (!sched->CreateTask(factory, croutine_name_)) {
class Scheduler
bool Scheduler::CreateTask(const RoutineFactory& factory,
const std::string& name) {
return CreateTask(factory.create_routine(), name, factory.GetDataVisitor());
}
return CreateTask(factory.create_routine(), name, factory.GetDataVisitor());
class Scheduler
/*
//func=factory.create_routine = [=]() {
return [=]() {
std::shared_ptr<M0> msg;
for (;;) {
CRoutine::GetCurrentRoutine()->set_state(RoutineState::DATA_WAIT);
if (dv->TryFetch(msg)) {
f(msg);
CRoutine::Yield(RoutineState::READY);
} else {
CRoutine::Yield();
}
}
};
};
name=croutine_name_ = role_attr_.node_name() + "_" + role_attr_.channel_name();
*/
bool Scheduler::CreateTask(std::function<void()>&& func,
const std::string& name,
std::shared_ptr<DataVisitorBase> visitor) {
if (unlikely(stop_.load())) {
ADEBUG << "scheduler is stoped, cannot create task!";
return false;
}
//根据node名称创建任务ID
auto task_id = GlobalData::RegisterTaskName(name);
auto cr = std::make_shared<CRoutine>(func);
cr->set_id(task_id);
cr->set_name(name);
//分发协程任务
if (!DispatchTask(cr)) {
return false;
}
//注册唤醒任务
if (visitor != nullptr) {
visitor->RegisterNotifyCallback([this, task_id, name]() {
if (unlikely(stop_.load())) {
return;
}
this->NotifyProcessor(task_id);
});
}
return true;
}
auto cr = std::make_shared(func);
class CRoutine构造函数
CRoutine::CRoutine(const std::function<void()> &func) : func_(func) {
std::call_once(pool_init_flag, [&]() {
auto routine_num = 100;
auto &global_conf = common::GlobalData::Instance()->Config();
if (global_conf.has_scheduler_conf() &&
global_conf.scheduler_conf().has_routine_num()) {
routine_num = global_conf.scheduler_conf().routine_num();
}
context_pool.reset(new base::CCObjectPool<RoutineContext>(routine_num));
});
context_ = context_pool->GetObject();
if (context_ == nullptr) {
AWARN << "Maximum routine context number exceeded! Please check "
"[routine_num] in config file.";
context_.reset(new RoutineContext());
}
MakeContext(CRoutineEntry, this, context_.get());
state_ = RoutineState::READY;
updated_.test_and_set(std::memory_order_release);
}
context_pool.reset(new base::CCObjectPool(routine_num));
class CCObjectPool
struct Node {
T object;
Node *next;
};
struct alignas(2 * sizeof(Node *)) Head {
uintptr_t count;
Node *node;
};
template <typename T>
CCObjectPool<T>::CCObjectPool(uint32_t size) : capacity_(size) {
node_arena_ = static_cast<Node *>(CheckedCalloc(capacity_, sizeof(Node)));
FOR_EACH(i, 0, capacity_ - 1) { node_arena_[i].next = node_arena_ + 1 + i; }
node_arena_[capacity_ - 1].next = nullptr;
free_head_.store({0, node_arena_}, std::memory_order_relaxed);
}
context_ = context_pool->GetObject();
class CCObjectPool
template <typename T>
std::shared_ptr<T> CCObjectPool<T>::GetObject() {
Head free_head;
if (unlikely(!FindFreeHead(&free_head))) {
return nullptr;
}
auto self = this->shared_from_this();
return std::shared_ptr<T>(reinterpret_cast<T *>(free_head.node),
[self](T *object) { self->ReleaseObject(object); });
}
MakeContext(CRoutineEntry, this, context_.get());
void CRoutineEntry(void *arg) {
CRoutine *r = static_cast<CRoutine *>(arg);
r->Run();
CRoutine::Yield(RoutineState::FINISHED);
}
/*
constexpr size_t STACK_SIZE = 8 * 1024 * 1024;
constexpr size_t REGISTERS_SIZE = 56;
*/
void MakeContext(const func &f1, const void *arg, RoutineContext *ctx) {
ctx->sp = ctx->stack + STACK_SIZE - 2 * sizeof(void *) - REGISTERS_SIZE;
std::memset(ctx->sp, 0, REGISTERS_SIZE);
char *sp = ctx->stack + STACK_SIZE - 2 * sizeof(void *);
*reinterpret_cast<void **>(sp) = reinterpret_cast<void *>(f1);
sp -= sizeof(void *);
*reinterpret_cast<void **>(sp) = const_cast<void *>(arg);
}
if (!DispatchTask(cr)) {
return false;
}其中cr为:
auto cr = std::make_shared(func);
cr->set_id(task_id); // auto task_id = GlobalData::RegisterTaskName(name);
cr->set_name(name); //name=croutine_name_ = role_attr_.node_name() + "" + role_attr.channel_name();
class SchedulerClassic : public Scheduler
bool SchedulerClassic::DispatchTask(const std::shared_ptr<CRoutine>& cr) {
// we use multi-key mutex to prevent race condition
// when del && add cr with same crid
if (likely(id_cr_wl_.find(cr->id()) == id_cr_wl_.end())) {
{
std::lock_guard<std::mutex> wl_lg(cr_wl_mtx_);
if (id_cr_wl_.find(cr->id()) == id_cr_wl_.end()) {
id_cr_wl_[cr->id()];
}
}
}
std::lock_guard<std::mutex> lg(id_cr_wl_[cr->id()]);
{
WriteLockGuard<AtomicRWLock> lk(id_cr_lock_);
if (id_cr_.find(cr->id()) != id_cr_.end()) {
return false;
}
id_cr_[cr->id()] = cr;
}
if (cr_confs_.find(cr->name()) != cr_confs_.end()) {
ClassicTask task = cr_confs_[cr->name()];
cr->set_priority(task.prio());
cr->set_group_name(task.group_name());
} else {
// croutine that not exist in conf
cr->set_group_name(classic_conf_.groups(0).name());
}
// Check if task prio is reasonable.
if (cr->priority() >= MAX_PRIO) {
AWARN << cr->name() << " prio is greater than MAX_PRIO[ << " << MAX_PRIO
<< "].";
cr->set_priority(MAX_PRIO - 1);
}
// Enqueue task.
{
WriteLockGuard<AtomicRWLock> lk(
ClassicContext::rq_locks_[cr->group_name()].at(cr->priority()));
ClassicContext::cr_group_[cr->group_name()].at(cr->priority())
.emplace_back(cr);
}
PerfEventCache::Instance()->AddSchedEvent(SchedPerf::RT_CREATE, cr->id(),
cr->processor_id());
ClassicContext::Notify(cr->group_name());
return true;
}
ClassicContext::Notify(cr->group_name());
class ClassicContext
void ClassicContext::Notify(const std::string& group_name) {
cv_wq_[group_name].notify_one();
}
scheduler_conf {
policy: "classic"
classic_conf {
groups: [
{
name: "group1"
processor_num: 16
affinity: "range"
cpuset: "0-7,16-23"
processor_policy: "SCHED_OTHER"
processor_prio: 0
tasks: [
{
name: "ABC"
prio: 2
},{
name: "XYZ"
prio: 1
}
]
},{
name: "group2"
processor_num: 16
affinity: "1to1"
cpuset: "8-15,24-31"
processor_policy: "SCHED_OTHER"
processor_prio: 0
tasks: [
{
name: "MMN"
prio: 0
},{
name: "NXX"
prio: 1
}
]
}
]
}
}
CreateProcessor();
class SchedulerClassic
void SchedulerClassic::CreateProcessor() {
for (auto& group : classic_conf_.groups()) {
auto& group_name = group.name();
auto proc_num = group.processor_num();
if (task_pool_size_ == 0) {
task_pool_size_ = proc_num;
}
auto& affinity = group.affinity();
auto& processor_policy = group.processor_policy();
auto processor_prio = group.processor_prio();
std::vector<int> cpuset;
ParseCpuset(group.cpuset(), &cpuset);
ClassicContext::cr_group_[group_name];
ClassicContext::rq_locks_[group_name];
ClassicContext::mtx_wq_[group_name];
ClassicContext::cv_wq_[group_name];
for (uint32_t i = 0; i < proc_num; i++) {
auto ctx = std::make_shared<ClassicContext>();
ctx->SetGroupName(group_name);
pctxs_.emplace_back(ctx);
auto proc = std::make_shared<Processor>();
proc->BindContext(ctx);
proc->SetAffinity(cpuset, affinity, i);
proc->SetSchedPolicy(processor_policy, processor_prio);
processors_.emplace_back(proc);
}
}
}
proc->BindContext(ctx);
class Processor
void Processor::BindContext(const std::shared_ptr<ProcessorContext> &context) {
context_ = context;
std::call_once(thread_flag_,
[this]() { thread_ = std::thread(&Processor::Run, this); });
}
std::call_once(thread_flag_,
this { thread_ = std::thread(&Processor::Run, this); });
class Processor
void Processor::Run() {
tid_.store(static_cast<int>(syscall(SYS_gettid)));
while (likely(running_)) {
if (likely(context_ != nullptr)) {
auto croutine = context_->NextRoutine();
if (croutine) {
croutine->Resume();
croutine->Release();
} else {
context_->Wait();
}
} else {
std::unique_lock<std::mutex> lk(mtx_ctx_);
cv_ctx_.wait_for(lk, std::chrono::milliseconds(10));
}
}
}
auto croutine = context_->NextRoutine();
class ClassicContext
std::shared_ptr<CRoutine> ClassicContext::NextRoutine() {
if (unlikely(stop_)) {
return nullptr;
}
for (int i = MAX_PRIO - 1; i >= 0; --i) {
ReadLockGuard<AtomicRWLock> lk(rq_locks_[group_name_].at(i));
for (auto& cr : cr_group_[group_name_].at(i)) {
if (!cr->Acquire()) {
continue;
}
if (cr->UpdateState() == RoutineState::READY) {
PerfEventCache::Instance()->AddSchedEvent(SchedPerf::NEXT_RT, cr->id(),
cr->processor_id());
return cr;
}
if (unlikely(cr->state() == RoutineState::SLEEP)) {
if (!need_sleep_ || wake_time_ > cr->wake_time()) {
need_sleep_ = true;
wake_time_ = cr->wake_time();
}
}
cr->Release();
}
}
return nullptr;
}
croutine->Resume();
class CRoutine
RoutineState CRoutine::Resume() {
if (unlikely(force_stop_)) {
state_ = RoutineState::FINISHED;
return state_;
}
if (unlikely(state_ != RoutineState::READY)) {
AERROR << "Invalid Routine State!";
return state_;
}
current_routine_ = this;
PerfEventCache::Instance()->AddSchedEvent(
SchedPerf::SWAP_IN, id_, processor_id_, static_cast<int>(state_));
SwapContext(GetMainStack(), GetStack());
PerfEventCache::Instance()->AddSchedEvent(
SchedPerf::SWAP_OUT, id_, processor_id_, static_cast<int>(state_));
current_routine_ = nullptr;
return state_;
}
SwapContext(GetMainStack(), GetStack());
inline void SwapContext(char** src_sp, char** dest_sp) {
ctx_swap(reinterpret_cast<void**>(src_sp), reinterpret_cast<void**>(dest_sp));
}
extern "C" {
extern void ctx_swap(void**, void**) asm("ctx_swap");
};
extern void ctx_swap(void**, void**) asm(“ctx_swap”);
void CRoutineEntry(void *arg) {
CRoutine *r = static_cast<CRoutine *>(arg);
r->Run();
CRoutine::Yield(RoutineState::FINISHED);
}
/*
constexpr size_t STACK_SIZE = 8 * 1024 * 1024;
constexpr size_t REGISTERS_SIZE = 56;
*/
void MakeContext(const func &f1, const void *arg, RoutineContext *ctx) {
ctx->sp = ctx->stack + STACK_SIZE - 2 * sizeof(void *) - REGISTERS_SIZE;
std::memset(ctx->sp, 0, REGISTERS_SIZE);
char *sp = ctx->stack + STACK_SIZE - 2 * sizeof(void *);
*reinterpret_cast<void **>(sp) = reinterpret_cast<void *>(f1);
sp -= sizeof(void *);
*reinterpret_cast<void **>(sp) = const_cast<void *>(arg);
}
.globl ctx_swap
.type ctx_swap, @function
ctx_swap:
pushq %rdi
pushq %r12
pushq %r13
pushq %r14
pushq %r15
pushq %rbx
pushq %rbp
movq %rsp, (%rdi)
movq (%rsi), %rsp
popq %rbp
popq %rbx
popq %r15
popq %r14
popq %r13
popq %r12
popq %rdi
ret
croutine->Release();
class CRoutine
inline void CRoutine::Release() {
return lock_.clear(std::memory_order_release);
}
//注册唤醒任务
if (visitor != nullptr) {
visitor->RegisterNotifyCallback([ this, task_id, name ] ( ) {
if (unlikely(stop_.load())) {
return;
}
this->NotifyProcessor(task_id);
});
}
class DataVisitorBase
void RegisterNotifyCallback(std::function<void()>&& callback) {
notifier_->callback = callback;
}
this->NotifyProcessor(task_id);
class SchedulerClassic
bool SchedulerClassic::NotifyProcessor(uint64_t crid) {
if (unlikely(stop_)) {
return true;
}
{
ReadLockGuard<AtomicRWLock> lk(id_cr_lock_);
if (id_cr_.find(crid) != id_cr_.end()) {
auto cr = id_cr_[crid];
if (cr->state() == RoutineState::DATA_WAIT) {
cr->SetUpdateFlag();
}
ClassicContext::Notify(cr->group_name());
return true;
}
}
return false;
}
ClassicContext::Notify(cr->group_name());
class ClassicContext
void ClassicContext::Notify(const std::string& group_name) {
cv_wq_[group_name].notify_one();//context_->Wait();在函数Processor::Run()中
}