• 基于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
    ";
        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
    ";
        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 << "
    ";
        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 << "
    ";
        }
        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;
    }
    
  • 相关阅读:
    P2324 [SCOI2005]骑士精神
    P4160 [SCOI2009]生日快乐
    BZOJ 1103: [POI2007]大都市meg
    P4289 [HAOI2008]移动玩具
    P2472 [SCOI2007]蜥蜴
    adb: command not found 解決方法(转载)
    Thinkpad x230i安装Ubuntu10.04发生no network devices available
    Ubuntu10.04安装与配置nfs服务器(转载)
    RTSP协议简介(转载)
    UIBC 小註解(转载)
  • 原文地址:https://www.cnblogs.com/ysherlock/p/10010106.html
Copyright © 2020-2023  润新知