专栏首页racaljk基于windows fiber的协程(coroutine)实现

基于windows fiber的协程(coroutine)实现

一个非常简单,但是实用的协程实现,使用Windows的*Fiber函数族(linux可以稍微改一下用*context函数族)。

fco.h

#ifndef _MSC_VER
#error "this fast coroutine library only supports MSVC building chain"
#endif

#include <Windows.h>
#include <cstdint>
#include <map>

namespace fco {
static constexpr int ERR_NOT_EXIST_CO = -1;

enum Status {
  READY,  // Set up to READY when fco::newco() called
  AWAIT,  // Set up to AWAIT when fco::yield() called
};

struct Scheduler;
struct Coroutine;

struct Coroutine {
  void (*task)(Scheduler*, void*);
  void* userData;
  char status;
  LPVOID winFiber;
};

struct Scheduler {
  std::map<int, Coroutine*> coroutines;
  int currentIdx;
  LPVOID main;
};

void __stdcall __entry(LPVOID lpParameter);

Scheduler* initialize();

void destroy(Scheduler* s);

int newco(Scheduler* scheduler, void (*task)(Scheduler*, void*),
          void* userData);

void resume(Scheduler* s, int coid);

void yield(Scheduler* scheduler);

int current(Scheduler* scheduler);

}  // namespace fco

fco.cpp

#include "fco.h"

// Initialize fco library, return a global scheduler
fco::Scheduler* fco::initialize() {
  Scheduler* sched = new Scheduler;
  sched->currentIdx = ERR_NOT_EXIST_CO;
  sched->main = ConvertThreadToFiber(NULL);
  return sched;
}

// Release all resources
void fco::destroy(Scheduler* s) {
  for (auto& c : s->coroutines) {
    DeleteFiber(c.second->winFiber);
  }
  delete s;
}

// This is should NEVER BE called on user land
void __stdcall fco::__entry(LPVOID lpParameter) {
  // Execute the task of current coroutine
  Scheduler* s = (Scheduler*)lpParameter;
  Coroutine* currentCo = s->coroutines[s->currentIdx];
  (currentCo->task)(s, currentCo->userData);

  // Clean up executed task
  s->coroutines.erase(s->coroutines.find(s->currentIdx));
  s->currentIdx = ERR_NOT_EXIST_CO;
  currentCo->status = Status::READY;
  DeleteFiber(currentCo->winFiber);
  delete currentCo;

  // Switch to entry function
  SwitchToFiber(s->main);
}

// Create new coroutine and return an unique identity
int fco::newco(Scheduler* scheduler, void (*task)(fco::Scheduler*, void*),
               void* userData) {
  Coroutine* co = new Coroutine;
  co->task = task;
  co->userData = userData;
  co->winFiber = CreateFiber(0, __entry, scheduler);
  if (co->winFiber == NULL) {
    return ERR_NOT_EXIST_CO;
  }
  co->status = Status::READY;
  int newCoId =
      scheduler->coroutines.size() != 0
          ? scheduler->coroutines.end().operator--().operator*().first + 1
          : 0;
  scheduler->coroutines.insert(std::make_pair(newCoId, co));
  return newCoId;
}

// Resume suspended coroutine by given coid
void fco::resume(fco::Scheduler* scheduler, int coid) {
  if (coid < 0) {
    return;
  }
  Coroutine* co = scheduler->coroutines[coid];
  if (co->status == Status::READY || co->status == Status::AWAIT) {
    scheduler->currentIdx = coid;
    scheduler->coroutines[scheduler->currentIdx]->status = Status::AWAIT;
    co->status = Status::READY;
    SwitchToFiber(co->winFiber);
  }
}

// Yield CPU time to main coroutine
void fco::yield(fco::Scheduler* scheduler) {
  Coroutine* co = scheduler->coroutines[scheduler->currentIdx];
  co->status = Status::AWAIT;

  scheduler->currentIdx = ERR_NOT_EXIST_CO;
  SwitchToFiber(scheduler->main);
}

// Get current running coroutine identity
int fco::current(Scheduler* scheduler) { return scheduler->currentIdx; }

example

  • hello world
#include <cstdlib>
#include <ctime>
#include <iostream>
#include <vector>
#include "fco.h"

void bar(fco::Scheduler* s, void* param) {
  for (int i = 0; i < 5; i++) {
    std::cout << "world\n";
    fco::yield(s);
  }
}

int main() {
  fco::Scheduler* s = fco::initialize();
  int barFunc = fco::newco(s, bar, nullptr);
  for (int i = 0; i < 5; i++) {
    std::cout << "hello\n";
    fco::resume(s, barFunc);
  }
  fco::destroy(s);
  return 0;
}
  • 生产者消费者模型
#include <cstdlib>
#include <ctime>
#include <iostream>
#include <vector>
#include "fco.h"

std::vector<int> vec;

void producer(fco::Scheduler* s, void* param) {
  while (vec.size() < 10) {
    int resource = rand();
    std::cout << "Producing " << resource << "\n";
    vec.push_back(resource);
  }
  fco::resume(s, (int)param);
  fco::yield(s);
}

void consumer(fco::Scheduler* s, void* param) {
  int producerCo = fco::newco(s, producer, (void*)fco::current(s));

  while (true) {
    while (!vec.empty()) {
      int resource = vec.back();
      vec.pop_back();
      std::cout << "Consuming " << resource << "\n";
    }
    fco::resume(s, producerCo);
  }
}

void factory() {
  fco::Scheduler* s = fco::initialize();
  int consumerCo = fco::newco(s, consumer, nullptr);
  fco::resume(s, consumerCo);

  fco::destroy(s);
}

int main() {
  srand((int)time(0));
  factory();
  system("pause");
  return 0;
}

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • C语言中一些不被熟知的特性

    限定词restricted用于限定一个指针(如名,告诉编译器该指针的内存访问在任何情况下都只能通过该指针进行,其余指向无效.如

    racaljk
  • 探索C++对象模型

    只说C++对象模型在内存中如何分配这是不现实的,所以这里选择VS 2013作为调试环境具体探讨object在内存中分配情况.目录给出了具体要探讨的所有模型,正...

    racaljk
  • C++11新语法糖之尾置返回类型

    C++11的尾置返回类型初衷是为了方便复杂函数的声明和定义,但是当复杂度稍微提升一些的时候很明显能注意到这种设计的作用微乎其微.

    racaljk
  • iOS JavaScriptCore JS多参数--对应iOS写法

    iOS  js与webView交互。JavaScriptCore框架,具体的不多说。资料一大堆,说说一个很有趣的问题。

    ZY_FlyWay
  • 2018年奇虎360春招笔试题--玫瑰花

    郭耀华
  • 2018年奇虎360春招笔试题--玫瑰花

    这道题,第一感觉想用排列组合做,但是想了好久,没想到解决办法(刚刚考试的时候没有答出来)。后来想了一下应该使用动态规划来做。 我们首先分析一下情况: 1.当K>...

    郭耀华
  • 数据结构散列线性开型寻址(C++实现)插入,删除,查找

    插入x,若散列表已存在x,输出“Existed”,否则插入x到散列表中,输出所在的下标。 查询x,若散列表不含有x,输出“-1”,否则输出x对应下标。 删除...

    种花家的奋斗兔
  • 【C】用C语言提取bmp图片像素,并进行K-means聚类分析——容易遇到的问题

    关于bmp图片的格式,网上有很多文章,具体可以参考百度百科,也有例子程序。这里只提要注意的问题。 (1)结构体定义问题:首先按照百度百科介绍的定义了结构体,但是...

    ascii0x03
  • 第十三天、归并排序

    题目 用归并排序法对一组数据由小到大进行排序,数据分别为695、458、362、789、12、15、163、23、2、986。 1、程序分析     归...

    Jack_Cui
  • Problem G: STL——整理唱片(list的使用)

    这里用到了remove_if(op), 不得不说,这个很好用,意思是list中满足op这个条件的元素将会被全部移除

    _DIY

扫码关注云+社区

领取腾讯云代金券