#include<stdio.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<unistd.h>
#include<pthread.h>
#define N 4

struct thread_info {
   int pipe_fd[2];
   int* row;
   int thread_id;
};

void* row_sum(void*);

int main(int argc, char** argv)
{
   int   a[N][N] = {{1,1,4,1}, {2,2,1,2}, {1,3,4,1}, {4,1,2,1}};
   int   i;
   int   ret;
   int   sum = 0;
   int   **pd;
   void  *status;
   int   partial_sum[N];
   int   ok = 1;

   struct thread_info* info;
   pthread_t active_threads[N];

   *pd = (int*)malloc(N*sizeof(int));
   for (i=0; i<N; i++) {
      pd[i] = (int*)malloc(2*sizeof(int));

      if (pipe(pd[i]) == -1) perror("pipe() call failed");

      info = malloc (sizeof(struct thread_info));
      info->pipe_fd[0] = dup(pd[i][0]);
      info->pipe_fd[1] = dup(pd[i][1]);
      info->row = a[i];

      printf("Creata pipe %d. Canale di lettura = %d. Canale di scrittura = %d.\n", i, info->pipe_fd[0], info->pipe_fd[1]);
      info->thread_id = i;

      printf("Creo thread per la riga %d.\n",i);
      pthread_create(&active_threads[i], NULL, row_sum, (void *)info);
      close(pd[i][1]);
   }

   printf("Attesa fine dei thread e lettura somme parziali....");
   fflush(stdout);
	for (i=0; i<N; i++) {
      pthread_join(active_threads[i], &status);
      if (*((int*)status) == 0) {
         while (1) {
            if ((ret = read(pd[i][0], &partial_sum[i], sizeof(int))) == -1) {
               printf("Errore per il thread %d. Read failed",i);
               ok = 0;
            }
            if (!ret) break;
         }
      }
      close(pd[i][0]);
   }
   printf("fatto.\n");

   for (i=0; i<N; i++) {
      printf("Somma parziale thread %d = %d.\n",i,partial_sum[i]);
      sum += partial_sum[i];
   }

   printf("Somma");
   if (!ok) printf(" parziale (possibili errori)");
   printf(" = %d\n", sum);
   exit(0);
}

void* row_sum(void* arg) {
   struct thread_info *this_info;
   int i, partial_sum=0;
   int status = 0;

   this_info = (struct thread_info *)arg;
   printf("Thread %d: letti i dati di input.\n",this_info->thread_id);

   close(this_info->pipe_fd[0]);
   for (i=0; i<N; i++) {
      partial_sum += this_info->row[i];
      printf("Somma parziale thread %d: %d\n",this_info->thread_id,partial_sum);
   }
   printf("Thread %d: scrittura nella pipe....");
   if (write(this_info->pipe_fd[1], &partial_sum, sizeof(int))==-1) {
      printf("fallita errore durante la write.\n", this_info->thread_id);
      status = -1;
   } else printf("riuscita.\n");
   close(this_info->pipe_fd[1]);
   pthread_exit((void*)&status);
}

