Lecture 4

SFR1811·2022년 1월 19일
0

Coroutine

Coroutine의 구성요소

  1. execution location
    • starting at the beginning of the coroutine and remembered at each suspend
  2. execution state
    • each coroutine has its own stack!
  3. execution status
    • active. inactive, terminated


coroutine executes synchronously with other coroutines!
i.e. we are not doing concurrency yet

in this case, a single thread is jumping back and forth between routines but with multiple stacks

There is two types of coroutines
1. semi-coroutine
2. full-coroutine


Semi-Coroutine

Fibonacci Sequence Example

a fibonacci function that generates nth fibonacci number on nth call - with no paramater

Coroutine is like a class

** NEED TO BE COMPILED WITH u++

_Coroutine Fibonacci{
  int fn;                           // communication variable
  void main(){
    int fn1, fn2;
    fn = 0; fn1 = fn;
    suspend();                      // return to last resume
    fn = 1; fn2 = fn1; fn1 = fn;
    suspend();                      // return to last resume
    for(;;){
      fn = fn1 + fn2; fn2 = fn1; fn1 = fn;
      suspend();                    // return to last resume
    }
  }
 public:
  int operator()(){                 // functor
    resume();                       // transfer to last suspend
    return fn;
  }
}

Public routine is like an interface routine that protects inside mechanism from outside.
We can create multiple instances of coroutine because its a class!

Usage:

int main(){
  Fibonacci f1, f2;    // multiple instances
  for(int i = 1; i <= 10; i++){
    cout << f1() << " " << f2() << endl;
  }
}

The reason why ()() functor is not in the stacks of coroutine is because the coroutine has not resumed yet

resume and suspend is a context switch not a call and return so coroutine only remembers last suspend and last resume.

the stacks of coroutine is created on first resume (cocall)

Just like any other object, coroutine is also object and it is destructed from stack once it is off the scope. The stack in coroutine also gets unwind and destructors of any objects in that stacks also gets properly called

* do not use catch(...) in coroutine. this will catch cleanup exception which is used to force stack unwinding on destruction


input formatter example

input:
abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz

output:
abcd efgh ijkl mnop qrst
uvwx yzab cdef ghij klmn
opqr stuv wxyz

without coroutine:

int main(){
  int g,b;
  char ch;
  cin >> noskipws;                // turn off white space skipping
  for(;;){
    for (g = 0; g < 5; g++){
      for(b = 0; b < 4; b++){
        for(;;){                  // this is to ignore newline char and eof
          cin >> ch;              // read 1 char
   if (cin.fail()) goto fini;     // on eof, do multi level exit
          if (ch != '\n') break;  // ignore newline
        }
        cout << ch;               // print char
      }
      cout << " ";                // print block separator
    }
    cout << endl;                 // print group separator
  }
 fini: ;
  if(g != 0 || b != 0) cout << endl;
}

with coroutine:

_Coroutine Format{
  int g,b;
  char ch;
  void main(){
    for(;;){
      for (g = 0; g < 5; g++){
        for(b = 0; b < 4; b++){
          for(;;){                  // this is to ignore newline char and eof
            **suspend();**
            if (ch != '\n') break;  // ignore newline
          }
          cout << ch;               // print char
        }
        cout << " ";                // print block separator
      }
      cout << endl;                 // print group separator
    }  
  }
 public:
  Format() {**resume();**}              // start coroutine
  ~Format() {if( g != 0 || b != 0 ) cout << endl;}
  void ptr(char ch) {Format::ch = ch; **resume();**}
}

usage:

int main(){
  Format fmt;
  char ch;
  cin >> noskipws;
  for(;;){
    cin >> ch;
   if(cin.fail()) break;
    fmt.prt(ch);
  }
}

Correct Coroutine Usage

  • eliminate computation of flag variable retaining information about execution state
    - don't do stuff that coroutine is already doing

  • So basically anything that gets changed in linear maner as resume() is called can be replaced with "Zen" of coroutine!

tips

  1. write program as if you are not using coroutine
  2. take the code and put it in a coroutine wrapper
  3. replace any input or output with suspend();
  4. add interface function for resume(); and return communication variable from it
  • this method does not for advanced coroutine :(

uBaseCoroutine

every coroutine inharites from uBaseCoroutine

_Coroutine uBaseCoroutine{
 protected:
  void resume();                          //context switch to this
  void suspend();                         //context switch to last resumer
 public:
  uBaseCoroutine();
  uBaseCoroutine(unsigned int stackSize); // get stack size
  void verify();                          // check stack
  const char * setName(const char * name);// printed in error message
  const char * getName() const;
  uBaseCoroutine & starter() const;       // coroutine performing first resume
  uBaseCoroutine & resumer() const;       // coroutine performing last resume
}
profile
3B CS

0개의 댓글