|
/*
|
|
Solaris: (Oracle SolarisStudio 12.x)
|
|
% cc -mt -g -m64 -o test-solaris-fork-deadlock test-solaris-fork-deadlock.c \
|
|
-ldl -lpthread
|
|
|
|
Linux: (-D_GNU_SOURCE is needed for RTLD_DEFAULT)
|
|
% gcc -g -D_GNU_SOURCE -o test-solaris-fork-deadlock.linux \
|
|
test-solaris-fork-deadlock.c -ldl -pthread
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
#include <dlfcn.h>
|
|
#include <pthread.h>
|
|
#include <sys/types.h>
|
|
#include <sys/wait.h>
|
|
|
|
/* workaround for Linux */
|
|
#if !defined(RTLD_PROBE)
|
|
#define RTLD_PROBE RTLD_DEFAULT
|
|
#endif
|
|
|
|
struct data_for_loop_dlsym {
|
|
const char *name;
|
|
volatile int stop;
|
|
void *ptr;
|
|
};
|
|
|
|
static void*
|
|
loop_dlsym(void *data)
|
|
{
|
|
struct data_for_loop_dlsym *s = data;
|
|
void *ptr = NULL;
|
|
|
|
while (!(s->stop)) {
|
|
ptr = s->ptr = dlsym(RTLD_PROBE, s->name);
|
|
}
|
|
|
|
return ptr;
|
|
}
|
|
|
|
void do_fork_or_vfork(int flag_v)
|
|
{
|
|
pid_t pid;
|
|
extern char **environ;
|
|
|
|
if (flag_v) {
|
|
fprintf(stderr, "calling vfork()\n");
|
|
pid = vfork();
|
|
} else {
|
|
fprintf(stderr, "calling fork()\n");
|
|
pid = fork();
|
|
}
|
|
if (pid < 0) {
|
|
fprintf(stderr, "Error: v?fork returns %ld\n", (long)pid);
|
|
exit(3);
|
|
} else if (pid == 0) {
|
|
/* child process */
|
|
execle("/bin/echo", "echo", "child echo output",
|
|
NULL, environ);
|
|
/* error of execle */
|
|
_exit(4);
|
|
} else {
|
|
/* this process */
|
|
pid_t ret;
|
|
int stat;
|
|
|
|
fprintf(stderr, "waiting for child pid %ld\n", (long)pid);
|
|
ret = waitpid(pid, &stat, 0);
|
|
fprintf(stderr, "waitpid returns %ld with status %d\n", (long)ret, stat);
|
|
}
|
|
return;
|
|
}
|
|
|
|
void usage(const char *argv0)
|
|
{
|
|
printf("Usage: %s SYMBOL [fork|vfork]\n", argv0);
|
|
printf("Example: %s printf\n", argv0);
|
|
printf("Example: %s malloc fork\n", argv0);
|
|
printf("Example: %s _ex_unwind vfork\n", argv0);
|
|
return;
|
|
}
|
|
|
|
int main(int argc, char *argv[])
|
|
{
|
|
pthread_t tid;
|
|
int ret;
|
|
struct data_for_loop_dlsym d;
|
|
void *pth_status;
|
|
int flag_v = 0;
|
|
|
|
if (argc <= 1 || argc > 3) {
|
|
usage(argv[0]);
|
|
exit(0);
|
|
}
|
|
|
|
d.stop = 0;
|
|
d.name = argv[1];
|
|
d.ptr = (void *)main;
|
|
|
|
if (argv[2] != NULL) {
|
|
if (strcmp(argv[2], "fork") == 0) {
|
|
flag_v = 0;
|
|
} else if (strcmp(argv[2], "vfork") == 0) {
|
|
flag_v = 1;
|
|
} else {
|
|
fprintf(stderr, "Error: 3rd arg must be fork or vfork (default fork)\n");
|
|
usage(argv[0]);
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
fprintf(stderr, "this process: getpid() => %ld\n", (long)getpid());
|
|
|
|
ret = pthread_create(&tid, NULL, loop_dlsym, &d);
|
|
if (ret != 0) {
|
|
fprintf(stderr, "Error: pthread_create returns %d\n", ret);
|
|
exit(2);
|
|
}
|
|
|
|
while (d.ptr == (void *)main) {
|
|
fprintf(stderr, "sleep(1)\n");
|
|
sleep(1);
|
|
fprintf(stderr, "wake up\n");
|
|
}
|
|
fprintf(stderr, "dlsym returns %p\n", d.ptr);
|
|
|
|
do_fork_or_vfork(flag_v);
|
|
|
|
d.stop = 1;
|
|
|
|
ret = pthread_join(tid, &pth_status);
|
|
fprintf(stderr, "pthread_join returns %d with status %p\n", ret, pth_status);
|
|
|
|
fprintf(stderr, "end normally\n");
|
|
return 0;
|
|
}
|