1

My application starts a subprocess program to read video using the QuickTime framework via fork() and pipes. The subprocess goes into a wait loop when it is not busy, i.e. it does usleep until there is input. The subprocess is not a GUI application and it is written in C++.

When opening AVI video coded using the MSVC codec, a second copy of the application icon shows in the dock and bounces. After about 30 seconds in the Activity Monitor I can see that the subprocess changes to "not responding" even though CPU appears to be ~0%. The subprocess is still running and responding; it's just that Activity Monitor says otherwise.

If I look at the state of the subprocess, via gdb attach or check its output; everything looks fine. I can tell the subprocess to close the file and open another one and continue using it at which point the bouncing dock icon disappears and the process is not marked as not responding.

It's as if OSX thinks my subprocess has crashed (?) but I cannot detect an exception.

How can I stop the subprocess showing an icon in the dock, bouncing and being marked as not responding ?

This is how I set up communication with the subprocess:

#include <unistd.h>

#define READ 0
#define WRITE 1

// Start process
pid_t popen2(const char *command, char * const argv[], int *infp, int *outfp)
{
  int p_stdin[2], p_stdout[2];
  pid_t pid;

  // Set up pipes
  if(pipe(p_stdin) != 0 || pipe(p_stdout) != 0)
    return(-1);

  pid = fork();

  if(pid < 0)
    return(pid);
  else if(pid == 0)
  {
    // Set up communication via stdin/out
    close(p_stdin[WRITE]);
    dup2(p_stdin[READ], READ);
    close(p_stdout[READ]);
    dup2(p_stdout[WRITE], WRITE);

    execvp(command, argv); // run subprocess
    perror("execvp");

    exit(1);
  }

  // Provide pointers to the file descriptors to the caller
  if(infp == NULL)
    close(p_stdin[WRITE]);
  else
    *infp = p_stdin[WRITE];

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

  return(pid);
}

See this SO question for more discussion of popen2().

Note: this code may or may not be the cause of my problem. As a first step, I would really like to prove what is the cause.

Community
  • 1
  • 1
koan
  • 3,596
  • 2
  • 25
  • 35
  • Show us some code (and tell us what "some specific data" means). – John Zwinck Jun 22 '13 at 09:20
  • Code for what part ? The subprocess uses the QuickTime framework. When opening some AVI with MSVC codec I can observe the described behaviour. I suspect some other codecs that QuickTime *can* recognise also cause bouncing icon/not responding. – koan Jun 22 '13 at 10:05
  • It's hard for me to know what part of the code is relevant, but having some code--any part of it--might help. I have an OS X computer right here, yet I cannot do much with the question as it stands (in fact, what is the question??). – John Zwinck Jun 22 '13 at 10:07
  • Subprocesses shouldn't get a dock icon (bouncing or otherwise) and shouldn't be labelled as not responding when for all intents and purposes it is responding. My question is how to stop this behaviour ? – koan Jun 22 '13 at 10:23
  • You haven't told us what mechanism or API you are using to spawn the subprocess, for example. This is Not a Real Question. – John Zwinck Jun 22 '13 at 10:34
  • Please help me improve the question – koan Jun 22 '13 at 10:58
  • You should comment more your code. (Almost) Per each line, explain what you are trying to do, like the lines where you are connecting the pipe to stdin and stdout. Put also the reference to the source code you have used as starting point: it will help us to understand where to search the problem. – Antonio Jun 28 '13 at 00:05

3 Answers3

3

The “not responding” part is simple: Your subprocess is not running a runloop of any type, so from the POV of the system, it’s not handling events.

I’m a bit hazier on why your new process is getting a dock icon, but it basically boils down to fork() creating a process that inherits the attributes of the parent process (in this case, of it being a foreground application). OS X has a number of mechanisms to launch subprocesses in more sensible ways than fork(). If your app is in Cocoa, use NSTask, otherwise, take a look at posix_spawn(2).

This should be a drop in replacement for your routine:

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <spawn.h>
#include <signal.h>
#include <crt_externs.h>

#define READ 0
#define WRITE 1
#define environ (*_NSGetEnviron())

pid_t popen2(const char *command, char * const argv[], int *infp, int *outfp)
{
    int p_stdin[2], p_stdout[2];
    pid_t pid;

    if(pipe(p_stdin) != 0 || pipe(p_stdout) != 0)
        return(-1);

    posix_spawn_file_actions_t file_actions;
    posix_spawn_file_actions_init(&file_actions);
    posix_spawn_file_actions_adddup2(&file_actions, p_stdin[READ], 0);
    posix_spawn_file_actions_adddup2(&file_actions, p_stdout[WRITE], 1);
    posix_spawn_file_actions_adddup2(&file_actions, 2, 2);

    posix_spawnattr_t spawnAttributes;

    posix_spawnattr_init(&spawnAttributes);
    sigset_t no_signals;
    sigset_t all_signals;
    sigemptyset (&no_signals);
    sigfillset (&all_signals);
    posix_spawnattr_setsigmask(&spawnAttributes, &no_signals);
    posix_spawnattr_setsigdefault(&spawnAttributes, &all_signals);
    short flags = POSIX_SPAWN_CLOEXEC_DEFAULT | POSIX_SPAWN_SETSIGMASK | POSIX_SPAWN_SETSIGDEF;
    posix_spawnattr_setflags(&spawnAttributes, flags);

    if (posix_spawn(&pid, command, &file_actions, &spawnAttributes, argv, environ)) {
        perror("posix_spawn");

        exit(1);
    }

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

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

    return(pid);
}
microtherion
  • 3,938
  • 1
  • 15
  • 18
  • The subprocess doesn't generate a dock icon or "not responding" when I open other video files. I'd like to continue using `fork()` if possible; it seems to work most of the time; unless you have a theory of why it would be better it could mean more work and still have the same problem. – koan Jun 22 '13 at 11:18
  • Most likely that particular codec uses a framework other codecs are not using, which interacts adversely with state you inherited from your app process. Issues like this are rampant when using `fork()` on app processes, because you’re inheriting all sorts of state, much of which is not meant to be inherited. – microtherion Jun 22 '13 at 11:50
  • If we go along with your theory, would there be a way to test this ? If I run the subprocess directly from the command line and open the video file, there is no bounce or "not responding". Perhaps we can prove your theory by running a fork() and a NSTask from Python or another scripting language ? (I don't know Python though). – koan Jun 22 '13 at 13:47
  • You could test this from python, but only from a python based cocoa app, since the bouncing is due to state inherited from your main app. Check out PyObjC http://pythonhosted.org/pyobjc/ for examples. But what makes you so hesitant to restructure your actual app? Your subprocess seems to be a separate binary, so it sounds to me like you have a pretty straightforward `fork()/exec()` construct that would map into an equally straightforward `NSTask`. – microtherion Jun 22 '13 at 19:17
  • I don't know Objective C so well and setting up communication via std io can be tricky. If you had an example that showed using fork could cause a similar dock icon bounce/not responding then I would be more convinced that your answer would solve my problem. – koan Jun 24 '13 at 10:03
  • If you post a code snippet showing how you’re currently setting up communication with your subprocess, I can probably show you how to do the equivalent with `NSTask`. – microtherion Jun 24 '13 at 16:46
  • OK. I’ve updated my answer to include what should be a drop in replacement for your `popen2()` routine. – microtherion Jun 25 '13 at 15:35
  • Thanks a lot for your code; I tried it out, but it didn't solve the problem, the extra bouncing icon still appears. – koan Jun 26 '13 at 14:19
  • Hmm. One more thing you could try is to add `POSIX_SPAWN_SETPGROUP` to the list of flags in the code above. – microtherion Jun 26 '13 at 18:36
  • Still no joy. Incidentally, your code is not a direct replacement; for some reason the parent process hangs (beach ball) if there is a controlled exit of the subprocess including closing the pipes and `waitpid()`. – koan Jun 26 '13 at 20:25
  • What does `sample` say? Sorry to be dragging this discussion on and on, but I wouldn’t want my code to stay up here if it is buggy. – microtherion Jun 26 '13 at 22:36
  • I'm not sure that your code is buggy. There seems to be some difference in the way pipes are handled when closing them which causes problems for the parent process. – koan Jun 27 '13 at 09:57
0

If you're using a compiler that supports c++11, I'd recommend checking out packaged_tasks.

Alternatively, you could also use condition_variables, but I'd try to get it working with packaged tasks first. Condition variables are more of the primitive than the higher-level packaged tasks. Either way, it's a lot easier (and standards compliant) to use these mechanisms than tradition IPC techniques.

That is, of course, if you don't have to have a separate process.

For further information, I'd highly recommend checking out The C++ Programming Language, 4th Edition. It has simple example of a producer/consumer mechanism with a simple vector that may suit you well. If you don't spring for the book, I'm sure you can find similar examples online for using condition_variables, futures or promises.

HTH

Homer6
  • 15,034
  • 11
  • 61
  • 81
  • The subprocess must be separate. In this case it's the reason for its existence: QuickTime is a 32 bit only library and this is a helper process. – koan Jun 28 '13 at 08:40
0

I ended up rewriting the subprocess as a MacOS XPC service. In the XPC service's property list I added LSBackgroundOnly to get Launch Services to ignore system event handling.

koan
  • 3,596
  • 2
  • 25
  • 35