[GRASS5] R_open_driver() after fork()

Glynn Clements glynn.clements at virgin.net
Thu Feb 13 12:25:48 EST 2003


Radim Blazek wrote:

> > > In sync_driver() it hangs on read(). SIGALRM is recieved and dead()
> > > executed, but module doesn't continue in execution - still hanging on
> > > read(), until child is killed.
> >
> > The timeout code relies upon signal() having the SysV semantics
> > (signals interrupt system calls); it won't work if signal() has the
> > BSD semantics (which is the case on Linux with glibc 2.x).
> >
> > Using sigaction() (without SA_RESTART) works; try the attached patch.
> >
> > However, I'm unsure as to the portability issues involved. E.g.
> > signal() is specified by ANSI (but its semantics aren't well defined),
> > while sigaction() is POSIX. OTOH, the new r.mapcalc uses sigaction()
> > to catch SIGFPE, and no-one has complained about that yet (but that
> > doesn't really say much; we only get regular feedback for Linux and
> > MacOSX, and occasional feedback for (specific versions of) Cygwin,
> > Irix and Solaris).
> 
> No, it did not help,

With the patch, R_open_driver() shouldn't hang indefinitely in
sync_driver(); you should get a warning after 5 seconds then failure
after a further 10 seconds (15 seconds total). Basically, the second
R_open_driver() call should eventually fail, rather than blocking
indefinitely.

If SIGALRM fails to interrupt the read() call even with the patch,
then your OS kernel is broken.

There are two distinct issues here.

The first is that the child is holding the connection open, preventing
further connection attempts. This is essentially a problem with your
program, although the libraster API could be changed so as to prevent
it.

The second is that the existing timeout code doesn't work if signal()
has BSD semantics.

The patch only attempts to fix the second problem (which is a bug in
libraster), not the first (which is both a bug in your program and a
limitation of the current libraster API).

> but I was not precise enough in my first mail,
> I discovered later that the problem appeares if fork() is executed 
> when driver is opened:
> 
> R_open_driver();
> pid fork(); 
> if (pid > 0) {
>     R_close_driver();
>     R_open_driver();
> }
> 
> I can avoid this by closing monitor before fork() is called -
> but is it final solution?

At present, yes; otherwise the child will hold the connection open,
preventing subsequent connections.

Radim Blazek wrote (subsequently):

> Yes, this works, but in fact I do not call fork() directly in module.
> I call F_open() to open GUI form and this function creates child,
> so child doesn't know if driver is open. 

Presumably the child calls one of the exec*() functions shortly after
the fork()? In which case, we should probably set the close-on-exec
flag on monitor connections:

	flags = fcntl(_wfd, F_GETFD);
	flags |= FD_CLOEXEC;
	fcntl(_wfd, F_SETFD, flags);

This will force the descriptor to be closed automatically upon
exec*().

> Can I get info if driver is open?

No.

Eric G. Miller wrote:

> Does it work correctly to have the child also call R_close_driver()?

In general, it won't; R_close_driver() calls R_stabilize(), which
sends data to the monitor; that data may end up being interleaved with
data from the other process.

This is similar to the problem of using fork() with ANSI I/O; if any
output streams have buffered data, it may end up being written twice.

More generally, fork() and high-level APIs don't mix.

-- 
Glynn Clements <glynn.clements at virgin.net>




More information about the grass-dev mailing list