I’m trying to spawn a new process from my C++-project using fork-exec. I’m using fork-exec in order to create a bi-directional pipe to the child process. But I’m afraid my resources in the forked process won’t get freed properly, since the exec-call will completely take over my process and is not going to call any destructors.
I tried circumventing this by throwing an exception and calling execl from a catch block at the end of main, but this solution doesn’t destruct any singletons.
Is there any sensible way to achieve this safely? (hopefully avoiding any atExit hacks)
Ex: The following code outputs:
We are the child, gogo!
Parent proc, do nothing
Destroying object
Even though the forked process also has a copy of the singleton which needs to be destructed before I call execl.
#include <iostream>
#include <unistd.h>
using namespace std;
class Resources
{
public:
~Resources() { cout<<"Destroying object\n"; }
};
Resources& getRes()
{
static Resources r1;
return r1;
}
void makeChild(const string &command)
{
int pid = fork();
switch(pid)
{
case -1:
cout<<"Big error! Wtf!\n";
return;
case 0:
cout<<"Parent proc, do nothing\n";
return;
}
cout<<"We are the child, gogo!\n";
throw command;
}
int main(int argc, char* argv[])
{
try
{
Resources& ref = getRes();
makeChild("child");
}
catch(const string &command)
{
execl(command.c_str(), "");
}
return 0;
}
There are excellent odds that you don’t need to call any destructors in between
forkandexec. Yeah,forkmakes a copy of your entire process state, including objects that have destructors, andexecobliterates all that state. But does it actually matter? Can an observer from outside your program — another, unrelated process running on the same computer — tell that destructors weren’t run in the child? If there’s no way to tell, there’s no need to run them.Even if an external observer can tell, it may be actively wrong to run destructors in the child. The usual example for this is: imagine you wrote something to
stdoutbefore callingfork, but it got buffered in the library and so has not actually been delivered to the operating system yet. In that case, you must not callfcloseorfflushonstdoutin the child, or the output will happen twice! (This is also why you almost certainly should call_exitinstead ofexitif theexecfails.)Having said all that, there are two common cases where you might need to do some cleanup work in the child. One is file descriptors (do not confuse these with stdio FILEs or iostream objects) that should not be open after the
exec. The correct way to deal with these is to set theFD_CLOEXECflag on them as soon as possible after they are opened (some OSes allow you to do this inopenitself, but that’s not universal) and/or to loop from 3 to some large number callingclose(notfclose) in the child. (FreeBSD hasclosefrom, but as far as I know, nobody else does, which is a shame because it’s really quite handy.)The other case is system-global thread locks, which – this is a thorny and poorly standardized area – may wind up held by both the parent and the child, and then inherited across
execinto a process that has no idea it holds a lock. This is whatpthread_atforkis supposed to be for, but I have read that in practice it doesn’t work reliably. The only advice I can offer is “don’t be holding any locks when you callfork“, say sorry.