Communities

Writing
Writing
Codidact Meta
Codidact Meta
The Great Outdoors
The Great Outdoors
Photography & Video
Photography & Video
Scientific Speculation
Scientific Speculation
Cooking
Cooking
Electrical Engineering
Electrical Engineering
Judaism
Judaism
Languages & Linguistics
Languages & Linguistics
Software Development
Software Development
Mathematics
Mathematics
Christianity
Christianity
Code Golf
Code Golf
Music
Music
Physics
Physics
Linux Systems
Linux Systems
Power Users
Power Users
Tabletop RPGs
Tabletop RPGs
Community Proposals
Community Proposals
tag:snake search within a tag
answers:0 unanswered questions
user:xxxx search by author id
score:0.5 posts with 0.5+ score
"snake oil" exact phrase
votes:4 posts with 4+ votes
created:<1w created < 1 week ago
post_type:xxxx type of post
Search help
Notifications
Mark all as read See all your notifications »
Q&A

Welcome to Software Development on Codidact!

Will you help us build our independent community of developers helping developers? We're small and trying to grow. We welcome questions about all aspects of software development, from design to code to QA and more. Got questions? Got answers? Got code you'd like someone to review? Please join us.

Comments on How kill a child process without read() hanging in the parent process?

Parent

How kill a child process without read() hanging in the parent process?

+6
−0

There is an external program I'm calling from within my C/C++ program, by using fork() and execl(), and redirecting the stdio with dup2() to be able to read the output from the external program. It's based on this code: A simple popen2 implementation

In another thread just started, the timeout handling is done.

Then I make calls to read() on the out file handle, in a loop, writing what's read to a stream.

Now that external program waits for something on the system to happen - which might not happen, so after a while, I'd like to call it a timeout and end the child process (from the extra thread).

Whether sending SIGTERM or SIGKILL, changing the order of closing file handles w.r.t. to that, the result is always that the last call to read() hangs forever.

Added: Seems even if the process exits in time, the read() hangs. That did not happen when I used the regular popen() to start the process instead of popen2(), but since I need the PID to terminate the process, I'm using the latter.

I have found discussions about a problem with read() in a somewhat similar situation, and it was suggested that the SIGCHLD must be ignored in the parent process. That doesn't help, though.

Is there something wrong per se in doing things this way, that cause this read() to hang?

int exec_proc2(std::string cmd, std::ostream & outputStream, unsigned timeoutMilsecs)
{
    constexpr int bufsize = 128;
    std::array<char, bufsize> buffer;
    
    int inf, outf;
    auto pid = popen2( cmd.c_str(), &inf, &outf );
    close( inf ); // writing to process not used, we're only reading
    if (pid < 0)
    {   close( outf );
        return -1;
    }
    
    std::atomic<bool> notDone = true;
    std::atomic<bool> timedOut = false;
    thread timeoutTask( [&]
        {   TimeoutWatch tw(timeoutMilsecs, true);
            while (notDone && !tw.TimedOut())
            {   sleep(0.1);
            }
            if (tw.TimedOut())
            {   close( outf );
                int ret = kill( pid, SIGKILL );
                if (ret != 0)
                {   // this never failed so far
                }
                notDone = false;
                cout << "DBG: TIMEOUT@exec_proc2" << endl; // this is always printed after the timeout time
                timedOut = true;
            }
        } );
    
    size_t count;
    do
    {   if ((count = read( outf, buffer.data(), bufsize )) > 0)
  	    {   outputStream.write( buffer.data(), count );
  	    }
    } while(count > 0 && !timedOut);
    cout << "DBG: joining...@exec_proc2" << endl; // this line is never printed, so it hangs above already
    notDone = false; // for the case that the read() loop exits earlier than the timeout thread: tell the thread it's over
    timeoutTask.join();
    
    close( outf );
    return timedOut ? -2 : 0;
}
History
Why does this post require attention from curators or moderators?
You might want to add some details to your flag.
Why should this post be closed?

0 comment threads

Post
+3
−0

Below is a new version of popen2() that works. Some pipe ends were not closed in the linked to original version. (see the branch for "pid != 0", where extra handles are closed - whether there is an error or not, but it's the parent process) (I also renamed the pipe variables to something more clear to me)

The exec_proc2() code from the 1st post remains unchanged.

pid_t popen2(const char *command, int *infp, int *outfp)
{
    constexpr int READ = 0, WRITE = 1;
    int tochild[2], fromchild[2];
    if (pipe(tochild) != 0 || pipe(fromchild) != 0)
        return -1;

    const pid_t pid = fork();

    if (pid != 0) // we're in the parent process
    {	close(tochild[READ]); // Always close these ends, too
        close(fromchild[WRITE]);
        if (pid < 0) // ERROR
            return pid;
     }
    else // if (pid == 0) // fork() returns the child process ID to the parent and returns 0 to the child process
    {
        dup2(tochild[READ], READ);
        dup2(fromchild[WRITE], WRITE);
        close(tochild[READ]); close(tochild[WRITE]);
        close(fromchild[READ]); close(fromchild[WRITE]);

        execl("/bin/sh", "sh", "-c", command, NULL);
        perror("execl");
        _exit(1);
    }

    if (infp == NULL)
        close(tochild[WRITE]);
    else
        *infp = tochild[WRITE];

    if (outfp == NULL)
        close(fromchild[READ]);
    else
        *outfp = fromchild[READ];

    return pid;
}
History
Why does this post require attention from curators or moderators?
You might want to add some details to your flag.

1 comment thread

Can you please be explicit on how that version differs from the original one? And what that crucial h... (6 comments)
Can you please be explicit on how that version differs from the original one? And what that crucial h...
celtschk‭ wrote about 3 years ago

Can you please be explicit on how that version differs from the original one? And what that crucial hint was?

sktpin‭ wrote about 3 years ago

I added where to look for the extra closing. As for the hint, I saw it in a similar scenario on a site I'm note sure is welcome to reference here, as, from what I gathered, this site was created for the very reason of the originators having some major beef with that other site... ;)

celtschk‭ wrote about 3 years ago

Well, the extra information you added spells out the crucial bit (closing of file handles that weren't closed before; thanks to that now I have an idea what went wrong before), therefore have my +1.

Links are not that relevant (except when not using them would violate a license or result in plagiarism) as they tend to rot anyway. Including the relevant information (in this case, the fact that closing file handles in the parent process solved the problem) is what is important.

Lundin‭ wrote about 3 years ago

sktpin‭ There's no beef as such with Stack Overflow, if that's what you refer to (references like "Somewhere Else" etc are mostly tongue-in-cheek). It's perfectly fine to refer to it (and import your content from there to here). Codidact simply strives to be something else: community-driven open-source, instead of profit-driven closed-source.

Lundin‭ wrote about 3 years ago

As for the technical content, why would you do this with multiple processes and IPC (pipes) instead of multithreading?

sktpin‭ wrote about 3 years ago

I'm not sure what you are asking. Having a mechanism for calling executables seems quite nice if you want to execute, well, other executables? How would threads replace that?