[ruby-dev:31865] Re: 拡張ライブラリ内での SIGVTALRM

From: Tanaka Akira <akr@...>
Date: 2007-09-25 20:38:48 UTC
List: ruby-dev #31865
In article <20070926002126.06db083c.tommy@tmtm.org>,
  とみたまさひろ <tommy@tmtm.org> writes:

> Solaris では、かなり前([ruby-dev:17878])にも同じような問題が起きたこと
> があるので、常に --enable-pthread しといた方が無難なのかもしれません。

--enable-pthread だと

% ./ruby -ve 'Thread.new {}
r, w = IO.pipe 
r.sysread(10)'
ruby 1.8.6 (2007-09-26 patchlevel 5000) [i686-linux]
-e:3:in `sysread': Interrupted system call (Errno::EINTR)
        from -e:3

とか、

% ./ruby -rnet/http -e '
Thread.new {}
h = Net::HTTP.new("www.ruby-lang.org")
h.read_timeout = nil
h.get("/")'
/home/akr/ruby/18pth/lib/ruby/1.8/net/protocol.rb:135:in `sysread': Interrupted system call (Errno::EINTR)
...

とか、

% ./ruby -e 'Thread.new {}  
r, w = IO.pipe
p w.syswrite("a" * 1024 * 1024)
p w.syswrite("a" * 1024 * 1024)'
65536
-e:4:in `syswrite': Interrupted system call (Errno::EINTR)
        from -e:4

とか、

nute(5:32:01)% ./ruby -e '
Thread.new { } 
n = 5000  
w = STDOUT 
loop {  
  w.write("a" * n) 
  w.write("b" * n) 
  w.flush  
}  
'|./ruby -e ' 
n = 5000  
r = STDIN 
loop {  
  sleep 0.1 
  s = r.read(n); p [s.length, s.squeeze] 
  sleep 0.1 
  s = r.read(n); p [s.length, s.squeeze] 
}  
'  
[5000, "a"]
[5000, "b"]
[5000, "a"]
[5000, "b"]
[5000, "a"]
[5000, "b"]
[5000, "a"]
[5000, "b"]
[5000, "a"]
[5000, "b"]
[5000, "a"]
[5000, "b"]
[5000, "a"]
[5000, "ba"]
[5000, "ab"]
[5000, "ba"]
[5000, "aba"]
[5000, "ab"]
...
% ./ruby -v
ruby 1.8.6 (2007-09-26 patchlevel 5000) [i686-linux]

とか起きるしなぁ。それはそれでなかなか。

なお、ここで使っている ./ruby は --enable-pthread です。

> # setitimer(ITIMER_VIRTUAL) はユーザー空間での CPU 時間がカウントされる
> # ものだと思っていたので、システムコール中に SIGVTALRM が発生するのが不
> # 思議。そういうものではないのかな…。

この件がそうであるかどうかはわかりませんが、
setitimer(ITIMER_VIRTUAL) がシステムコール中に SIGVTALRM を
引き起こす例をひとつ知っています。

拡張ライブラリが内部的にネイティブスレッドを使っていて、その
ネイティブスレッドが SIGVTARLM をマスクしている場合には、
SIGVTARLM がシステムコール中に発生することがあります。そのネ
イティブスレッドがユーザー空間での CPU 時間を消費してタイマ
が発火した時そのネイティブスレッドがシグナルを受け付けない場
合、他の、システムコールでブロックしているスレッドにシグナル
が到着する、というストーリーみたいです。

% cat extconf.rb 
require 'mkmf'
have_library('pthread')
create_makefile('pth')
% cat pth.c     
#include <stdlib.h>
#include <errno.h>
#include <signal.h>
#include <pthread.h>

static void *
loop(void *arg)
{
  sigset_t sigset;
  int ret;

  if (sigfillset(&sigset) == -1) { perror("sigfillset"); exit(1); }
  ret = pthread_sigmask(SIG_BLOCK, &sigset, NULL);
  if (ret != 0) { perror("pthread_sigmask"); exit(1); }

  while (1);
}

void
Init_pth()
{
  pthread_t pth;
  int ret;
  ret = pthread_create(&pth, NULL, loop, NULL);
  if (ret != 0) { errno = ret; perror("pthread_create"); exit(1); }
}

% ruby-1.8 extconf.rb 
checking for main() in -lpthread... yes
creating Makefile
% make
gcc -I. -I. -I/home/akr/ruby/18/lib/ruby/1.8/i686-linux -I.  -fPIC -g -O2  -c pth.c
gcc -shared -o pth.so pth.o -L. -L/home/akr/ruby/18/lib -Wl,-R/home/akr/ruby/18/lib -L.  -rdynamic -Wl,-export-dynamic    -lpthread  -ldl -lcrypt -lm   -lc
% ruby-1.8 -rpth -e '
  Thread.new {}
  r, w = IO.pipe
  r.sysread(10)'
-e:4:in `sysread': Interrupted system call (Errno::EINTR)
        from -e:4
zsh: exit 1     ruby-1.8 -rpth -e '   Thread.new {}   r, w = IO.pipe   r.sysread(10)'
% strace ruby-1.8 -rpth -e '
  Thread.new {}
  r, w = IO.pipe
  r.sysread(10)'
...
read(3, 0x8135308, 10)                  = ? ERESTARTSYS (To be restarted)
--- SIGVTALRM (Virtual timer expired) @ 0 (0) ---
sigreturn()                             = ? (mask now [])
...
% ruby-1.8 -v
ruby 1.8.6 (2007-09-26 patchlevel 5000) [i686-linux]

なお、この ruby-1.8 は --enable-pthread ではありません。
-- 
[田中 哲][たなか あきら][Tanaka Akira]

In This Thread