←back to thread

135 points jmillikin | 1 comments | | HN request time: 0.204s | source
Show context
adinisom ◴[] No.44547674[source]
My favorite trick in C is a light-weight Protothreads implemented in-place without dependencies. Looks something like this for a hypothetical blinky coroutine:

  typedef struct blinky_state {
    size_t pc;
    uint64_t timer;
    ... variables that need to live across YIELDs ...
  } blinky_state_t;
  
  blinky_state_t blinky_state;
  
  #define YIELD() s->pc = __LINE__; return; case __LINE__:;
  void blinky(void) {
    blinky_state_t *s = &blinky_state;
    uint64_t now = get_ticks();
    
    switch(s->pc) {
      while(true) {
        turn_on_LED();
        s->timer = now;
        while( now - s->timer < 1000 ) { YIELD(); }
        
        turn_off_LED();
        s->timer = now;
        while( now - s->timer < 1000 ) { YIELD(); }
      }
    }
  }
  #undef YIELD
Can, of course, abstract the delay code into it's own coroutine.

Your company is probably using hardware containing code I've written like this.

What's especially nice that I miss in other languages with async/await is ability to mix declarative and procedural code. Code you write before the switch(s->pc) statement gets run on every call to the function. Can put code you want to be declarative, like updating "now" in the code above, or if I have streaming code it's a great place to copy data.

replies(4): >>44547719 #>>44547827 #>>44548121 #>>44548762 #
1. syncurrent ◴[] No.44548762[source]
In `proto_activities` this blinking would look like this:

  pa_activity (Blinker, pa_ctx_tm(), uint32_t onMs, uint32_t offMs) {
    pa_repeat {
      turn_on_LED();
      pa_delay_ms (onMs);
  
      turn_off_LED();
      pa_delay_ms (offMs);
    }
  } pa_end
Here the activity definition automatically creates the structure to hold the pc, timer and other variables which would outlast a single tick.