エラトステネスのふるい

libtaskはチャネルにも対応している。ということで、Limboで書いたエラトステネスのふるいをC + libtaskで書き直してみた。Plan9日記では何度もチャネルを取り上げているが、チャネルはタスク間でメッセージをやり取りするための通信手段を提供する。chancreate関数の第2引数が0なので、バッファは使わない。したがって、あるタスクがメッセージをchansendすると、他のタスクがそのチャネルからchanrecvするまで、そのタスクはブロックされる。

やっぱり、型の扱いがLimboの方が書きやすいな。Limboではチャネルに型があって、send/recv("<-"という演算子が使われる)すればいいんだけど、libtask(C)だとデータ型に応じて、chansend, chansendp, chansendul関数を使い分ける必要がある。Limboとの違いは、channbsend([p|ul])というノンブロッキング版が用意されていることだろうか。

#include <stdio.h>
#include <stdlib.h>
#include "task.h"

enum {
  STACK = 32768
};

int max = 100;

void
sievetask(void *arg)
{
  Channel *c = (Channel *)arg;
  Channel *nc;
  int p, n;

  p = chanrecvul(c);
  if (p >= max)
    taskexitall(0);
  printf("%d\n", p);

  nc = chancreate(sizeof(int), 0);
  taskcreate(sievetask, nc, STACK);

  for (;;) {
    n = chanrecvul(c);
    if (n % p)
      chansendul(nc, n);
  }
}

void
taskmain(int argc, char **argv)
{
  Channel *c;
  int i = 2;

  if (argc == 2)
    max = atoi(argv[1]);

  c = chancreate(sizeof(int), 0);
  taskcreate(sievetask, c, STACK);

  for (;;)
    chansendul(c, i++);
}