/*
 *  semaphore.c
 *  
 *
 *  Created by Mij <mij@bitchx.it> on 12/03/05.
 *  Original source file available at http://mij.oltrelinux.com/devel/unixprg/
 *
 */



#define _POSIX_SOURCE
#include <stdio.h>
/* sleep() */
#include <errno.h>
#include <unistd.h>
/* abort() and random stuff */
#include <stdlib.h>
/* time() */
#include <time.h>
#include <signal.h>
#include <pthread.h>
#include <semaphore.h>


/* this skips program termination when receiving signals */
void signal_handler(int type);

/*
 * thread A is synchronous. When it needs to enter its
 * critical section, it can't do anything other than waiting.
 */

void *thread_a(void *);

/*
 * thread B is asynchronous. When it tries to enter its
 * critical section, it switches back to other tasks if
 * it hasn't this availability.
 */

void *thread_b(void *);


/* the semaphore */
sem_t mysem;

int main(int argc, char *argv[])
{
    pthread_t mytr_a, mytr_b;
    int ret;
    
    
    srand(time(NULL));
    signal(SIGHUP, signal_handler);
    signal(SIGUSR1, signal_handler);

    /*
     * creating the unnamed, local semaphore, and initialize it with
     * value 1 (max concurrency 1)
     */

    ret = sem_init(&mysem, 0, 1);
    if (ret != 0) {
        /* error. errno has been set */
        perror("Unable to initialize the semaphore");
        abort();
    }
    
    /* creating the first thread (A) */
    ret = pthread_create(&mytr_a, NULL, thread_a, NULL);
    if (ret != 0) {
        perror("Unable to create thread");
        abort();
    }

    /* creating the second thread (B) */
    ret = pthread_create(&mytr_b, NULL, thread_b, NULL);
    if (ret != 0) {
        perror("Unable to create thread");
        abort();
    }
    
    /* waiting for thread_a to finish */
    ret = pthread_join(mytr_a, (void *)NULL);
    if (ret != 0) {
        perror("Error in pthread_join");
        abort();
    }

    /* waiting for thread_b to finish */
    ret = pthread_join(mytr_b, NULL);
    if (ret != 0) {
        perror("Error in pthread_join");
        abort();
    }

    return 0;
}


void *thread_a(void *x)
{
    unsigned int i, num;
    int ret;
    
    
    printf(" -- thread A -- starting\n");
    num = ((unsigned int)rand() % 40);
    
    /* this does (do_normal_stuff, do_critical_stuff) n times */
    for (i = 0; i < num; i++) {
        /* do normal stuff */
        sleep(3 + (rand() % 5));
        
        /* need to enter critical section */
        printf(" -- thread A -- waiting to enter critical section\n");
        /* looping until the lock is acquired */
        do {
            ret = sem_wait(&mysem);
            if (ret != 0) {
                /* the lock wasn't acquired */
                if (errno != EINVAL) {
                    perror(" -- thread A -- Error in sem_wait. terminating -> ");
                    pthread_exit(NULL);
                } else {
                    /* sem_wait() has been interrupted by a signal: looping again */
                    printf(" -- thread A -- sem_wait interrupted. Trying again for the lock...\n");
                }
            }
        } while (ret != 0);
        printf(" -- thread A -- lock acquired. Enter critical section\n");
        
        /* CRITICAL SECTION */
        sleep(rand() % 2);
        
        /* done, now unlocking the semaphore */
        printf(" -- thread A -- leaving critical section\n");
        ret = sem_post(&mysem);
        if (ret != 0) {
            perror(" -- thread A -- Error in sem_post");
            pthread_exit(NULL);
        }
    }
    
    printf(" -- thread A -- closing up\n");

    pthread_exit(NULL);
}


void *thread_b(void *x)
{
    unsigned int i, num;
    int ret;
    
    
    printf(" -- thread B -- starting\n");
    num = ((unsigned int)rand() % 100);
    
    /* this does (do_normal_stuff, do_critical_stuff) n times */
    for (i = 0; i < num; i++) {
        /* do normal stuff */
        sleep(3 + (rand() % 5));
        
        /* wants to enter the critical section */
        ret = sem_trywait(&mysem);
        if (ret != 0) {
            /* either an error happened, or the semaphore is locked */
            if (errno != EAGAIN) {
                /* an event different from "the semaphore was locked" happened */
                perror(" -- thread B -- error in sem_trywait. terminating -> ");
                pthread_exit(NULL);
            }
            
            printf(" -- thread B -- cannot enter critical section: semaphore locked\n");
        } else {
            /* CRITICAL SECTION */
            printf(" -- thread B -- enter critical section\n");

            sleep(rand() % 10);
            
            /* done, now unlocking the semaphore */
            printf(" -- thread B -- leaving critical section\n");
            sem_post(&mysem);
        }
    }
    
    printf(" -- thread B -- closing up\n");
    
    /* joining main() */
    pthread_exit(NULL);
}


void signal_handler(int type)
{
    /* do nothing */
    printf("process got signal %d\n", type);
    return;
}