Reputation: 720
I am trying to use fork to execute child programs from a multithreaded parent using code similar to:
#include <thread>
#include <unistd.h>
#include <vector>
#include <sys/wait.h>
void printWithCat(const std::string& data) {
std::vector<char*> commandLine;
// exec won't change argument so safe cast
commandLine.push_back(const_cast<char*>("cat"));
commandLine.push_back(0);
int pipes[2];
pipe(pipes);
// Race condition here
pid_t pid = fork();
if (pid == 0) {
// Redirect pipes[0] to stdin
close(pipes[1]);
close(0);
dup(pipes[0]);
close(pipes[0]);
execvp("cat", &commandLine.front());
}
else {
close(pipes[0]);
write(pipes[1], (void*)(data.data()), data.size());
close(pipes[1]);
waitpid(pid, NULL, 0);
}
}
int main()
{
std::thread t1(printWithCat, "Hello, ");
std::thread t2(printWithCat, "World!");
t1.join();
t2.join();
}
This code contains a race condition between the call to pipe and the call to fork. If both threads create pipes and then fork, each child process contains open file descriptors to both pipes and only close one. The result is that a pipe never gets closed and the child process never exits. I currently wrap the pipe and fork calls in a global lock but this adds an additional synchronisation. Is there a better way?
Upvotes: 2
Views: 1350
Reputation: 283971
Don't think you're avoiding synchronization by avoiding a lock in your code -- the kernel is going to take locks for process creation anyway, probably on a far more global level than your lock.
So go ahead and use a lightweight mutex here.
Your problems are going to arise when different parts of the program make fork
calls and don't agree on a single mutex (because some are buried in library code, etc)
Upvotes: 1