libthread: copy exec args into malloc'ed memory
On Solaris, the child of fork does not have access to the stacks
of other threads that existed at the time of the fork, as demonstrated
by the C program below (fails on Solaris, works everywhere else).
Work around this by making a copy of everything the child needs
in malloc'ed memory, which is inherited properly.
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>
int *p;
pthread_mutex_t mu = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t ready = PTHREAD_COND_INITIALIZER;
pthread_cond_t never = PTHREAD_COND_INITIALIZER;
void*
thread1(void *v)
{
int local = 42;
pthread_mutex_lock(&mu);
p = &local;
pthread_cond_signal(&ready);
pthread_cond_wait(&never, &mu);
return 0;
}
void*
thread2(void *v)
{
pthread_mutex_lock(&mu);
while(p == 0)
pthread_cond_wait(&ready, &mu);
pthread_mutex_unlock(&mu);
printf("parent: *p = %d\n", *p);
int pid = fork();
if (pid == 0) {
printf("child: *p = %d\n", *p);
exit(0);
}
printf("parent: pid = %d\n", pid);
int status;
waitpid(pid, &status, 0);
if(WIFEXITED(status))
printf("child exit %d\n", WEXITSTATUS(status));
else if(WIFSIGNALED(status))
printf("child signal %d\n", WTERMSIG(status));
else
printf("unexpected child status %d\n", status);
return 0;
}
int
main(void)
{
pthread_t t1, t2;
pthread_create(&t1, 0, thread1, 0);
pthread_create(&t2, 0, thread2, 0);
void *v;
pthread_join(t2, &v);
return 0;
}
This commit is contained in:
@@ -27,17 +27,45 @@ execproc(void *v)
|
|||||||
int
|
int
|
||||||
_runthreadspawn(int *fd, char *cmd, char **argv, char *dir)
|
_runthreadspawn(int *fd, char *cmd, char **argv, char *dir)
|
||||||
{
|
{
|
||||||
int pid;
|
int i, pid, argc;
|
||||||
Execjob e;
|
Execjob *e;
|
||||||
|
|
||||||
e.fd = fd;
|
// Copy all args into malloc'ed memory, in case the arguments
|
||||||
e.cmd = cmd;
|
// point into a thread stack. On Solaris, the stacks of other
|
||||||
e.argv = argv;
|
// threads are not inherited by the forked child.
|
||||||
e.dir = dir;
|
e = mallocz(sizeof *e, 1);
|
||||||
e.c = chancreate(sizeof(void*), 0);
|
memmove(e->fd, fd, 3*sizeof fd[0]);
|
||||||
proccreate(execproc, &e, 65536);
|
e->cmd = strdup(cmd);
|
||||||
pid = recvul(e.c);
|
if(e->cmd == nil)
|
||||||
chanfree(e.c);
|
sysfatal("out of memory");
|
||||||
|
argc = 0;
|
||||||
|
while(argv[argc] != nil)
|
||||||
|
argc++;
|
||||||
|
e->argv = mallocz((argc+1)*sizeof argv[0], 1);
|
||||||
|
if(e->argv == nil)
|
||||||
|
sysfatal("out of memory");
|
||||||
|
for(i=0; i<argc; i++) {
|
||||||
|
e->argv[i] = strdup(argv[i]);
|
||||||
|
if(e->argv[i] == nil)
|
||||||
|
sysfatal("out of memory");
|
||||||
|
}
|
||||||
|
if(e->dir != nil) {
|
||||||
|
e->dir = strdup(dir);
|
||||||
|
if(e->dir == nil)
|
||||||
|
sysfatal("out of memory");
|
||||||
|
}
|
||||||
|
e->c = chancreate(sizeof(void*), 0);
|
||||||
|
|
||||||
|
proccreate(execproc, e, 65536);
|
||||||
|
pid = recvul(e->c);
|
||||||
|
|
||||||
|
free(e->cmd);
|
||||||
|
for(i=0; i<argc; i++)
|
||||||
|
free(e->argv[i]);
|
||||||
|
free(e->argv);
|
||||||
|
free(e->dir);
|
||||||
|
chanfree(e->c);
|
||||||
|
free(e);
|
||||||
return pid;
|
return pid;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -78,7 +106,7 @@ _threadspawn(int fd[3], char *cmd, char *argv[], char *dir)
|
|||||||
return -1;
|
return -1;
|
||||||
case 0:
|
case 0:
|
||||||
/* can't RFNOTEG - will lose tty */
|
/* can't RFNOTEG - will lose tty */
|
||||||
if(dir != nil )
|
if(dir != nil)
|
||||||
chdir(dir); /* best effort */
|
chdir(dir); /* best effort */
|
||||||
dup2(fd[0], 0);
|
dup2(fd[0], 0);
|
||||||
dup2(fd[1], 1);
|
dup2(fd[1], 1);
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ enum
|
|||||||
|
|
||||||
struct Execjob
|
struct Execjob
|
||||||
{
|
{
|
||||||
int *fd;
|
int fd[3];
|
||||||
char *cmd;
|
char *cmd;
|
||||||
char **argv;
|
char **argv;
|
||||||
char *dir;
|
char *dir;
|
||||||
|
|||||||
Reference in New Issue
Block a user