[comp.sys.hp] fork

roger@zuken.co.jp (Roger Meunier) (08/09/90)

When a C++ program exits, it calls a list of destructors to clean up
static data.  But what happens when exit() is called after fork()?

I ran into a problem with HP's implementation of Motif 1.0.  For the
XmText widget, they allow Chinese character input by setting up a
connection with an input server.  Apparently, they fork() the server
on the first XmCreateText().  I noticed that in one of my applications,
it was taking an extremely long time to create the first text widget
(8+ sec.), and that core was being dumped during the creation.  Looking
at the core file with cdb, I noticed that exit() was being called from
the routine that did the fork(), and that subsequently all my static
destructors were being called, including one that caused the core to
be dumped (it was calling XCloseDisplay()!).  Why the *child* process
should be trying to do this is beyond me.

Has anyone else run into similar problems when fork()'ing in a C++
environment?  Is this a design problem with the C++ static object clean-up
strategy, or is HP's implementation of nlio forgetting to take C++ into
account, or both?
--
Roger Meunier @ Zuken, Inc.  Yokohama, Japan	(roger@zuken.co.jp)

mayer@hplabsz.HPL.HP.COM (Niels Mayer) (08/10/90)

In article <ROGER.90Aug9114200@rd11.zuken.co.jp> roger@zuken.co.jp (Roger Meunier) writes:
>When a C++ program exits, it calls a list of destructors to clean up
>static data.  But what happens when exit() is called after fork()?
>
>I ran into a problem with HP's implementation of Motif 1.0.  For the
>XmText widget, they allow Chinese character input by setting up a
>connection with an input server.  Apparently, they fork() the server
>on the first XmCreateText().  I noticed that in one of my applications,
>it was taking an extremely long time to create the first text widget
>(8+ sec.), and that core was being dumped during the creation.  Looking
>at the core file with cdb, I noticed that exit() was being called from
>the routine that did the fork(), and that subsequently all my static
>destructors were being called, including one that caused the core to
>be dumped (it was calling XCloseDisplay()!).  Why the *child* process
>should be trying to do this is beyond me.
>
>Has anyone else run into similar problems when fork()'ing in a C++
>environment?  Is this a design problem with the C++ static object clean-up
>strategy, or is HP's implementation of nlio forgetting to take C++ into
>account, or both?

I don't think this has much to do with HP's implementation, nor C++ -- it
is a general problem you have to watch out for when doing system(), popen()
and fork() under X.

Basically, your application's open connections to the X server take up file
descriptors, and these are all faithfully copied upon vfork()/fork()....

To fix the problem, you need to close the file descriptors after you
fork, e.g.:
  pid_t pid;
  if((pid = vfork()) == 0)
  {
    for(i=3;i<_NFILE;i++)  /* close all fd except stdin, stdout, and stderr */
        close(i);
    ...
   }

As to why the X implementation doesn't set the close-on-exec flag via
fcntl() to prevent this problem, only the experts know.

			--------------------

I'm trying to deal with a similar problem right now as a matter of fact.

Does anybody have a public domain (or non-restrictive-copyright) version of
popen() and system() that you'd be interested in sharing???

Actually, system() is easy... But I'm too lazy to go figure out how to do
popen() without sullying the pristine "clean-room" environment of my mind.

-------------------------------------------------------------------------------
	    Niels Mayer -- hplabs!mayer -- mayer@hplabs.hp.com
		  Human-Computer Interaction Department
		       Hewlett-Packard Laboratories
			      Palo Alto, CA.
				   *

chan@hpfcmgw.HP.COM (Chan Benson) (08/11/90)

> the pristine "clean-room" environment of my mind.
> 
> 	    Niels Mayer -- hplabs!mayer -- mayer@hplabs.hp.com

Hmmmm. Must be a different Niels Mayer.

But enough levity. If I read the original question correctly, the fork
is being done in the library code, so the application programmer can't
close all the fd's after the fork. Strangely enough though, the library
code *is* closing all the fd's after the fork.

Does your code do any forking? There is some weird stuff in there to 
handle SIGCLD when the NLS server is forked (actually as a grandchild
of the original process).

Any NLS gurus out there? 

			-- Chan Benson
			HP Fort Collins

dave@dptechno.UUCP (Dave Lee) (08/13/90)

In article <5768@hplabsz.HPL.HP.COM> mayer@hplabs.hp.com (Niels Mayer) writes:
>In article <ROGER.90Aug9114200@rd11.zuken.co.jp> roger@zuken.co.jp (Roger Meunier) writes:
>>When a C++ program exits, it calls a list of destructors to clean up
>>static data.  But what happens when exit() is called after fork()?
>
>  pid_t pid;
>  if((pid = vfork()) == 0)
>  {
>    for(i=3;i<_NFILE;i++)  /* close all fd except stdin, stdout, and stderr */
>        close(i);
>    ...
>   }
>
>As to why the X implementation doesn't set the close-on-exec flag via
>fcntl() to prevent this problem, only the experts know.
>
>Does anybody have a public domain (or non-restrictive-copyright) version of
>popen() and system() that you'd be interested in sharing???
>
>Actually, system() is easy... But I'm too lazy to go figure out how to do
>popen() without sullying the pristine "clean-room" environment of my mind.


A cleaner way to handle the above problems (if indeed they are due to
the open fd from a display connection) is ...

#include <fcntl.h>
fcntl( DISPLAY_PTR , F_SETFD , 1 );

Where "DISPLAY_PTR" is a pointer to the open Display from XOpenDisplay();

Insert this just after opening the display, or before doing any system()
popen(), or fork()/exec.

This should then remove the need to recode system() or popen() from scratch.

#
#
#
#
# 
#






-- 
Dave Lee
uunet!dptechno!dave

roger@zuken.co.jp (Roger Meunier) (08/20/90)

In article <5768@hplabsz.HPL.HP.COM> mayer@hplabsz.HPL.HP.COM (Niels Mayer) writes:

 >I don't think this has much to do with HP's implementation, nor C++ -- it
 >is a general problem you have to watch out for when doing system(), popen()
 >and fork() under X.
 >
 >Basically, your application's open connections to the X server take up file
 >descriptors, and these are all faithfully copied upon vfork()/fork()....
 >
 >To fix the problem, you need to close the file descriptors after you
 >fork,               ^^^

I am not the one who is fork()'ing; Motif is.  My destructors get called
when the kanji input server (/usr/lib/nlio/serv/X11/xj0input) is spawned,
because exit() (rather than _exit()?) is being called in the child
environment.  My destructor calls XCloseDisplay(), at which time some
error occurs, and core is dumped because the error message cannot be
output.  Here's a sample stack trace of the core dump:

	 0 __doprnt + 0x6
	 1 __fprintf + 0x32
	 2 __XIOError + 0x30
	 3 __XReply + 0x20
	 4 _XSync + 0x52
	 5 _XCloseDisplay + 0x64
	...
	10 _STDmain_cxx_ ()
	11 _exit + 0x38
	12 __XIOError + 0x80
	13 __XReply + 0x20
	14 _XSync + 0x52
	15 _XCloseDisplay + 0x64
	...
	49003 _STDmain_cxx_ + 0x10
	49004 _exit + 0x38
	49005 _XHPInputChinese_t + 0x652
	49006 _XHPNlioctl + 0xac
	49007 _XmTextInputCreate + 0x218
	49008 _XmTextSetHighlight + 0x2f6
	49009 __XtAddDefaultConverters + 0x520
	49010 __XtCreate + 0x1da
	49011 _XtCreateWidget + 0xac
	49012 _XmCreateText + 0x1c
	...

In this case, the error during XCloseDisplay() causes a call to exit(),
which calls the destructors *again*, ad infinitum.  This doesn't occur
every time; usually _XIOError() bombs at the first _doprnt() and never
gets to call exit().

This raises another question: if static destructors can trigger an error
condition which in turn triggers a call to exit(), is there some standard
method for avoiding the above infinite looping?
--
Roger Meunier @ Zuken, Inc.  Yokohama, Japan	(roger@zuken.co.jp)