即席httpdを書いてみる
libtaskの例として、httpdを書いてみた。200行ぐらいだけど、コアの部分はこんな感じ。fdread/fdwriteはノンブロッキングI/Oなんだけど、普通のシングルスレッドプログラムのようにselectループを書く必要がない。どうなっているかというと、libtask内部でスケジューラが存在しているのだ。タスクがI/O待ちが必要になると、ライブラリ内で明示的にスケジューラに制御を移し、次のタスクに切り替える。
void httpdtask(void *arg) { int rfd, fd, n; int status; char *msg; struct stat stat; char fullpath[PATH_MAX], buf[4096]; rfd = (int)arg; status = parserequest(rfd, fullpath, &stat); switch (status) { case HTTP_OK: fd = open(fullpath, O_RDONLY); if (fd < 0) { perror("open"); goto end; } sendheader(rfd, status, strstatus(status), &stat); for (;;) { n = fdread(fd, buf, sizeof(buf)); if (n <= 0) break; n = fdwrite(rfd, buf, n); if (n < 0) break; } close(fd); break; default: msg = strstatus(status); sendheader(rfd, status, msg, 0); n = snprintf(buf, BUFSIZ, "<html><title>%d %s</title>" "<body><h1>%d %s</h1></body></html>", status, msg, status, msg); fdwrite(rfd, buf, n); break; } end: close(rfd); } void taskmain(int argc, char **argv) { int cfd, fd; int rport; char remote[16]; if (argc != 3) { fprintf(stderr, "usage: httpd documentroot port\n"); exit(1); } documentroot = argv[1]; port = atoi(argv[2]); fd = netannounce(TCP, 0, port); if (fd < 0) { fprintf(stderr, "cannot announce on tcp port %d: %s\n", port, strerror(errno)); exit(1); } fdnoblock(fd); for (;;) { cfd = netaccept(fd, remote, &rport); if (cfd < 0) break; fdnoblock(cfd); taskcreate(httpdtask, (void *)cfd, 32768); } }