自己紹介

太田一樹。
東京の大学の情報科学科に通う大学生。moratorium満喫中。

お勧め書籍 [全部見る]

飾り

Search


Category Archives

Recent Entries

  1. 論文
  2. JJUG CCCでプレゼンします
  3. kzk's bookshelf
  4. En Google by Gulfweed
  5. PNUTS
  6. コメントスパム対策
  7. Hadoop + Luceneで分散インデクシング
  8. Hadoopの解析資料
  9. Cluster 2008
  10. SWoPP 2008

2007年11月16日

close(2) while select(2)ing

昨日ちゅんさんと話していた話題。select(2)中に監視対象のディスクリプタを別スレッドからclose(2)したらどうなるの?epoll(2)だと?

以下のような事を試してみた。

thread1             thread2
--------------------------------
(r,w) <-pipe()
select(fdset = {r})
                    close(r)
                    close(w)
                    (r2, w2) <- pipe() // 同じfdになる
                    write(w2)

select(2)版コード

void* fd_consumer(void *writefd_){
  int* pipefd = (int*)writefd_;
  sleep(1);
  close(pipefd[0]);
  close(pipefd[1]);
  cerr << "close(2) pipe" << endl;
  sleep(1);
  if(pipe(pipefd)){ perror("Failed to make pipe"); exit(1); }
  cerr << "pipe(2) in another thread: " << pipefd[0] << ", " << pipefd[1] << endl;
  sleep(1);
  cerr << "write(2) to pipe" << endl;
  char buf[] = "a";
  write(pipefd[1], buf, 1);
  return NULL;
}

int main(void){
  int pipefd[2];
  if(pipe(pipefd)){
    perror("failed to make pipe");
    exit(1);
  }
  cerr << "pipe(2): " << pipefd[0] << ", " << pipefd[1] << endl;
 
  fd_set rfds;
  fd_set wfds;
  fd_set efds;

  pthread_t thread;
  pthread_create(&thread, NULL, fd_consumer, (void *)pipefd);

  FD_ZERO(&rfds);
  FD_ZERO(&wfds);
  FD_ZERO(&efds);
  FD_SET(pipefd[0], &rfds);
  FD_SET(pipefd[0], &wfds);
  FD_SET(pipefd[0], &efds);

  struct timeval tv; tv.tv_sec = 5; tv.tv_usec = 0;
  int r = select(pipefd[0]+1, &rfds, &wfds, &efds, &tv);
  if(r < 0){
    cerr << "select(2) error" << endl;
  } else if (r == 0){
    cerr << "select(2) timedout" << endl;
  }else{
    cerr << "select(2) catched " << r << " elements" << endl;
  }
  if(FD_ISSET(pipefd[0], &rfds))
    cerr << "pipefd[0] set(rfds)" << endl;
  if(FD_ISSET(pipefd[0], &wfds))
    cerr << "pipefd[0] set(wfds)" << endl;
  if(FD_ISSET(pipefd[0], &efds))
    cerr << "pipefd[0] set(efds)" << endl;
  return 0;
}

epoll(2)版コード

void* fd_consumer(void *writefd_){
  int* pipefd = (int*)writefd_;
  sleep(1);
  close(pipefd[0]);
  close(pipefd[1]);
  cerr << "close(2) pipe" << endl;
  sleep(1);
  if(pipe(pipefd)){ perror("Failed to make pipe"); exit(1); }
  cerr << "pipe(2) in another thread: " << pipefd[0] << ", " << pipefd[1] << endl;
  sleep(1);
  cerr << "write(2) to pipe" << endl;
  char buf[] = "a";
  write(pipefd[1], buf, 1);
  return NULL;
}

int main(void){
  int epfd = epoll_create(10);

  int pipefd[2];
  if(pipe(pipefd)){
    perror("failed to make pipe");
    exit(1);
  }
  cerr << "pipe(2): " << pipefd[0] << ", " << pipefd[1] << endl;

  struct epoll_event epev;
  memset(&epev, 0, sizeof(epev));
  epev.events = EPOLLIN;
  epev.data.fd = pipefd[0];
  epoll_ctl(epfd, EPOLL_CTL_ADD, pipefd[0], &epev);

  pthread_t thread;
  pthread_create(&thread, NULL, fd_consumer, (void *)pipefd);

  struct epoll_event events[10];
  int r = epoll_wait(epfd, events, 10, 5000);
  if(r < 0){
    cerr << "epoll(2) error" << endl;
  } else if (r == 0){
    cerr << "epoll(2) timedout" << endl;
  }else{
    cerr << "epoll(2) catched " << r << " elements" << endl;
  }

  for(int i=0; i<r; i++){
    if(events[i].events & EPOLLIN)
      cerr << "EPOLLIN" << endl;
    if(events[i].events & EPOLLOUT)
      cerr << "EPOLLOUT" << endl;
    if(events[i].events & EPOLLERR)
      cerr << "EPOLLERR" << endl;
    if(events[i].events & EPOLLHUP)
      cerr << "EPOLLHUP" << endl;
  }
  return 0;
}

実行結果

mac% g++ seltest.cpp -Wall; time ./a.out
pipe(2): 3, 4
select(2) error
pipefd[0] set(rfds)
pipefd[0] set(wfds)
pipefd[0] set(efds)
./a.out  0.00s user 0.01s system 0% cpu 1.011 total
linux% g++ -Wall seltest.cpp -lpthread; time ./a.out
pipe(2): 3, 4
close(2) pipe
pipe(2) in another thread: 3, 4
write(2) to pipe
select(2) catched 1 elements
pipefd[0] set(rfds)
./a.out  0.00s user 0.00s system 0% cpu 5.007 total
linux% g++ -Wall epolltest.cpp -lpthread; time ./a.out
pipe(2): 4, 5
close(2) pipe
pipe(2) in another thread: 4, 5
write(2) to pipe
epoll(2) timedout
./a.out  0.00s user 0.00s system 0% cpu 5.007 total

結果

select(2)だとMacOS XとLinuxで挙動が違う。未定義動作?

Macの方の動作を意図してselect(2)を使ったプログラムを書いていたので、ちょっと困った。代替となるportableな方法無いかなぁ。


trackbacks

trackbackURL:

『close(2) while select(2)ing』の関連記事

comments

昔の話なので適切なのかどうかはわかんないんですが、
死んだfdを待ち続けないために、
「close通知用pipe」を作ってそれも一緒に待ち受け→通知されたのを検出したら可及的速やかに死去
という方法をとってました。要はその目的のためだけに何らかのIPCを持つ。と。。

MacOS Xのカーネルって要はMachで、それのselectがMachのPortとポートセットで実現されてるなら、
Machのポートには破壊されたことを通知するイベントがあるんで
それをエラーに見せてるのかも。完全な想像ですが。
(LinuxとかBSDみたいにビット立ててどうにかする実装とどうやってポータビリティ確保してるんだろう。。)

  • .mjt
  • 2007年11月16日 11:53

コメントありがとうございます。

> 「close通知用pipe」
いわゆるself-pipeとかいうテクニック?ですね。解決策としてそれを検討中でした。

> Machのポートには破壊されたことを通知するイベントがあるんで
> それをエラーに見せてるのかも。完全な想像ですが。
Mach全然知らんのでちょっと調べてみまする。さすがmjt氏詳しすぎです(笑)

  • kzk
  • 2007年11月16日 12:20
comment form
comment form