/* 
 * 
 */

#include "main.h"
#include <pthread.h>
#include <sched.h>

/*
 * Producer.
 * 
 * Birth function for thread that performs the producer behavior
 * 
 * Note: is pinned to a core, to facilitate collecting measurements
 */
void* 
producer_birthFn( void* _params )
 {
   cpu_set_t cpuinfo;
   int lastTupleIter, oldConsumerReceivedACKNum;
    
   ProducerParams *params = (ProducerParams *)_params;
    
   lastTupleIter = 0; //compared to global tupleIter while waiting
   oldConsumerReceivedACKNum = 0; //used when waiting for consumer to receive
   
   /* --------------------------------------------------
    * Pin thread to core, the producers are divided
    * equally over all cores. Pinning prohibits the
    * switching of cores so that perf counter and TSC values remain
    * from the same core between readings.  Pinning shouldn't 
    * affect results.. may be odd case when num thds doesn't divide into
    * num Cores
    * --------------------------------------------------
    */
 /*
   CPU_ZERO( &cpuinfo );
   CPU_SET( params->coreID, &cpuinfo );
   pthread_setaffinity_np( pthread_self(), sizeof(cpuinfo), &cpuinfo );
   pthread_yield(); //get off the core, so next can be created on it
   uint32_t cpuid = sched_getcpu();
  */
   
       
   /*Protocol:
    * wait for change in tupleIter (save updated tuple num for next time)
    * Get producer lock (only one producer at a time)
    * write into comm vars
    * get current ACK number
    * notify consumer
    * wait for ACK (get ACK lock, check on change in ACK number)
    * release producer lock
    * if not done, repeat
    */
   while( lastTupleIter < params->numTuplesToCreate )
    {
      //wait for change in tupleNum (save updated tuple num for next time)
      pthread_mutex_lock(     &tupleIterLock  );
      while( lastTupleIter == tupleIter )
       {
         pthread_cond_wait( &tupleIterCond,
                            &tupleIterLock );
       }
      pthread_mutex_unlock(   &tupleIterLock  );
      
      lastTupleIter = tupleIter; //save for next time through loop
      
      DEBUG__printf2("Producer: %d starting tuple: %d\n", params->producerID, tupleIter);
      
      //Two vars used to comm with consumer.  One holds message to send,
      // other holds ID of producer sending.
      //Protect the two variables with a lock, that only one
      // producer can get.  Update the variable with the message to be 
      // communicated, and write ID of sender in second var.

      //Get producer lock
      pthread_mutex_lock( &producerAccessMutex );
      
      // write into comm vars
      producerMessage = tupleIter;  //just a dummy -- overhead meas, do nothing
      currProductionNum += 1;
      
      // get current ACK number
      oldConsumerReceivedACKNum = currConsumerReceivedACKNum;

      // notify consumer (don't think need the cond lock here -- teeter-totter)
      pthread_mutex_lock(     &productionReadyLock  );
      DEBUG__printf1("producer %d wrote msg, about to wake up consumer\n", params->producerID );
      pthread_cond_broadcast( &productionReadyCond );
      pthread_mutex_unlock(   &productionReadyLock  );

      // wait for ACK (get ACK lock, check on change in ACK number)
      pthread_mutex_lock(     &consumerReceivedAckLock  );
      while( currConsumerReceivedACKNum == oldConsumerReceivedACKNum )
       {
         pthread_cond_wait( &consumerReceivedAckCond,
                            &consumerReceivedAckLock );
       }
      pthread_mutex_unlock(   &consumerReceivedAckLock  );
      DEBUG__printf2("producer %d got ack %d\n", params->producerID, currConsumerReceivedACKNum );

      // release producer lock (so different producer can get and send)
      pthread_mutex_unlock(   &producerAccessMutex );  
    } //if not done, do again  

   //Shutdown producer
   pthread_exit(NULL);
    
 }

