mirror of
https://github.com/ioacademy-jikim/debugging
synced 2025-06-08 08:26:14 +00:00
134 lines
2.7 KiB
C
134 lines
2.7 KiB
C
/*
|
|
This test makes sure the thread exit notification signals don't
|
|
interfere with the stack growth signals.
|
|
|
|
Thread death notifications are sent as RT signals, which are
|
|
queued. In general, these notifications are ignored, since they're
|
|
only used by the main thread if it has exited and is still waiting
|
|
for the rest to exit.
|
|
|
|
The system has a finite limit to the number of RT signals which can
|
|
be queued (typically 1024), and beyond that it stops queueing
|
|
siginfo. We rely on getting SIGSEGVs with siginfo information to
|
|
grow the stack. If we don't get the siginfo, then it just looks
|
|
like the program crashed.
|
|
|
|
The extra complication in this test is making sure that the
|
|
unwanted signals are discarded while the main thread is blocked in
|
|
a syscall. So, to check this, main creates a new process, which
|
|
attempts to grow the stack once all the threads have been created
|
|
and exited. main() itself is blocked waiting for the child
|
|
process.
|
|
|
|
Oh, and this test also makes sure that thread resources are cleaned
|
|
up properly.
|
|
*/
|
|
#include <pthread.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
#include <signal.h>
|
|
#include <sys/wait.h>
|
|
|
|
static int grower;
|
|
|
|
static void handler(int sig)
|
|
{
|
|
}
|
|
|
|
static void *thr(void *v)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
#define FRAME 4096
|
|
|
|
static void grow(int depth)
|
|
{
|
|
volatile char frame[FRAME];
|
|
|
|
memset((char *)frame, 0xff, sizeof(frame));
|
|
|
|
if (depth > 1)
|
|
grow(depth-1);
|
|
}
|
|
|
|
static void *maker(void *v)
|
|
{
|
|
int i;
|
|
|
|
sleep(1);
|
|
|
|
/* Create lots of threads */
|
|
printf("creating threads...\n");
|
|
for(i = 0; i < 1300; i++) {
|
|
pthread_t t;
|
|
int ret;
|
|
|
|
if (i % 100 == 0)
|
|
printf("%d...\n", i);
|
|
|
|
ret = pthread_create(&t, NULL, thr, NULL);
|
|
if (ret) {
|
|
printf("pthread_create failed: %s\n", strerror(ret));
|
|
exit(1);
|
|
}
|
|
|
|
ret = pthread_join(t, NULL);
|
|
if (ret) {
|
|
printf("pthread_join failed: %s\n", strerror(ret));
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
kill(grower, SIGUSR1);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
int main()
|
|
{
|
|
pthread_t pth;
|
|
sigset_t mask;
|
|
int status;
|
|
struct sigaction sa;
|
|
|
|
sigemptyset(&mask);
|
|
sigaddset(&mask, SIGCHLD);
|
|
sigprocmask(SIG_BLOCK, &mask, NULL);
|
|
|
|
sa.sa_handler = handler;
|
|
sa.sa_flags = 0;
|
|
sigfillset(&sa.sa_mask);
|
|
sigaction(SIGUSR1, &sa, NULL);
|
|
|
|
grower = fork();
|
|
|
|
if (grower == -1) {
|
|
perror("fork");
|
|
exit(1);
|
|
}
|
|
|
|
if (grower == 0) {
|
|
pause(); /* child - wait for SIGUSR1 */
|
|
grow(10);
|
|
printf("stack grew OK\n");
|
|
exit(0);
|
|
}
|
|
|
|
pthread_create(&pth, NULL, maker, NULL);
|
|
|
|
/* wait for child */
|
|
if (waitpid(grower, &status, 0) != grower)
|
|
printf("FAILED\n");
|
|
else if (WIFEXITED(status) && WEXITSTATUS(status) == 0)
|
|
printf("PASS: child OK\n");
|
|
else
|
|
printf("FAILED: exit status=%d\n", status);
|
|
|
|
pthread_join(pth, NULL);
|
|
|
|
return 0;
|
|
}
|