Skip to content

Latest commit

 

History

History

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 
 
 
 
 
 
 

Demo programs

Three complete, working programs, each one a different way of combining protothreads with work that happens outside them. They are built together, and are not built by default because two of the three need POSIX threads and the library itself does not:

cmake -S . -B build -DPROTOTHREAD_DEMOS=ON
cmake --build build
./build/ptpool && ./build/ptaio && ./build/pthelper
source binary what it shows
pool.c ptpool 500 protothreads over a fixed pool of 4 worker threads: parallel computation underneath, protothreads owning the sequencing
async_io.c ptaio concurrent asynchronous I/O and no POSIX threads at all -- one poll() loop turning completions into signals
helper_thread.c pthelper one throwaway POSIX thread per blocking call, created and joined by the protothread that needs it

All three obey the same rule, which is the thing to take away from them: a worker, a completion handler or an interrupt handler must never call pt_signal() itself. It records what happened, and the thread that owns protothread_run() turns that into a signal between protothread runs, where no protothread is halfway through enqueuing itself. pool.c explains the race this avoids in full.

Run ptaio with -v to trace every submission and completion. They come out thoroughly interleaved and in no useful order, because the simulated device finishes requests late and out of sequence, and each protothread is somewhere different in its own sequence of I/Os:

   0 ms  submit  protothread 19  op 1/3
   0 ms  done    protothread  8  op 1/1
   1 ms  done    protothread  3  op 1/3
   1 ms  submit  protothread  3  op 2/3
   1 ms  done    protothread  5  op 1/3
   1 ms  submit  protothread  5  op 2/3
   1 ms  done    protothread 14  op 1/4
   1 ms  submit  protothread 14  op 2/4

The exact order differs from run to run, because the device's deadlines are anchored to the wall clock. The protothread scheduling itself stays entirely determined by the completion events; see the header comment in async_io.c.

No protothread is written to cope with that, and none has to be: each one reads as straight-line code that starts an I/O and waits for it.