NSTask terminates when NSApplication exits

  • I am trying to write a program that maintains different installs of
    another program including launching the program. To do so, I am using
    NSTask. Now when I quit my cocoa app. the NSTask app dies. The task
    that the NSTask is running is a Java program, not sure if that makes a
    difference. According to what I have read, the application should keep
    running even when the parent task exits. I am running my cocoa app
    from in XCode4, not sure if that has any effect on it. Could it be
    that I am not specifying standard* streams so when the parent app
    quits, those streams are closed and thus the process?

    I can probably find out the answer by trying different things, but I'd
    like to get a better insight for what is going on and why the child
    task is terminating.
  • On Jan 18, 2012, at 11:59 AM, Andrew wrote:

    > I can probably find out the answer by trying different things, but I'd
    > like to get a better insight for what is going on and why the child
    > task is terminating.

    You may want to try LSxxxx (Launch Services) routines.

    --
    Scott Ribe
    <scott_ribe...>
    http://www.elevated-dev.com/
    (303) 722-0567 voice
  • On Jan 18, 2012, at 10:59 AM, Andrew wrote:

    > I am trying to write a program that maintains different installs of
    > another program including launching the program. To do so, I am using
    > NSTask. Now when I quit my cocoa app. the NSTask app dies. …
    > According to what I have read, the application should keep
    > running even when the parent task exits.

    Nope. In Unix, a process is killed when its parent process exits. This is one reason why you shouldn't launch application processes directly :) Instead, use NSWorkspace to launch the app (or call LaunchServices directly if you prefer a C API.) This starts it up in the normal way, as a subprocess of your login session’s main launchd process.

    —Jens
  • On Jan 18, 2012, at 11:59 AM, Andrew wrote:

    > I am trying to write a program that maintains different installs of
    > another program including launching the program. To do so, I am using
    > NSTask. Now when I quit my cocoa app. the NSTask app dies. The task
    > that the NSTask is running is a Java program, not sure if that makes a
    > difference. According to what I have read, the application should keep
    > running even when the parent task exits. I am running my cocoa app
    > from in XCode4, not sure if that has any effect on it. Could it be
    > that I am not specifying standard* streams so when the parent app
    > quits, those streams are closed and thus the process?
    >
    > I can probably find out the answer by trying different things, but I'd
    > like to get a better insight for what is going on and why the child
    > task is terminating.

    Any special handling of NSTask aside, Mac OS X uses Unix-based process control which closes all child processes when the parent is closed. Since your sub-program is Java you may be able to detach the child process. This is usually accomplished by the sub-program by executing a low-level fork(). It may need to be followed by an exec() to fully detach the process. Note that you can't do this with Cocoa/Objective-C (at least Apple says you shouldn't…)

    Alternatively (and probably preferably) you could use launchd/Launch Services as recommended. Useful reading: http://developer.apple.com/library/mac/#documentation/MacOSX/Conceptual/BPS
    ystemStartup/Chapters/Introduction.html


    HTH,

    Keary Suska
    Esoteritech, Inc.
    "Demystifying technology for your home or business"
  • On Jan 18, 2012, at 3:13 PM, Keary Suska wrote:

    > Any special handling of NSTask aside, Mac OS X uses Unix-based process control which closes all child processes when the parent is closed.

    No, that's not true.  Where did you get that?

    Processes with a controlling terminal get a SIGHUP when that terminal is closed, and that will kill a naive process, but that wouldn't apply to subprocesses of GUI apps.  Other than that, child processes are independent of their parent process.

    > Since your sub-program is Java you may be able to detach the child process.

    This doesn't make sense to me.  What does being Java have to do with anything?

    > This is usually accomplished by the sub-program by executing a low-level fork(). It may need to be followed by an exec() to fully detach the process.

    Fork() creates the child subprocess as a near duplicate of the parent.  Exec() replaces the process's image with a new one.  This is the standard means of creating a subprocess running a new program, but doesn't particularly "detach" an already-existing subprocess from its parent (whatever that might mean).

    > Note that you can't do this with Cocoa/Objective-C (at least Apple says you shouldn't…)

    You can fork() and exec() just fine.  What you can't do is fork(), _not_ call exec(), and then do anything other than call POSIX async-cancel-safe APIs.  That includes using high-level frameworks like Cocoa, Core Foundation, or the like.

    Regards,
    Ken
  • Thanks, I'll have a look.

    BTW, I was able to confirm it is a result of streams. My Java
    processes do not quit if I pipe their output to null:
     NSTask *task = [NSTask new];
     [task setLaunchPath:execPath];
     [task setCurrentDirectoryPath:_directory];
     [task setArguments:arguments];
     [task setStandardError:[NSFileHandle fileHandleWithNullDevice]];
     [task setStandardOutput:[NSFileHandle fileHandleWithNullDevice]];
     [task launch];

    So if I want to capture the output I can simply use a file handle to a
    real file and it looks like the NSTask process no longer quits.

    On Wed, Jan 18, 2012 at 12:55 PM, Scott Ribe
    <scott_ribe...> wrote:
    > On Jan 18, 2012, at 11:59 AM, Andrew wrote:
    >
    >> I can probably find out the answer by trying different things, but I'd
    >> like to get a better insight for what is going on and why the child
    >> task is terminating.
    >
    > You may want to try LSxxxx (Launch Services) routines.
    >
    > --
    > Scott Ribe
    > <scott_ribe...>
    > http://www.elevated-dev.com/
    > (303) 722-0567 voice
    >
    >
    >
    >
  • On Jan 18, 2012, at 2:27 PM, Ken Thomases wrote:

    >> Note that you can't do this with Cocoa/Objective-C (at least Apple says you shouldn't…)
    >
    > You can fork() and exec() just fine.  What you can't do is fork(), _not_ call exec(), and then do anything other than call POSIX async-cancel-safe APIs.  That includes using high-level frameworks like Cocoa, Core Foundation, or the like.

    I think it's recommended that you not launch a GUI app via fork/exec. Calling fork/exec *from* GUI apps is fine all day long.

    --
    Scott Ribe
    <scott_ribe...>
    http://www.elevated-dev.com/
    (303) 722-0567 voice
  • On Wed, Jan 18, 2012 at 12:38 PM, Jens Alfke <jens...> wrote:
    > Nope. In Unix, a process is killed when its parent process exits.

    No it's not.

    /tmp% cat processes.c
    #include <unistd.h>
    #include <stdio.h>

    int main(int argc, char **argv)
    {
        printf("Parent pid is %lu\n", (unsigned long)getpid());

        pid_t child = fork();
        if (child == -1) {
            fprintf(stderr, "Fork failed\n");
            return 1;
        } else if (child == 0) {
            sleep(10000);
            return 0;
        } else {
            printf("Child pid is %lu, parent exiting\n", (unsigned long)child);
            return 0;
        }
    }
    /tmp% clang -o processes processes.c
    /tmp% ./processes
    Parent pid is 47549
    Child pid is 47550, parent exiting
    /tmp% ps aux 47549
    USER  PID  %CPU %MEM      VSZ    RSS  TT  STAT STARTED      TIME COMMAND
    /tmp% ps aux 47550
    USER  PID  %CPU %MEM      VSZ    RSS  TT  STAT STARTED      TIME COMMAND
    kyle 47550  0.0  0.0  2434784    160 s000  S    9:34AM  0:00.00 ./processes

    --Kyle Sluder
  • On Jan 18, 2012, at 2:27 PM, Ken Thomases wrote:

    > On Jan 18, 2012, at 3:13 PM, Keary Suska wrote:
    >
    >> Any special handling of NSTask aside, Mac OS X uses Unix-based process control which closes all child processes when the parent is closed.
    >
    > No, that's not true.  Where did you get that?

    It has likely, as you point out, come from the entirely wrong orifice. I suspect I am confusing tis with system() and popen() calls, which I think have a dependency on the parent process' life, but I could be wrong about that too.

    > Processes with a controlling terminal get a SIGHUP when that terminal is closed, and that will kill a naive process, but that wouldn't apply to subprocesses of GUI apps.  Other than that, child processes are independent of their parent process.
    >
    >> Since your sub-program is Java you may be able to detach the child process.
    >
    > This doesn't make sense to me.  What does being Java have to do with anything?

    I meant as opposed to a Cocoa-based app. The Java runtime, AFAIK, does not have the same restrictions.

    >> This is usually accomplished by the sub-program by executing a low-level fork(). It may need to be followed by an exec() to fully detach the process.
    >
    > Fork() creates the child subprocess as a near duplicate of the parent.  Exec() replaces the process's image with a new one.  This is the standard means of creating a subprocess running a new program, but doesn't particularly "detach" an already-existing subprocess from its parent (whatever that might mean).

    Again, this might be my mistaking daemonizing pipe-to-subprocess, but I could be wrong about that too.

    >> Note that you can't do this with Cocoa/Objective-C (at least Apple says you shouldn't…)
    >
    > You can fork() and exec() just fine.  What you can't do is fork(), _not_ call exec(), and then do anything other than call POSIX async-cancel-safe APIs.  That includes using high-level frameworks like Cocoa, Core Foundation, or the like.

    This might also be where I recalled the necessity of fork-exec, but it has been some time since I wandered down that rabbit hole..

    Sorry for the noise...

    Keary Suska
    Esoteritech, Inc.
    "Demystifying technology for your home or business"
  • On Jan 18, 2012, at 1:38 PM, Jens Alfke wrote:

    >
    > Nope. In Unix, a process is killed when its parent process exits.

    Not true. It looks like the case at a cursory level because session management does this when you're in the terminal. There are various ways to arrange for a process to exit when its parent exits. But it is *not* the case that a process is automatically killed when its parent exits.

    --
    Scott Ribe
    <scott_ribe...>
    http://www.elevated-dev.com/
    (303) 722-0567 voice
  • On Jan 19, 2012, at 10:10 AM, Scott Ribe wrote:

    > Not true. It looks like the case at a cursory level because session management does this when you're in the terminal. There are various ways to arrange for a process to exit when its parent exits. But it is *not* the case that a process is automatically killed when its parent exits.

    Huh; you learn something new every day!

    In addition to shell behavior you mention, I also see this when Xcode crashes and takes down the app I’m debugging. But I suppose Xcode configures the process it launches to work that way?

    —Jens
  • On Jan 19, 2012, at 12:49 PM, Jens Alfke wrote:

    > On Jan 19, 2012, at 10:10 AM, Scott Ribe wrote:
    >
    >> Not true. It looks like the case at a cursory level because session management does this when you're in the terminal. There are various ways to arrange for a process to exit when its parent exits. But it is *not* the case that a process is automatically killed when its parent exits.
    >
    > Huh; you learn something new every day!
    >
    > In addition to shell behavior you mention, I also see this when Xcode crashes and takes down the app I’m debugging. But I suppose Xcode configures the process it launches to work that way?

    This is probably because Xcode is using a pseudo-terminal device as the subprocess's output, so it can capture it and show it in the console window, and thus the subprocess has a controlling terminal, which is closed when Xcode crashes, which delivers SIGHUP to the subprocess.  If a process hasn't specifically arranged otherwise, it will be killed by the SIGHUP.

    Regards,
    Ken
  • On Jan 19, 2012, at 11:49 AM, Jens Alfke wrote:

    > Huh; you learn something new every day!

    Yeah, specifically I learned this about 6 months ago ;-)

    > In addition to shell behavior you mention, I also see this when Xcode crashes and takes down the app I’m debugging. But I suppose Xcode configures the process it launches to work that way?

    Yeah, probably what Ken said ;-) In my case, I spent a lot of effort trying to figure out how to get a child process to *not* exit when the parent crashed, then suddenly it just started working that way ;-)

    --
    Scott Ribe
    <scott_ribe...>
    http://www.elevated-dev.com/
    (303) 722-0567 voice
previous month january 2012 next month
MTWTFSS
            1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31          
Go to today