Discussion:
pthread+exec question
Eyal Lebedinsky
2003-12-02 19:46:34 UTC
Permalink
I am trying to undersnatd the interaction between threads and
exec(). The attached program demonstrates how the exec() behaves
differently when issued from inside a thread (./p) or directly
from main (./p x). In the former case the shell proceeds when
the exec is issued rather than wait for the completion of the
program.

The problem is that the exec'ed program takes over the pid of
the thread rather than of main(), and the original process
exits, letting my shell proceed rather than wait.

How do I prevent this? I have a need to exec() from inside a
thread and not release a wait()ing parent at that point.

I tried playing with pthread_attr_setscope() but got incorrect
results: non-zero return code and no perror. Just change to
PTHREAD_SCOPE_PROCESS
and see. Anyway, it probably is unrelated.

I looked into process groups and got no solution either.

--
Eyal Lebedinsky (***@eyal.emu.id.au) <http://samba.org/eyal/>
-------------- next part --------------
#include <stdio.h>
#include <sys/types.h> /* pid_t */
#include <unistd.h>
#include <pthread.h>

static pid_t main_pid;
static pid_t main_pgrp;
static pid_t thread_pid;
static pid_t thread_pgrp;

void
check (char *func, int rc)
{
if (rc) {
fprintf (stderr, "%s rc=%d", func, rc);
perror (" ");
exit (1);
}
}

void *
thread (void *parm)
{
thread_pid = getpid ();
thread_pgrp = getpgrp ();
fprintf (stderr,
"thread pid is %ld pgrp is %ld\n",
(long)thread_pid,
(long)thread_pgrp);

execl ("/bin/sh", "sh", "-c", "(sleep 1 ; echo hi from $$)", NULL);
/* not reached */

return (NULL);
}

int
main (int argc, char **argv, char **envp)
{
pthread_attr_t attr;
pthread_t th;
void *rc;

main_pid = getpid ();
main_pgrp = getpgrp ();
fprintf (stderr,
"main pid is %ld pgrp is %ld\n",
(long)main_pid,
(long)main_pgrp);

if (argc > 1)
rc = thread (NULL);
else {
check ("pthread_attr_init",
pthread_attr_init (&attr));
check ("pthread_attr_setscope",
pthread_attr_setscope (&attr,
PTHREAD_SCOPE_SYSTEM));
check ("pthread_attr_setdetachstate",
pthread_attr_setdetachstate (&attr,
PTHREAD_CREATE_JOINABLE));
check ("pthread_create",
pthread_create (&th, /* thread id */
&attr, /* pthread_attr */
thread, /* start routine */
NULL)); /* start routine arg */
check ("pthread_join",
pthread_join (th, &rc));
}
/* not reached */

return (0);
}
Andreas Bauer
2003-12-02 19:46:35 UTC
Permalink
Post by Eyal Lebedinsky
I am trying to undersnatd the interaction between threads and
exec(). The attached program demonstrates how the exec() behaves
differently when issued from inside a thread (./p) or directly
from main (./p x). In the former case the shell proceeds when
the exec is issued rather than wait for the completion of the
program.
The problem is that the exec'ed program takes over the pid of
the thread rather than of main(), and the original process
exits, letting my shell proceed rather than wait.
If I recall correctly, that's a limitation of pthreads under Linux. A
process that opens a thread can only wait for this particular thread, not
for any further opened threads:

Thread 1 -(opens)-> Thread 2 -(opens)-> Thread 3

Thread 1 (your program here) can not see or wait for Thread 3. So after
you've opened up Thread 2 and send it to the background, Thread 1
finished all the work and you return to shell. Thread 2 is the only
process that may wait for Thread 3 to terminate. Remember in Linux a
process and a thread are the same thing really.
Post by Eyal Lebedinsky
How do I prevent this? I have a need to exec() from inside a
thread and not release a wait()ing parent at that point.
By using a different concept for your program. It's been a while since I
used pthreads, but that's all I can think of right now. (Someone correct
me please, in case.)
Post by Eyal Lebedinsky
I looked into process groups and got no solution either.
See if you can buy or borrow Butenhof's "Programming with POSIX Threads".
It's a nice introduction to pthreads.


Hope this helped a bit, though I am no pthreads expert :-)

Andi
--
Andreas Bauer, baueran at in.tum.de, http://home.in.tum.de/baueran/
"Should've bought a Holden, Frank!" -- Ozzie in Bad Taste
Eyal Lebedinsky
2003-12-02 19:46:35 UTC
Permalink
Post by Andreas Bauer
Post by Eyal Lebedinsky
I am trying to undersnatd the interaction between threads and
exec(). The attached program demonstrates how the exec() behaves
differently when issued from inside a thread (./p) or directly
from main (./p x). In the former case the shell proceeds when
the exec is issued rather than wait for the completion of the
program.
The problem is that the exec'ed program takes over the pid of
the thread rather than of main(), and the original process
exits, letting my shell proceed rather than wait.
If I recall correctly, that's a limitation of pthreads under Linux.
Not really just Linux. I had the pleasure under HP/UX too. I think it
is more of a basic issue. POSIX seems to say that the thread issuing
the exec() is the one that continues, but POSIX says nothing about
the relationship between processes and threads re pid. So doing the
exec() inside a thread may terminate the original pid (as it does
here) or may not (if all threads share a pid and are really
lightweight).

I think Solaris has the problem too, but I do need to recheck.
Post by Andreas Bauer
See if you can buy or borrow Butenhof's "Programming with POSIX Threads".
It's a nice introduction to pthreads.
Got it. Read it.

--
Eyal Lebedinsky (***@eyal.emu.id.au) <http://samba.org/eyal/>
Eyal Lebedinsky
2003-12-02 19:46:35 UTC
Permalink
Eyal> Not really just Linux. I had the pleasure under HP/UX too. I think it
Eyal> is more of a basic issue. POSIX seems to say that the thread issuing
Eyal> the exec() is the one that continues, but POSIX says nothing about
Eyal> the relationship between processes and threads re pid. So doing the
Eyal> exec() inside a thread may terminate the original pid (as it does
Eyal> here) or may not (if all threads share a pid and are really
Eyal> lightweight).
None of this was making any sense to me, based on my understanding of
exec() in mt programs, so I double checked my reference (``Threads
``The call exec() does exactly what it did before -- it deletes the
entire memory mapping for the calling process and replaces it with
the one appropriate for the new program. A program with multiple
threads that calls exec() will have all of those threads (and any
LWPs) terminated, and the only thing left will be the new
program.''
Sure, this is the general way exec() should go, but the question is:
What pid will the new program have (we may have had many pids
before)? Will the program that launched us (e.g. our terminal shell)
drop from their wait() at this point?

--
Eyal Lebedinsky (***@eyal.emu.id.au) <http://samba.org/eyal/>
Damien Elmes
2003-12-02 19:46:35 UTC
Permalink
Post by Eyal Lebedinsky
``The call exec() does exactly what it did before -- it deletes the
entire memory mapping for the calling process and replaces it with
the one appropriate for the new program. A program with multiple
threads that calls exec() will have all of those threads (and any
LWPs) terminated, and the only thing left will be the new
program.''
What pid will the new program have (we may have had many pids
before)? Will the program that launched us (e.g. our terminal shell)
drop from their wait() at this point?
[disclaimer.. i've done very little with threading]

if you want to ensure that you keep the main pid of the process (and thus avoid
falling through), the simplest way i can see of doing this is to refactor your
program to check for a flag that's set when the child thread ends, and roll the
exec behavior into the main one. this may not be a very elegant way to go about
it, but threads and process control (such as signals) have never been
particularly elegant.
--
Damien Elmes
***@repose.cx
Eyal Lebedinsky
2003-12-02 19:46:35 UTC
Permalink
Post by Damien Elmes
if you want to ensure that you keep the main pid of the process (and thus avoid
falling through), the simplest way i can see of doing this is to refactor your
program to check for a flag that's set when the child thread ends, and roll the
exec behavior into the main one. this may not be a very elegant way to go about
it, but threads and process control (such as signals) have never been
particularly elegant.
Yes, this was the clear solution from the start, but I thought I could
avoid
it by doing something right. No such luck.

I am now convinced that this "hack" is actually the only way to do it. I
am lucky in that:
- I am in control of all the threads (including main)
- my need is to exec from a first generation thread
So I can (actually, I did this and it works) do it. If any of the above
was not the case then I would be in real trouble.

My approach was to write an exec_delay() function that saves the <path,
argv,
envp> in a safe place and then does a pthread_exit(), which falls into
the
pthread_join() which notices the saved data and executes the exec()
then.
The original exec() was already in a wrapper so the implementation was
straight forward. One benefit is that now the main program does have an
opportunity to cleanup/shutdown before doing the exec(), and the very
final
exit() is what the exec() should replace.

If nothing else, I now know more about fork/exec/wait interactions with
POSIX threads that I ever wanted to know.

Thanks everyone.

--
Eyal Lebedinsky (***@eyal.emu.id.au) <http://samba.org/eyal/>

Loading...