Front page | perl.perl5.porters |
Postings from March 2000
Build Report for Perl v5.6.0-RC3 on i386.openbsd-thread v2.6
Thread Next
From:
Tom Christiansen
Date:
March 21, 2000 16:02
Subject:
Build Report for Perl v5.6.0-RC3 on i386.openbsd-thread v2.6
Message ID:
19350.953683330@chthon
Well, here's my research project for the day.
The first thing you need to do to make OpenBSD build a pthreaded
Perl is get enough compiler flags right. This is not in the hints
files. You need -pthread all over the place:
% grep pthread config.sh
ccflags='-fno-strict-aliasing -I/usr/local/include -pthread'
cppflags='-fno-strict-aliasing -I/usr/local/include -pthread'
d_old_pthread_create_joinable='define'
d_oldpthreads='undef'
d_pthread_yield='define'
i_pthread='define'
ldflags='-pthread -L/usr/local/lib'
libswanted='sfio socket bind inet nsl nm ndbm gdbm dbm db malloc dl dld ld sun m c cposix posix ndir dir sec ucb bsd BSD PW x iconv pthread'
old_pthread_create_joinable=''
Now, doing that, there were three test failures. I'll explain them all.
0) threads
1) openpid
2) fork
====================================================================
The threads failure is test 19, which fails because Perl regex
library is not thread safe. I cannot fix this. All I can say
is don't play $1 games diddingly between duelling threads. Of course,
this is indeterminable, so life is imperfect. But we knew that.
====================================================================
The next problem, the openpid failure, you can fix trivially by,
when compiling under threads, using fork not instead of BSD's normal
vfork. Without threads, however, vfork is fine. Note that the
openbsd hints say this:
# Currently, vfork(2) is not a real win over fork(2) but this will
# change starting with OpenBSD 2.7.
usevfork='true'
But with pthreads, for some strange reason, you can't do that and
all work. I don't know why. If you read
/usr/src/lib/libc_r/uthread/uthread_vfork.c, you'll see that it
just does this:
int
vfork(void)
{
return (fork());
}
Anyway, that's easy to fix.
====================================================================
Now the hard one. This is tests 10 and 11 of fork.t, which is
really testing waitpid. That's where we lose.
Here's the failing test. I added full paths just in case. The other
is like this but without the exec. Same issue.
$| = 1;
$\ = "\n";
my $echo = '/bin/echo';
if ($pid = fork) {
waitpid($pid, 0);
print "parent got $?"
}
else {
exec($echo, "foo");
}
Now, if you run without threads, it succeeds. If you run with
threads, it fails. And now I know why. If you adjust the print,
you'll see it clearly:
print "parent got $? $!"
That now prints
parent got -1 Interrupted system call
Bingo. The short story is that under threads and there alone, the
wait4 you get is being EINTRd by the SIGCHLD. This does not normally
happen.
The test is easily fixable this way:
$| = 1;
$\ = "\n";
my $echo = '/bin/echo';
if ($pid = fork) {
while (waitpid($pid,0) == -1 && $! =~ /Interrupted system call/) { }
print "parent got $?"
}
else {
exec($echo, "foo");
}
And now it works. But one should *not* have to do that. We are not
being sigrestarted.
Here's more detail. First, here's the kernel trace without threading:
2923 perl CALL sigprocmask(0x1,0)
2923 perl RET sigprocmask 0
2923 perl CALL fork
2923 perl RET fork 93/0x5d
93 perl RET fork 0
93 perl CALL getpid
93 perl RET getpid 93/0x5d
93 perl CALL execve(0xa8db0,0xa8dc0,0xdfbfda1c)
93 perl NAMI "/bin/echo"
93 echo EMUL "native"
93 echo RET execve 0
....
93 echo CALL write(0x1,0x8000,0x4)
93 echo GIO fd 1 wrote 4 bytes
"foo
"
93 echo RET write 4
93 echo CALL exit(0)
2923 perl CALL wait4(0x5d,0xdfbfd908,0,0)
2923 perl RET wait4 93/0x5d
And here it is with the threading:
1465 perl CALL fork
1465 perl RET fork 19545/0x4c59
1465 perl CALL wait4(0x4c59,0xdfbfd8c4,0,0)
19545 perl RET fork 0
19545 perl CALL getpid
19545 perl RET getpid 19545/0x4c59
19545 perl CALL execve(0xb6e50,0xb6e60,0xdfbfda18)
19545 perl NAMI "/bin/echo"
19545 echo CALL write(0x1,0x8000,0x4)
19545 echo GIO fd 1 wrote 4 bytes
"foo
"
19545 echo RET write 4
19545 echo CALL exit(0)
1465 perl PSIG SIGCHLD caught handler=0x401d1ad4 mask=0x0 code=0x0
1465 perl RET wait4 -1 errno 4 Interrupted system call
1465 perl CALL sigreturn(0xdfbfd74c)
1465 perl RET sigreturn JUSTRETURN
See what happens? Apparently, in the thread version, it's losing
the SA_RESTART flag in our sigaction, so waitpid returns EINTR when
the SIGCHLD hits it. Perl's built-in system() spins on that, but
Perl's waitpid() does not (which makes sense; you want complete
system accessibility). I suspect a bug in lurking in the icky
/usr/src/lib/libc_r/uthread/uthread_wait4.c but am unclear.
Here's the only sigaction in the non-threaded version:
2923 perl CALL sigaction(0x14,0,0xdfbfd8b0)
2923 perl RET sigaction 0
I cann't trace down the flags, because that last one is a pointer:
sigaction(int sig, const struct sigaction *act, struct sigaction *oact);
where all the good stuff is, but Perl is doing this there:
Sighandler_t
Perl_rsignal(pTHX_ int signo, Sighandler_t handler)
{
struct sigaction act, oact;
act.sa_handler = handler;
sigemptyset(&act.sa_mask);
act.sa_flags = 0;
#ifdef SA_RESTART
act.sa_flags |= SA_RESTART; /* SVR4, 4.3+BSD */
#endif
#ifdef SA_NOCLDWAIT
if (signo == SIGCHLD && handler == (Sighandler_t)SIG_IGN)
act.sa_flags |= SA_NOCLDWAIT;
#endif
if (sigaction(signo, &act, &oact) == -1)
return SIG_ERR;
else
return oact.sa_handler;
}
Meanwhile, the threaded version has *THIRTY-THREE* calls to sigaction.
I can't tell you why. Well, yes I can, I can see lots of stuff in
uthread_wait4.c, but that doesn't explain the following.
Here's the very curious thing. If in the parent, you add a call
to Perl system()
$| = 1;
$\ = "\n";
my $echo = '/bin/echo';
if ($pid = fork) {
system("true"); # or false, doesn't matter
waitpid($pid, 0);
print "parent got $?"
}
else {
exec($echo, "foo");
}
Then something manages to fix your signal state. Perl's system
does this:
rsignal_save(SIGINT, SIG_IGN, &ihand);
rsignal_save(SIGQUIT, SIG_IGN, &qhand);
do {
result = wait4pid(childpid, &status, 0);
} while (result == -1 && errno == EINTR);
(void)rsignal_restore(SIGINT, &ihand);
(void)rsignal_restore(SIGQUIT, &qhand);
which of course dodges the problem. Since it also fixes
the EINTR problem we otherwise get later, I think that calling our
own sigaction (which rsignal does), fixes it.
That's all I know.
--tom
Thread Next
-
Build Report for Perl v5.6.0-RC3 on i386.openbsd-thread v2.6
by Tom Christiansen