[プログラミング] aio_readを使ったFutureっぽいインタフェースの実装

ネットワークプログラム周りで、aio_read(3)*1を使う必要が出てきたので、軽く調べてみた。
今回やりたかったのは非同期にIOを発行したあとに別処理を行って、その後取得された結果を使うというものだったのでJavaFurtureっぽいインターフェイスの実装を行ってみた。

Descriptorクラスのread_async(buf , count)を呼ぶとFurtureDescriptor * が返ってくるので、それに対して別処理を行うなどしたあとget()を呼ぶとread(2)の結果に相当する値が返ってくる。

実装は以下

#include <iostream>
#include <cstdlib>

#include <unistd.h>
#include <aio.h>
#include <errno.h>

using namespace std;

class FurtureDescriptor{
  public:
    FurtureDescriptor(struct aiocb * aiocbp)
      :aiocbp(aiocbp){}

    ~FurtureDescriptor(){
      free(aiocbp);
    }

    //I/Oリクエストが完了するまで待機し、その後結果を返す
    int get(){
      int ret = ::aio_suspend(&aiocbp , 1 , NULL);
      if(ret < 0){
        perror("aio_suspend");
        return -1;
      }
      return ::aio_return(aiocbp);
    }
    
    //I/Oリクエストが完了しているかどうかを返す
    //エラーの場合もtrueを返す
    bool isDone(){
      int ret = ::aio_error(aiocbp);
      if(ret == EINPROGRESS){
        return false;
      }else{
        return true;
      }
    }

  private:
    struct aiocb * aiocbp;

};

class Descriptor{
  public:
    Descriptor(int fd)
      :fd(fd){}
    ~Descriptor(){
      ::close(fd);
    }
    ssize_t read(void * buf , size_t count){
      ssize_t ret = ::read(fd , buf , count);
      return ret;
    }

    //非同期にread(buf,count)をリクエストする
    //非同期操作中にbufを変更した場合の結果は不定
    FurtureDescriptor * read_async(void * buf , size_t count){
      struct aiocb * aiocbp = static_cast<struct aiocb *>(malloc(sizeof(struct aiocb)));
      aiocbp->aio_fildes = fd;
      aiocbp->aio_buf = buf;
      aiocbp->aio_nbytes = count;
      int ret = ::aio_read(aiocbp);
      if(ret < 0){
        perror("aio_read");
        return NULL;
      }
      return new FurtureDescriptor(aiocbp);
    }
  private:
    int fd;
};

#define N (1 << 29)
char buf[N];
int main(int argc , char * argv[]){
  int fd = open("large.file" , O_RDONLY);
  if(fd < 0){
    perror("open");
    exit(1);
  }
  Descriptor d(fd);
  //sync IO
  {
    int n = d.read(buf , N);
    cerr << n << endl;
  }
  //async IO
  {
    FurtureDescriptor * future = d.read_async(buf , N);
    //some operation
    sleep(1);
    int n = future->get();
    cerr << n << endl;
    delete future;
  }
  return 0;
}

*1: FreeBSDだとaio_read(2)