#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
#include "safe_printf.h"

/*
 * This program demonstrates that calling 'printf' (or puts, for that
 * matter, which is what gcc quietly changes the call to) is not safe
 * from within a signal handler that's invoked asynchronously (like
 * SIGCHLD's is).
 * 
 * Start the program, and you'll see it "get stuck"
 * after a while.  Attach gdb to it (gdb ./signaldeadlock)
 * "attach <pid>" in gdb, where pid is the process id.
 *
 * @author Godmar Back <godmar@gmail.com>
 * Written for CS 3214 Fall 2009, Virginia Tech
 * 
 * Updated by Dave O'Hallaron with the safe_printf() function
 * Fall, 2010
 */
#define DEADLOCK 1

#define MAXN 128

static void
catch_child(int signo)
{
#if DEADLOCK
    /* this call may reenter printf/puts! Bad! */
    printf("Child exited!\n");
#else
    /* This version is async-signal-safe */
    safe_printf("Child exitted!\n");
#endif

    /* reap all children */
    while (waitpid(-1, NULL, WNOHANG) > 0)
        continue;
}

int
main()
{
    signal(SIGCHLD, catch_child);

    int i;
    for (i = 0; i < 1000000; i++) {
        if (fork() == 0)
            exit(0);

        /* the child is running, it may exit
         * any time.... SIGCHLD may arrive
         * while in printf/puts
         * (this outputs character by character
         * to increase the chance for the deadlock
         * to occur so we don't have to wait long.)
         */
        char *p;
        for (p = "Child started\n"; *p; p++) {
#if DEADLOCK
            printf("%c", *p);
#else
            safe_printf("%c", *p);
#endif
        }
    }
    return 0;
}
