Unix Programming - possible race condition with fork and GLUT idle callback? weird problem

This is Interesting: Free IT Magazines  
Home > Archive > Unix Programming > November 2006 > possible race condition with fork and GLUT idle callback? weird problem





You are viewing an archived Text-only version of the thread. To view this thread in it's original format and/or if you want to reply to this thread please [click here]

Author possible race condition with fork and GLUT idle callback? weird problem
atv

2006-11-16, 1:17 pm

Ok,
I'm busy building an opengl application with glut. This doesn't really
matter, but just so you can put it in the right context. I have a
callback called idle(), which is called alot of times.

Now, i have one piece of code in here, which when i compile and run it
(i use xcode) i see this strange thing in the run log. It seems almost
like a race condition. I duplicated this by making a small c program
and calling a forkthis() function with the code in question in it in a
tight for loop. But this reproduction doesn't display anything weird. I
wonder if GLUT calls the idle() function faster then a for(;;) loop
could.

Anyway, here is what i see in the run log once running. The first fork
runs fine, after that it goes haywire:

[Session started at 2006-11-16 18:31:12 +0100.]
No log handling enabled - turning on stderr logging
snmpget: Timeout (No route to host)
Forking new processSNMPv2-MIB::sysDescr.0
--lots of output--

Now here is when it goes increasinly wrong (or so it seems):
snmpget: Timeout (No route to host)
No log handling enabled - turning on stderr logging
snmpget: Failure in sendto (Host is down)
Forking new processChild returnedForking new
processSNMPv2-MIB::sysDescr.0 = STRING: Dar

And this seems to grow exponentially
(forkingnewprocess;childreturned;forking
newprocessetc) until the entire
screen is filled.

So, like i said, i tried and reproduce it by making this:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>

int forkthis(void);
pid_t val=1;

int main(void)
{
for(;;) {
forkthis();
}
}

int forkthis(void)
{
int status;

if(wait4(val,&status,WNOHANG,NULL)==val)
printf("Child returned with pid %d\n",val);

if(kill(val,0)) {
printf("Forking new process\n");

if((val=fork())==-1) {
fprintf(stderr,"Could not fork\n");
exit(1);}

else if(!val) {
printf("I am the child with pid: %d\n",getpid());
exit(1);
}
else {
printf("I am the parent. My pid is %d and my child pid is
%d\n",getpid(),val);
}
}
}

But this outputs correctly:
Forking new process
I am the child with pid: 6029
I am the parent. My pid is 5845 and my child pid is 6029
Child returned with pid 6029
Forking new process
I am the child with pid: 6030
I am the parent. My pid is 5845 and my child pid is 6030
Child returned with pid 6030
Forking new process

etc. The code posted is exactly the same as in the application itself,
only without the for(;;) call in main. I have thought about the
possibility that

1. the loop is run once ok
2. idle() is called again while child has not yet returned or is in the
process of doing so (this would explain why i get so much "Forking new
process" and right after the "Child returned". While it is checking to
see if the process actually exists with the kill() function, it did
just return and forks a new process. This would introduce a race
condition i'd rather not have :-)

Is this theory is sound, how do i fix it ? :-P
Thanks for any replies!
gr,

Jens Thoms Toerring

2006-11-19, 1:16 pm

atv <alef@xs4all.nl> wrote:
> I'm busy building an opengl application with glut. This doesn't really
> matter, but just so you can put it in the right context. I have a
> callback called idle(), which is called alot of times.


> Now, i have one piece of code in here, which when i compile and run it
> (i use xcode) i see this strange thing in the run log. It seems almost
> like a race condition. I duplicated this by making a small c program
> and calling a forkthis() function with the code in question in it in a
> tight for loop. But this reproduction doesn't display anything weird. I
> wonder if GLUT calls the idle() function faster then a for(;;) loop
> could.


> Anyway, here is what i see in the run log once running. The first fork
> runs fine, after that it goes haywire:


> [Session started at 2006-11-16 18:31:12 +0100.]
> No log handling enabled - turning on stderr logging
> snmpget: Timeout (No route to host)
> Forking new processSNMPv2-MIB::sysDescr.0
> --lots of output--


> Now here is when it goes increasinly wrong (or so it seems):
> snmpget: Timeout (No route to host)
> No log handling enabled - turning on stderr logging
> snmpget: Failure in sendto (Host is down)
> Forking new processChild returnedForking new
> processSNMPv2-MIB::sysDescr.0 = STRING: Dar


> And this seems to grow exponentially
> (forkingnewprocess;childreturned;forking
newprocessetc) until the entire
> screen is filled.


If there's something "exponentially" going on my first guess would be
that the child doesn't terminate once it has done its job but instead
runs also the parents code and creates new children of its own...

> So, like i said, i tried and reproduce it by making this:


It's always problematic not to post code that exhibits the problem
but instead code that does not - how are we supposed to see what's
going wrong when all we have is code that works as expected?

> #include <stdio.h>
> #include <stdlib.h>
> #include <unistd.h>
> #include <sys/types.h>


> int forkthis(void);
> pid_t val=1;


> int main(void)
> {
> for(;;) {
> forkthis();
> }
> }


> int forkthis(void)
> {
> int status;


> if(wait4(val,&status,WNOHANG,NULL)==val)


Why don't you just use waitpid()?

> printf("Child returned with pid %d\n",val);


> if(kill(val,0)) {
> printf("Forking new process\n");


> if((val=fork())==-1) {
> fprintf(stderr,"Could not fork\n");
> exit(1);}


> else if(!val) {
> printf("I am the child with pid: %d\n",getpid());
> exit(1);
> }
> else {
> printf("I am the parent. My pid is %d and my child pid is
> %d\n",getpid(),val);
> }
> }
> }


> But this outputs correctly:
> Forking new process
> I am the child with pid: 6029
> I am the parent. My pid is 5845 and my child pid is 6029
> Child returned with pid 6029
> Forking new process
> I am the child with pid: 6030
> I am the parent. My pid is 5845 and my child pid is 6030
> Child returned with pid 6030
> Forking new process


That's one possible output - but the output of the parent after the
fork() also could come before the one from the child, there's no
guarantee that the child will run first (on a SMP machine they could
even run in parallel).

> etc. The code posted is exactly the same as in the application itself,
> only without the for(;;) call in main.


Since it behaves differently please allow me to have some doubts
about the code for your "real" program being exactly the same...

> I have thought about the possibility that


> 1. the loop is run once ok
> 2. idle() is called again while child has not yet returned or is in the
> process of doing so (this would explain why i get so much "Forking new
> process" and right after the "Child returned". While it is checking to
> see if the process actually exists with the kill() function, it did
> just return and forks a new process. This would introduce a race
> condition i'd rather not have :-)


Please could you rewrite that sentence in English that makes sense?
I have tried to parse it several times but don't understand it.

There is definitely a chance that between the call of wait4() and
the call of kill() the child dies, so wait4() returns 0 and kill()
return -1. But I don't see why you have to call both wait4() and
kill(). In that case you wouldn't be reaping in the return value of
the child (leaving a zombie behind) and "Child returned with pid"
wouldn't be printed out for that child.

But, as far as I can see, there's no need at all for the call of
kill(), you could simply do e.g.

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>

int fork_this(void);

pid_t val = -1;

int main( void )
{
for ( ; ; )
fork_this( );

return 0;
}

int fork_this( void )
{
int status;

if ( waitpid( val, &status, WNOHANG ) == val )
{
if ( val != -1 )
printf( "Child returned with pid %d\n", val );

printf( "Forking new process\n" );

if ( ( val = fork( ) ) == -1 )
{
fprintf( stderr, "Could not fork\n" );
exit( 1 );
}
else if ( val == 0 )
{
printf("I am the child with pid: %d\n", getpid( ) );
sleep( 2 );
exit( 1 );
}
else
printf( "I am the parent. My pid is %d and my child pid is %d\n",
getpid( ), val );
}
else if ( val != 1 )
{
printf( "Child isn't dead yet\n" );
sleep( 1 );
}

return 0;
}

Please note: setting up val to -1 at the start guarantees that waitpid()
will return -1 (which then is equal to val) so the very first child gets
spawned.
Regards, Jens
--
\ Jens Thoms Toerring ___ jt@toerring.de
\__________________________ http://toerring.de
Sponsored Links






Free braindumps | Software forum | Database administration forum

Copyright 2003 - 2008 webservertalk.com