/*****************************************************************************
 * FILE: bug6fix.c
 * DESCRIPTION:
 *   This solution uses a mutex variable to protect the global sum while each
 *   thread updates it. A much more efficient method would be that used in the
 *   dotprod_mutex.c example.
 * SOURCE: 07/06/05 Blaise Barney
 * LAST REVISED: 01/29/09 Blaise Barney
 ******************************************************************************/
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>

/* Define global data where everyone can see them */
#define NUMTHRDS 3
#define VECLEN 4
pthread_mutex_t mutexsum;
int *a, *b;
long sum=0.0;

void *dotprod(void *arg)
{
    /* Each thread works on a different set of data.
     * The offset is specified by the arg parameter. The size of
     * the data for each thread is indicated by VECLEN.
     */
    int i, start, end, offset, len;
    long tid;
    tid = (long)arg;
    offset = tid;
    len = VECLEN;
    start = offset*len;
    end   = start + len;
    
    /* Perform my section of the dot product */
    printf("thread: %ld starting. start=%d end=%d\n",tid,start,end-1);
    for (i=start; i<end ; i++) {
        pthread_mutex_lock(&mutexsum);
        sum += (a[i] * b[i]);
        pthread_mutex_unlock(&mutexsum);
    }
    printf("thread: %ld done. Global sum now is=%li\n",tid,sum);
    
    pthread_exit((void*) 0);
}

int main (int argc, char *argv[])
{
    long i;
    void *status;
    pthread_t threads[NUMTHRDS];
    pthread_attr_t attr;
    
    /* Assign storage and initialize values */
    a = (int*) malloc (NUMTHRDS*VECLEN*sizeof(int));
    b = (int*) malloc (NUMTHRDS*VECLEN*sizeof(int));
    
    for (i=0; i<VECLEN*NUMTHRDS; i++)
        a[i]=b[i]=1;
    
    /* Initialize mutex variable */
    pthread_mutex_init(&mutexsum, NULL);
    
    /* Create threads as joinable, each of which will execute the dot product
     * routine. Their offset into the global vectors is specified by passing
     * the "i" argument in pthread_create().
     */
    pthread_attr_init(&attr);
    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
    for(i=0;i<NUMTHRDS;i++)
        pthread_create(&threads[i], &attr, dotprod, (void *)i);
    
    pthread_attr_destroy(&attr);
    
    /* Wait on the other threads for final result */
    
    for(i=0;i<NUMTHRDS;i++) {
        pthread_join(threads[i], &status);
    }
    /* After joining, print out the results and cleanup */
    printf ("Final Global Sum=%li\n",sum);
    free (a);
    free (b);
    pthread_mutex_destroy(&mutexsum);
    pthread_exit(NULL);
}
