Cross-Platform Coroutines in C++
by George F. Frazier

Example 1:
class CCoroutine {
public:
    CCoroutine();
    virtual ~CCoroutine();
    static CCoroutine *initialize();
    virtual void go() = 0; 
    virtual void resume(CCoroutine *next) = 0;
};

Example 2:
void CTargetPlatformCoroutine::resume(CCoroutine *other)
{
    if (other == this) return;
    assert(other->state_->fiber_);
    SwitchToFiber(other->state_->fiber_);
}

Example 3:
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
pthread_create(&state_->thread_, &attr, crstart, this->state_);
pthread_attr_destroy(&attr);

Example 4:
static void *crstart(void *thunk) {
  CCoroutineState *s = (CCoroutineState *) thunk;
  s->waitToGo();
  s->parent_->go();
  assert(0);
  return 0;
}

Example 5:
void CTargetPlatformCoroutine::resume(CCoroutine *other)
{
  if (other == this) return;
  // Tell the other guy to go.
  assert(other->state_);
  other->state_->signalToGo();
  // Wait until someone tells us to go.
  assert(state_);
  state_->waitToGo();
}

Example 6:
void PrintRoutine::go()
{
    int j;
    while (true)
    {
        for (j = 0; j < k; j++)
            cout << Word1[j];
        resume(rspc);
        for (j = k - 1; j >= 0; j--)
             cout << Word1[j];
        resume(rspc);
    }
} 



Listing One
#ifndef _CWinNTCoroutine_h_
#define _CWinNTCoroutine_h_
//////////////////////////////////////////
// CWinNTCoroutine.h
#ifdef _WIN32
#ifdef _COROUTINE_DLL
#define ExportStatus _declspec(dllexport)
#else
#define ExportStatus _declspec(dllimport)
#endif
#else
#define ExportStatus
#endif
#include <assert.h>
/////////////////////////////////////////////////////////
class ExportStatus CTargetPlatformCoroutine : public CCoroutine {
public:
    CTargetPlatformCoroutine(unsigned stackSizeInBytes = DEFSTACKSIZE);
    virtual ~CTargetPlatformCoroutine();
    virtual void go(){assert(0);}
    virtual void resume(CCoroutine *next);
};
#endif
/////////////////////////////////////////////////////
// CWinNTCoroutine.cpp
/////////////////////////////////////////////////////
// CCoroutineNT.cpp
// NT implementation of Coroutine interface.
#if (!defined(_WINNT) || !defined(_WIN32_WINNT))
#error "This module is only for Windows NT."
#endif

#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <assert.h>

#include "CCoroutine.h"
#include "CWinNTCoroutine.h"

///////////////////////////////////////////////////////////
class CCoroutineState
{
public:
    CCoroutineState() : fiber_(0) {}
    LPVOID fiber_;
};
static void WINAPI startCoroutine(PVOID lpParameter)
{
    CCoroutine *c = (CCoroutine *) lpParameter;
    c->go();
    assert(0);
}
///////////////////////////////////////////////////////////
CTargetPlatformCoroutine::CTargetPlatformCoroutine(unsigned 
                            stackSizeInBytes) : CCoroutine(INITCOROUTINE)
{
    state_ = new CCoroutineState();
    if (stackSizeInBytes == INITCOROUTINE)
        return;
    state_->fiber_ = CreateFiber(0, startCoroutine, this);
    assert(state_->fiber_);
}
CTargetPlatformCoroutine::~CTargetPlatformCoroutine()
{
    assert(state_->fiber_);
    DeleteFiber(state_->fiber_);
    state_->fiber_ = 0;
    delete state_;
}
void CTargetPlatformCoroutine::resume(CCoroutine *other)
{
    if (other == this) return;

    assert(other->state_->fiber_);
    SwitchToFiber(other->state_->fiber_);
}
/////////////////////////////////////////////////////////
// This can't be pure virtual because it is static
CCoroutine *CCoroutine::initialize()
{
    CCoroutine *thisCoroutine = new CTargetPlatformCoroutine();
    LPVOID thisFiber = ConvertThreadToFiber(thisCoroutine);
    assert(thisFiber != 0);
    thisCoroutine->state_->fiber_ = thisFiber;
    return thisCoroutine;
}


Listing Two
////////////////////////////////////////////
// CSolarisCoroutine.h
////////////////////////////////////////////

#ifndef _CSolarisCoroutine_h_#define _CSolarisCoroutine_h_#include 
          <assert.h>class CTargetPlatformCoroutine : public CCoroutine {
public:
    CTargetPlatformCoroutine(unsigned stackSizeInBytes = DEFSTACKSIZE);
    virtual ~CTargetPlatformCoroutine();
    virtual void go() {assert(0);}
    virtual void resume(CCoroutine *next);
};
#endif

////////////////////////////////////////////
// CSolarisCoroutine.cpp
////////////////////////////////////////////
#include "CCoroutine.h"
#include "CSolarisCoroutine.h"

#include <pthread.h>
#include <assert.h>

////////////////////////////////////////////////
class CCoroutineState
{
public:
  CCoroutineState(CCoroutine *parent)
  : parent_(parent), go_(false)
  {
      pthread_mutex_init(&go_mutex_, 0);
      pthread_cond_init(&go_cond_, 0);
  }
  CCoroutine *parent_;
  pthread_t thread_;
  int go_;
  pthread_mutex_t go_mutex_;
  pthread_cond_t go_cond_;

  inline void waitToGo() {
    pthread_mutex_lock(&go_mutex_);
    while (!go_)
      pthread_cond_wait(&go_cond_, &go_mutex_);
    go_ = false;
    pthread_mutex_unlock(&go_mutex_);
  }
  inline void signalToGo() {
    // Tell the other guy to go.
    pthread_mutex_lock(&go_mutex_);
    go_ = true;
    pthread_cond_signal(&go_cond_);
    pthread_mutex_unlock(&go_mutex_);
  }
};
static void *crstart(void *thunk) {
  CCoroutineState *s = (CCoroutineState *) thunk;
  s->waitToGo();
  s->parent_->go();
  assert(0);
  return 0;
}
/////////////////////////////////////////////////////////////
CTargetPlatformCoroutine::CTargetPlatformCoroutine(unsigned 
                 requestedStackSizeInBytes) : CCoroutine(INITCOROUTINE)
{
  state_ = new CCoroutineState(this); 
  if (requestedStackSizeInBytes == INITCOROUTINE)
    return;
    pthread_attr_t attr;
    pthread_attr_init(&attr);
#ifdef _POSIX_THREAD_ATTR_STACKSIZE
    size_t stackSize = 8192;
    if (requestedStackSizeInBytes != DEFSTACKSIZE)
      stackSize = requestedStackSizeInBytes;
#ifdef PTHREAD_STACK_MIN                    // Solaris is missing this.
    if (stackSize < PTHREAD_STACK_MIN)
      stackSize = PTHREAD_STACK_MIN;
#endif
    pthread_attr_setstacksize(&attr, stackSize);
#endif
    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
    pthread_create(&state_->thread_, &attr, crstart, this->state_);
    pthread_attr_destroy(&attr);
}
CTargetPlatformCoroutine::~CTargetPlatformCoroutine()
{
  state_ = 0;
}
void CTargetPlatformCoroutine::resume(CCoroutine *other)
{
  if (other == this) return;

  // Tell the other guy to go.
  assert(other->state_);
  other->state_->signalToGo();

  // Wait until someone tells us to go.
  assert(state_);
  state_->waitToGo();
}
///////////////////////////////////////////////////////////
CCoroutine *CCoroutine::initialize()
{
  return new CTargetPlatformCoroutine();
}


Listing Three
// oddword.cpp : Defines the entry point for the console application. 
#include <iostream.h>
#include <string>
#include "CCoroutine.h"
#ifdef _WIN32
#include "CWinNTCoroutine.h"
#else
#include "CSolarisCoroutine.h"
#endif

CTargetPlatformCoroutine *rwrd;
CTargetPlatformCoroutine *rspc;

CTargetPlatformCoroutine *prtn;
std::string InputString;
int InputPointer = 0;
int k;
char Word1[20];

class ReadWord : public CTargetPlatformCoroutine
{
public:
    ReadWord() : CTargetPlatformCoroutine() {this->initialize();}
    virtual void go();
};
class ReadSpace : public CTargetPlatformCoroutine
{
public:
    ReadSpace() : CTargetPlatformCoroutine() {this->initialize();}
    virtual void go();
};
class PrintRoutine : public CTargetPlatformCoroutine
{
public:
    PrintRoutine() : CTargetPlatformCoroutine() {this->initialize();}
    virtual void go();
};
void ReadWord::go()
{
    char ch;
    while (true)
    {
        k = 0;
        memset(Word1, 0, 20);
        ch = InputString[InputPointer];
        while (ch != ' ' && ch != '.' && k < 20)
        {
            Word1[k] = ch;
            k++;
            InputPointer++;
            ch = InputString[InputPointer];
        }
        resume(prtn);

    }
}
void ReadSpace::go()
{
    while (true)
    {
         bool bSawSpace = false;
         if (InputPointer >= InputString.length())
             return;
         // assume that this thing ends with '.'
         while (InputString[InputPointer] == ' ')
         {
             InputPointer++;
             bSawSpace = true;

        }
         if (InputString[InputPointer] == '.')
         {
             cout << "." << endl;
             exit(0);
         }
         else
         {
             if (bSawSpace)
                 cout << " ";
             resume(rwrd);
         }
    }
}
void PrintRoutine::go()
{
    int j;
    while (true)
    {
        for (j = 0; j < k; j++)
            cout << Word1[j];
        resume(rspc);
        for (j = k - 1; j >= 0; j--)
            cout << Word1[j];
        resume(rspc);
    }
}
// Let the user enter a string on the command line. Store it in memory in 
// the variable InputString then read characters one at a time from 
// InputString to solve the odd word problem. Make sure to enclose your 
// command-line argument in double quotes
int main(int argc, char* argv[])
{
    if (argc != 2)
    {
        InputString = "whats      the     matter with Kansas.";
    }
    else
    {
        InputString = argv[1];
    }
    rwrd = new ReadWord();
    rspc = new ReadSpace();
    prtn = new PrintRoutine();

    rspc->go();
    return 0;
}





7

