Perl / perl5

🐪 The Perl programming language
https://dev.perl.org/perl5/
Other
1.97k stars 555 forks source link

Parent's file handle spins after forks #2935

Closed p5pRT closed 20 years ago

p5pRT commented 23 years ago

Migrated from rt.perl.org#4826 (status was 'resolved')

Searchable as RT4826$

p5pRT commented 23 years ago

From mlf@storm.techdata.com

This is a bug report for perl from mlf2@​tampabay.rr.com\, generated with the help of perlbug 1.28 running under perl v5.6.0.


Given the following script\, the expected output should be some permutation of​:

11094​: Fork => 11095 11095> one 11094​: Fork => 11096 11096> two 11094​: Fork => 11097 11097> three 11094​: Fork => 11098 11098> four 11094​: Fork => 11099 11099> five

Instead\, under Solaris\, I get​:

16754> one 16753​: Fork => 16754 16755> two 16753​: Fork => 16755 16756> three 16753​: Fork => 16756 16757> four 16753​: Fork => 16757 16758> five 16753​: Fork => 16758 16759> ; 16753​: Fork => 16759 16753​: Fork => 16760 16753​: Fork => 16761 16753​: Fork => 16762 16753​: Fork => 16763 16753​: Fork => 16764 16753​: Fork => 16765 16753​: Fork => 16766 16753​: Fork => 16767 16753​: Fork => 16768 16753​: Fork => 16769 16753​: Fork => 16770 16753​: Fork => 16771 16753​: Fork => 16772 16753​: Fork => 16773 16753​: Fork => 16774 16753​: Fork => 16775 16753​: Fork => 16776 16753​: Fork => 16777 16753​: Fork => 16778 16753​: Fork => 16779 16779> five 16778> four 16777> three 16771> } 16772> } 16760> exit; 16775> one 16761> } 16774> __END__ 16762> } 16765> one 16764> __END__ 16763> 16776> two 16773> 16767> three 16766> two 16769> five 16768> four 16770> exit;

The script produces incorrect output under with the following OS/perl versions that I've tested​:

Solaris 7 / perl 5.6.0 (the output above) Solaris 7 / perl 5.005_02 (script never stops) Solaris 8 / perl 5.005_03

It works as expected on the following​:

Red Hat Linux 6.2 (kernel 2.2.14-5.0) / perl 5.005_03 Red Hat Linux 6.2 (kernel 2.2.17) / perl 5.6.0

#!/usr/local/bin/perl

$SIG{CHLD} = sub { wait };

while ($data = \) {

  if ($pid = fork) { # parent...   print STDERR "$$​: Fork => $pid\n";

  } elsif (defined $pid) { # child...   print STDERR "$$> $data";   exit;   } }

__END__ one two three four five



Flags​:   category=core   severity=medium


Site configuration information for perl v5.6.0​:

Configured by mlf at Wed Aug 9 09​:31​:50 EDT 2000.

Summary of my perl5 (revision 5.0 version 6 subversion 0) configuration​:   Platform​:   osname=solaris\, osvers=2.7\, archname=sun4-solaris   uname='sunos storm.techdata.com 5.7 generic sun4u sparc sunw\,ultra-5_10 '   config_args=''   hint=recommended\, useposix=true\, d_sigaction=define   usethreads=undef use5005threads=undef useithreads=undef usemultiplicity=undef   useperlio=undef d_sfio=undef uselargefiles=define   use64bitint=undef use64bitall=undef uselongdouble=undef usesocks=undef   Compiler​:   cc='gcc'\, optimize='-O'\, gccversion=2.8.1   cppflags='-I/usr/local/include'   ccflags ='-I/usr/local/include -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64'   stdchar='char'\, d_stdstdio=define\, usevfork=false   intsize=4\, longsize=4\, ptrsize=4\, doublesize=8   d_longlong=define\, longlongsize=8\, d_longdbl=define\, longdblsize=16   ivtype='long'\, ivsize=4\, nvtype='double'\, nvsize=8\, Off_t='off_t'\, lseeksize=8   alignbytes=8\, usemymalloc=y\, prototype=define   Linker and Libraries​:   ld='gcc'\, ldflags =' -L/usr/local/lib '   libpth=/usr/local/lib /lib /usr/lib /usr/ccs/lib   libs=-lsocket -lnsl -ldb -ldl -lm -lc -lcrypt -lsec   libc=/lib/libc.so\, so=so\, useshrplib=false\, libperl=libperl.a   Dynamic Linking​:   dlsrc=dl_dlopen.xs\, dlext=so\, d_dlsymun=undef\, ccdlflags=' '   cccdlflags='-fPIC'\, lddlflags='-G -L/usr/local/lib'

Locally applied patches​:  


@​INC for perl v5.6.0​:   /usr/local/lib/perl5/5.6.0/sun4-solaris   /usr/local/lib/perl5/5.6.0   /usr/local/lib/perl5/site_perl/5.6.0/sun4-solaris   /usr/local/lib/perl5/site_perl/5.6.0   /usr/local/lib/perl5/site_perl/5.005/sun4-solaris   /usr/local/lib/perl5/site_perl/5.005   /usr/local/lib/perl5/site_perl   .


Environment for perl v5.6.0​:   HOME=/td/home/mlf   LANG (unset)   LANGUAGE (unset)   LD_LIBRARY_PATH=/usr/lib​:/usr/openwin/lib​:/usr/dt/lib   LOGDIR (unset)   PATH=/usr/ccs/bin​:/opt/local/bin​:/usr/local/bin​:/usr/bin​:/usr/sbin​:/usr/ucb​:/usr/etc​:/usr/openwin/bin​:/bin​:/sbin​:/etc​:/td/home/mlf/bin/s​:/usr/dt/bin​:/td/home/mlf/bin/sparc/sos5   PERL_BADLANG (unset)   SHELL=/opt/local/bin/tcsh

p5pRT commented 23 years ago

From [Unknown Contact. See original ticket]

At 15​:10 -0500 2000-12-04\, Mike Fisher wrote​:

Given the following script\, the expected output should be some permutation of​:

11094​: Fork => 11095 11095> one 11094​: Fork => 11096 11096> two 11094​: Fork => 11097 11097> three 11094​: Fork => 11098 11098> four 11094​: Fork => 11099 11099> five ... #!/usr/local/bin/perl

$SIG{CHLD} = sub { wait };

while ($data = \) {

if \($pid = fork\) \{             \# parent\.\.\.

print STDERR "$$​: Fork => $pid\n";

\} elsif \(defined $pid\) \{        \# child\.\.\.

print STDERR "$$> $data"; exit; } }

__END__ one two three four five

FWIW\, works "as expected" for me on MachTen -- output from parent and children neatly interleaved\, with children running first after the fork (the parent ran first on your Linux output) -- rather than the permutation that it might (still correctly) have been.

No cure\, but a few thoughts​:

1. Might your Sun system(s) be multi-processor while your Linux system is uniprocessor?

2. If so\, how does the test script behave on a uniprocessor Sun (presuming you have one to hand)?

3. Does adding a wait; after the first print make Solaris behave "as expected"?

4. Is the behaviour on Solaris the same if the script's reading STDIN -- from a shell here document or a file -- instead of DATA? (It is for me on MachTen\, but then MachTen behaves with DATA as well.)

5. If so\, is the behaviour of an equivalent C program reading from stdin the same as that of the Perl script?

I don't know what bearing 1 and 2 might have on anything. I'd expect 3 to be true. But you probably have reasons for not waiting at that point. If neither 4 nor 5 is true\, then we should suspect some glitch in perl's handling of the DATA filehandle; if 4 is true and not 5\, then there's some other problem with perl; and if both 4 and 5 are true\, it's a Solaris problem (which perl may\, nevertheless\, want to work around).

My guess\, given that you see snippets of the Perl script in your Solaris output\, is that we're in a 3 & !4 & !5 situation. But it would be nice to know.

p5pRT commented 23 years ago

From [Unknown Contact. See original ticket]

Dominic Dunlop wrote​:

FWIW\, works "as expected" for me on MachTen -- output from parent and children neatly interleaved\, with children running first after the fork (the parent ran first on your Linux output) -- rather than the permutation that it might (still correctly) have been.

No cure\, but a few thoughts​:

1. Might your Sun system(s) be multi-processor while your Linux system is uniprocessor?

2. If so\, how does the test script behave on a uniprocessor Sun (presuming you have one to hand)?

Tried both with similar results. The first and third Solaris results I listed were single CPU Ultra 5 workstations. The second was a dual CPU E450. All the Linux boxes were single CPU x86.

3. Does adding a wait; after the first print make Solaris behave "as expected"?

No\, actually\, it gets worse. Without the wait\, the script at least terminates under Solaris 7 on an Ultra 5\, perl 5.6.0. Adding the extra wait causes it to keep going\, seemingly forever.

4. Is the behaviour on Solaris the same if the script's reading STDIN -- from a shell here document or a file -- instead of DATA? (It is for me on MachTen\, but then MachTen behaves with DATA as well.)

If I change read from \ and give input from the keyboard\, it works as expected. If STDIN comes from a file via a shell redirect\, I get extra lines.

% cat >dummy 1 2 3 4 5 %ftest2 \< dummy 18855> 1 18854​: Fork => 18855 18856> 2 18854​: Fork => 18856 18857> 3 18854​: Fork => 18857 18858> 4 18854​: Fork => 18858 18859> 5 18854​: Fork => 18859 18860> 1 18854​: Fork => 18860 18854​: Fork => 18861 18854​: Fork => 18862 18854​: Fork => 18863 18854​: Fork => 18864 18854​: Fork => 18865 18854​: Fork => 18866 18854​: Fork => 18867 18854​: Fork => 18868 18868> 5 18867> 4 18866> 3 18865> 2 18864> 5 18863> 4 18862> 3 18861> 2

Again\, adding the extra wait causes it to spin out of control. I originally found the problem while using 'while (\<>) {'. (I meant to mention that in the bug report...)

5. If so\, is the behaviour of an equivalent C program reading from stdin the same as that of the Perl script?

Bingo...

#include \<sys/types.h> #include \<stdio.h> #include \<unistd.h> #include \<sys/wait.h>

char buf[1024]; pid_t me; pid_t pid;

int main() {

  me = getpid();

  while (fgets(buf\,1024\,stdin)) {

  if (pid = fork()) {   printf("%ld​: Fork => %ld\n"\, me\, pid);

  } else if (pid == 0) {   printf("%ld> %s"\, getpid()\, buf);

  } else {   printf("fork failed!\n");   }   } }

% ./ftest3 \<dummy 19625> 1 19626> 2 19627> 3 19628> 4 19629> 5 19624​: Fork => 19629 19624​: Fork => 19628 19630> 5 19624​: Fork => 19627 19631> 4 19632> 5 19624​: Fork => 19626 19633> 3 19624​: Fork => 19625 19636> 2 19637> 3 19638> 4 19639> 5 19624​: Fork => 19639 19624​: Fork => 19638 19640> 5 19624​: Fork => 19637 19641> 4 19642> 5 ...

My guess\, given that you see snippets of the Perl script in your Solaris output\, is that we're in a 3 & !4 & !5 situation. But it would be nice to know.

Looks like #5 is the culprit. Off to talk to Sun... Don't know if this might be worth mentioning as a gotcha in the docs or not.

Thanks for the feedback.

Mike

p5pRT commented 23 years ago

From @nwc10

On Wed\, Dec 06\, 2000 at 03​:58​:01PM -0500\, Mike Fisher wrote​:

4. Is the behaviour on Solaris the same if the script's reading STDIN -- from a shell here document or a file -- instead of DATA? (It is for me on MachTen\, but then MachTen behaves with DATA as well.)

If I change read from \ and give input from the keyboard\, it works as expected. If STDIN comes from a file via a shell redirect\, I get extra lines.

It makes it start to sound like it's buffering\, but without digging I can't see how. IIRC you say it

works on Solaris 8 works on Linux (perl 5.6 and 5.005)

fails on Solaris 7

IIRC Perl doesn't comprehend Solaris 8 stdio Perl does't comprehend glibc stdio (guessing that's your linux clib) for the   versions you used

Perl does (think it) comprehends Solaris 7 and attempts to "cheat it" for a   speed gain

STDIN from a keyboard is a tty\, and C's stdio will line buffer STDIN via a shell redirect is a file (presumably a file) and will buffer in blocks\, as will DATA from your script.

If the hypothesis is correct\, then it will work with \ if you cut & paste the script into a perl expecting a script on STDIN from the keyboard.

If it works like that (DATA from keyboard)\, then I'm 90% certain it's a buffering issue.

Nicholas Clark

p5pRT commented 23 years ago

From [Unknown Contact. See original ticket]

Dominic Dunlop wrote​:

...

5. If so\, is the behaviour of an equivalent C program reading from stdin the same as that of the Perl script?

Bingo...

[C code snipped]

I think those extra lines are needed to make it an exact equivalent of the Perl script with the added call to wait. Certainly\, without the exit(0)\, child processes get the chance to spawn their own children\, generally muddying the waters​:

That's what I get for trying to whip it off in a rush... :) You're right on both counts\, of course. Unfortunately\, neither seems to make a bit of difference. Very strange.

Mike

p5pRT commented 23 years ago

From [Unknown Contact. See original ticket]

Mike Fisher \mlf2@&#8203;tampabay\.rr\.com writes​:

Bingo...

#include \<sys/types.h> #include \<stdio.h> #include \<unistd.h> #include \<sys/wait.h>

char buf[1024]; pid_t me; pid_t pid;

int main() {

me = getpid();

while (fgets(buf\,1024\,stdin)) {

if (pid = fork()) { printf("%ld​: Fork => %ld\n"\, me\, pid);

} else if (pid == 0) { printf("%ld> %s"\, getpid()\, buf);

} else { printf("fork failed!\n"); } } }

So what do you expect? - if you printed the parent pids as well you would see there is a whole family of processes. I think you meant a break in the child branch.

Original process does fgets() which slurps (say) 8K or the whole file. Now each and every descendant has a copy of the file in memory\, no more IO is ever done on stdin.

Original then reads 1st line "1\n" leaving file posn @​ 2 then it forks.   child-1 does its print and loops\, reads the second line and forks   granchild-1 does its print and loops\, reads third line and forks   great-granchild-1 does its print reads 4th line and forks   great-great-grandchild-1 does its print\, reads fifth line and forks   great-great-great-granchild-1 does its print loops\, gets eof and exits.#   great-granchild-1 5th line and forks   ...   granchild-1 reads fourth line and forks.   child-1\, reads the third-line and forks   ... parent reads second line and forks   ....

That is expected behaviour with buffered IO and fork.

You should get 5*4*3*2*1 i.e. 120 processes

% ./ftest3 \<dummy 19625> 1 19626> 2 19627> 3 19628> 4 19629> 5 19624​: Fork => 19629 19624​: Fork => 19628 19630> 5 19624​: Fork => 19627 19631> 4 19632> 5 19624​: Fork => 19626 19633> 3 19624​: Fork => 19625 19636> 2 19637> 3 19638> 4 19639> 5 19624​: Fork => 19639 19624​: Fork => 19638 19640> 5 19624​: Fork => 19637 19641> 4 19642> 5 ...

My guess\, given that you see snippets of the Perl script in your Solaris output\, is that we're in a 3 & !4 & !5 situation. But it would be nice to know.

Looks like #5 is the culprit. Off to talk to Sun... Don't know if this might be worth mentioning as a gotcha in the docs or not.

Thanks for the feedback.

Mike

p5pRT commented 23 years ago

From [Unknown Contact. See original ticket]

Nicholas Clark wrote​:

On Wed\, Dec 06\, 2000 at 03​:58​:01PM -0500\, Mike Fisher wrote​:

4. Is the behaviour on Solaris the same if the script's reading STDIN -- from a shell here document or a file -- instead of DATA? (It is for me on MachTen\, but then MachTen behaves with DATA as well.)

If I change read from \ and give input from the keyboard\, it works as expected. If STDIN comes from a file via a shell redirect\, I get extra lines.

It makes it start to sound like it's buffering\, but without digging I can't see how. IIRC you say it

works on Solaris 8 works on Linux (perl 5.6 and 5.005)

fails on Solaris 7

Not quite. It fails on Solaris 2.6\, 7\, and 8. It works on Linux. (Red Hat 6.2 x86\, at least)

STDIN from a keyboard is a tty\, and C's stdio will line buffer STDIN via a shell redirect is a file (presumably a file) and will buffer in blocks\, as will DATA from your script.

If the hypothesis is correct\, then it will work with \ if you cut & paste the script into a perl expecting a script on STDIN from the keyboard.

It does. (at least on Solaris 2.6 and 7. Can't get to 8 at the moment.)

If it works like that (DATA from keyboard)\, then I'm 90% certain it's a buffering issue.

Could be. I'll see what Sun has to say about the C code test and let you know.

Mike

p5pRT commented 23 years ago

From [Unknown Contact. See original ticket]

Nick Ing-Simmons wrote​:

Mike Fisher \mlf2@&#8203;tampabay\.rr\.com writes​:

Bingo...

#include \<sys/types.h> #include \<stdio.h> #include \<unistd.h> #include \<sys/wait.h>

char buf[1024]; pid_t me; pid_t pid;

int main() {

me = getpid();

while (fgets(buf\,1024\,stdin)) {

if (pid = fork()) { printf("%ld​: Fork => %ld\n"\, me\, pid);

} else if (pid == 0) { printf("%ld> %s"\, getpid()\, buf);

} else { printf("fork failed!\n"); } } }

See my reply to Dominic about the missing exit and wait.

So what do you expect? - if you printed the parent pids as well you would see there is a whole family of processes. I think you meant a break in the child branch.

Yes. That was the idea.

Original process does fgets() which slurps (say) 8K or the whole file. Now each and every descendant has a copy of the file in memory\, no more IO is ever done on stdin.

Original then reads 1st line "1\n" leaving file posn @​ 2 then it forks. child-1 does its print and loops\, reads the second line and forks ... That is expected behaviour with buffered IO and fork.

You should get 5*4*3*2*1 i.e. 120 processes

32 actually. (2**n) The addition of an fflush(stdout) after each printf() cleans up the duplicate output.

Given that\, one would think that adding an exit() after the printf() and fflush() of the child section would reduce that count to 6 (parent + five children). In reality\, I get 15 (parent and 14 children). If I then add a wait() to the parent section\, the program continues forever. (as tested on Solaris 7) It works as expected under Linux.

The revised programs follows​:

#include \<sys/types.h> #include \<stdio.h> #include \<unistd.h> #include \<sys/wait.h>

char buf[1024]; pid_t pid;

int main() {

  while (fgets(buf\,1024\,stdin)) {

  if (pid = fork()) {   printf("%ld​: Fork => %ld\n"\, getpid()\, pid);   fflush(stdout);   wait((int *) 0);

  } else if (pid == 0) {   printf("%ld> %s"\, getpid()\, buf);   fflush(stdout);   exit(0);

  } else {   printf("fork failed!\n");   }   } }

Mike

p5pRT commented 23 years ago

From [Unknown Contact. See original ticket]

Mike Fisher \mlf2@&#8203;tampabay\.rr\.com writes​:

Given that\, one would think that adding an exit() after the printf() and fflush() of the child section would reduce that count to 6 (parent + five children).

No.

Solaris exit() (or fclose(stdin)) does fseek(stdin\,0\,SEEK_CUR)\, which in turn lseek's file 0.

Thus when child does exit() everyone's fd #0 stdin is rewound to where child thinks it is. Then when parent runs out of data in the buffer its next "fill" gets that data at the position last exit() left it\, which is not eof.

In reality\, I get 15 (parent and 14 children).

Yup. 5+4+3+2+1

If I then add a wait() to the parent section\, the program continues forever. (as tested on Solaris 7) It works as expected under Linux.

The revised programs follows​:

#include \<sys/types.h> #include \<stdio.h> #include \<unistd.h> #include \<sys/wait.h>

char buf[1024]; pid_t pid;

int main() {

while (fgets(buf\,1024\,stdin)) {

if (pid = fork()) { printf("%ld​: Fork => %ld\n"\, getpid()\, pid); fflush(stdout); wait((int *) 0);

} else if (pid == 0) { printf("%ld> %s"\, getpid()\, buf); fflush(stdout); exit(0);

Make that   _exit(0);

and Solaris produces​:

bash$ !s silly \< data 25247​: Fork => 25248 25248> 1 25247​: Fork => 25249 25249> 2 25247​: Fork => 25250 25250> 3 25247​: Fork => 25251 25251> 4 25247​: Fork => 25252 25252> 5 bash$

 

} else { printf("fork failed!\n"); } } }

Mike

p5pRT commented 23 years ago

From [Unknown Contact. See original ticket]

At 07​:14 -0500 2000-12-07\, Mike Fisher wrote​:

You're right on both counts\, of course. Unfortunately\, neither seems to make a bit of difference. Very strange.

Agreed. If the modified C program behaves strangely on Solaris\, I think we can safely say it's not a perl problem\, but a Solaris problem that perl's tripping over.

Out of interest\, here's a version of the program that does raw reads on stdin​:

#include \<sys/types.h> #include \<stdio.h> #include \<unistd.h> #include \<sys/wait.h>

int getn(void) {   char c;   int n = 0;

  while (read(0\, &c\, 1) == 1 && '0' \<= c && c \<= '9')   n = 10*n + c - '0'; /* Appallingly simple-minded */   return n; }

int main() {   pid_t pid;   int n;

  while ((n=getn()) > 0) {

  if (pid = fork()) {   printf("%ld​: Fork => %ld\n"\, getpid()\, pid);   wait((int *) 0 );   } else if (pid == 0) {   printf("%ld %ld> %d\n"\, getpid()\, getppid()\, n);   exit(0);   } else {   printf("fork failed!\n");   }   } }

How does it fare for you? Works just the same as the stdio-buffered version for me\, but with the difference that\, thanks to the wonder of shared file pointers\, the exit(0) line can be omitted -- in which case it's interesting to see who's the parent of the child that gets to report each number.

p5pRT commented 23 years ago

From [Unknown Contact. See original ticket]

Nick Ing-Simmons \nik@&#8203;tiuk\.ti\.com writes​:

Given that\, one would think that adding an exit() after the printf() and fflush() of the child section would reduce that count to 6 (parent + five children).

No.

Solaris exit() (or fclose(stdin)) does fseek(stdin\,0\,SEEK_CUR)\, which in turn lseek's file 0. ...

 exit\(0\);

Make that _exit(0);

An alternate fix is to make sure things are in sync before the fork()​:

#include \<sys/types.h> #include \<stdio.h> #include \<unistd.h> #include \<sys/wait.h>

char buf[1024]; pid_t pid;

int main() {

  while (fgets(buf\,1024\,stdin)) {  
  fseek(stdin\,0\,1);   if (pid = fork()) {   printf("%ld​: Fork => %ld @​ %ld\n"\, getpid()\, pid\, (long) ftell(stdin));   fflush(stdout);   wait((int *) 0);

  } else if (pid == 0) {   printf("%ld @​ %ld> %s"\, getpid()\, (long) ftell(stdin)\, buf);   fflush(stdout);   exit(0);   } else {   printf("fork failed!\n");   }   } }

That is effectively how it is fixed by perlio in bleadperl​:  
bash$ PERLIO=perlio ./perl silly \< ~/tmp/data 19720​: Fork => 19721 @​ 2 19721 @​ 2> 1 19722 @​ 4> 2 19720​: Fork => 19722 @​ 4 19720​: Fork => 19723 @​ 6 19723 @​ 6> 3 19720​: Fork => 19724 @​ 8 19724 @​ 8> 4 19720​: Fork => 19725 @​ 10 19725 @​ 10> 5 bash$

Where "silly" is

$| = 1;

  while (\<>) {  
  if ($pid = fork()) {   printf("%ld​: Fork => %ld @​ %ld\n"\, $$\, $pid\, tell(STDIN));   wait;

  } elsif ($pid == 0) {   printf("%ld @​ %ld> %s"\, $$\, tell(STDIN)\, $_);   exit(0);   } else {   printf("fork failed!\n");   }   }

With perlio PerlIO_flush(NULL) which is called before C level fork() makes sure things are in sync.

IIRC using sfio in older perls should be similar.

p5pRT commented 23 years ago

From [Unknown Contact. See original ticket]

Nick Ing-Simmons wrote​:

Given that\, one would think that adding an exit() after the printf() and fflush() of the child section would reduce that count to 6 (parent + five children).

No.

Solaris exit() (or fclose(stdin)) does fseek(stdin\,0\,SEEK_CUR)\, which in turn lseek's file 0.

Ahhhhhhh... That was the missing piece of the puzzle. (I should have trussed it I suppose...)

Thus when child does exit() everyone's fd #0 stdin is rewound to where child thinks it is. Then when parent runs out of data in the buffer its next "fill" gets that data at the position last exit() left it\, which is not eof.

Logical\, given the shared file pointers.

 printf\("%ld> %s"\, getpid\(\)\, buf\);
 fflush\(stdout\);
 exit\(0\);

Make that _exit(0);

It does at that. Likewise\, substituting syscall(_exit) for exit() in the original perl script solves the problem there as well. My only remaining question is\, is there a more portable (from the script's point of view) way to solve the problem?

Thanks for the help!

Mike

p5pRT commented 23 years ago

From [Unknown Contact. See original ticket]

At 15​:58 -0500 2000-12-06\, Mike Fisher wrote​:

The first and third Solaris results I listed were single CPU Ultra 5 workstations. The second was a dual CPU E450. All the Linux boxes were single CPU x86.

Ah well. At least Solaris is being consistently inconsistent.

3. Does adding a wait; after the first print make Solaris behave "as expected"?

No\, actually\, it gets worse. Without the wait\, the script at least terminates under Solaris 7 on an Ultra 5\, perl 5.6.0. Adding the extra wait causes it to keep going\, seemingly forever.

Weird!

4. Is the behaviour on Solaris the same if the script's reading STDIN -- from a shell here document or a file -- instead of DATA? (It is for me on MachTen\, but then MachTen behaves with DATA as well.)

If I change read from \ and give input from the keyboard\, it works as expected. If STDIN comes from a file via a shell redirect\, I get extra lines.

See Nicholas Clark's comments.

5. If so\, is the behaviour of an equivalent C program reading from stdin the same as that of the Perl script?

Bingo...

Ah. Thanks for writing the C.

#include \<sys/types.h> #include \<stdio.h> #include \<unistd.h> #include \<sys/wait.h>

char buf[1024]; pid_t me; pid_t pid;

int main() {

me = getpid();

while (fgets(buf\,1024\,stdin)) {

if \(pid = fork\(\)\) \{
  printf\("%ld&#8203;: Fork => %ld\\n"\, me\, pid\);

  wait((int *) 0);

\} else if \(pid == 0\) \{
  printf\("%ld> %s"\, getpid\(\)\, buf\);

  exit(0);

\} else \{
  printf\("fork failed\!\\n"\);
\}

} }

I think those extra lines are needed to make it an exact equivalent of the Perl script with the added call to wait. Certainly\, without the exit(0)\, child processes get the chance to spawn their own children\, generally muddying the waters​:

computer domo$ ./spin \<\<!

1 2 3 4 5 ! 10122> 1 10121​: Fork => 10122 10123> 2 10121​: Fork => 10123 10124> 2 10121​: Fork => 10124 10125> 3 10121​: Fork => 10125 10126> 3 10121​: Fork => 10126 10127> 3 10121​: Fork => 10127 10128> 3 10121​: Fork => 10128 10129> 4 10121​: Fork => 10129 10130> 4 10121​: Fork => 10130 10131> 4 10121​: Fork => 10131 10132> 4 10121​: Fork => 10132 10133> 4 10121​: Fork => 10133 10134> 4 10121​: Fork => 10134 10135> 4 10121​: Fork => 10135 10136> 4 10121​: Fork => 10136 10137> 5 10121​: Fork => 10137 10138> 5 10121​: Fork => 10138 10139> 5 10121​: Fork => 10139 10140> 5 10121​: Fork => 10140 10141> 5 10121​: Fork => 10141 10142> 5 10121​: Fork => 10142 10143> 5 10121​: Fork => 10143 10144> 5 10121​: Fork => 10144 10145> 5 10121​: Fork => 10145 10146> 5 10121​: Fork => 10146 10147> 5 10121​: Fork => 10147 10148> 5 10121​: Fork => 10148 10149> 5 10121​: Fork => 10149 10150> 5 10121​: Fork => 10150 10151> 5 10121​: Fork => 10151 10152> 5 10121​: Fork => 10152 $

Looks like #5 is the culprit. Off to talk to Sun...

Put that exit(0) in before you do. It may make all the difference. The call to wait is less important -- the zombie children will get swept up when the test program exits -- but does make the test a better citizen\, which may earn you brownie points with Sun if the program continues to misbehave and you have to put in that support call.