/* Copyright (C) 2009 by Mattia Monga <mattia.monga@unimi.it> */
/* $Id$ */

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <sched.h>
#include <semaphore.h>

void enter_section(sem_t *s){ 
        if (sem_wait(s) < 0){
                perror("Errore semaforo (down)");
                exit(1);
        }
}

        

void leave_section(sem_t *s)
{
        if (sem_post(s) < 0){
                perror("Errore semaforo (up)");
                exit(1);
        }
}

int run(const int p, void* s)
{
        int* shared = (int*)s; /* alias per comodita` */
        while (enter_section((sem_t*)shared[1]), 
               shared[0] < 10) {
                sleep(1);
                printf("Processo figlio (%d). s = %d\n", 
                       getpid(), shared[0]);
                if (!(shared[0] < 10)){
                        printf("Corsa critica!!!!\n");
                        abort();
                }
                shared[0] += 1;
                leave_section((sem_t*)shared[1]);
                sched_yield();
        }
        return 0;
}

int run0(void*s){ return run(0, s); }
int run1(void*s){ return run(1, s); }
        

int main(void){
 
        int shared[4] = {0 , 0, 0, 0};

        sem_t *ss;
        if (sem_init(ss, 
                     0 /* thread local semaphore */,
                     0 /* init value */
                    ) < 0){
                perror("Errore semaforo");
                exit(1);
        }
        shared[1] = (int)ss;

        /* int clone(int (*fn)(void *), 
         *           void *child_stack, 
         *           int flags,
         *           void *arg); 
         * crea una copia del chiamante (con le caratteristiche 
         * specificate da flags) e lo esegue partendo da fn */
        if (clone(run0, /* il nuovo
                         * processo esegue run(shared), vedi quarto
                         * parametro */
                  malloc(4096)+4096,  /* lo stack del nuovo processo 
                                       *  (cresce verso il basso!) */
                  CLONE_VM | SIGCHLD, /* la (virtual) memory e` condivisa */
                  shared) < 0){
                perror("Errore nella creazione");
                exit(1);
        } 

        if (clone(run1, malloc(4096)+4096,  CLONE_VM | SIGCHLD, shared) < 0){
                perror("Errore nella creazione");
                exit(1);
        } 

        /* Memoria condivisa: i due figli nell'insieme eseguono 10 o
         * 11 volte: e` possibile una corsa critica. Il padre
         * condivide shared[0] con i figli */

        while(shared[0] < 10) {
                sleep(1);
                printf("Processo padre. s = %d %d %d %d\n", 
                       shared[0],
                       shared[1],
                       shared[2],
                       shared[3]);
        }
        sem_destroy(ss);
        return 0;
}

    
        
  



/* Local Variables: */
/* compile-command: "make -k " */
/* End: */
