/*
 * shmark.c
 *
 * This will modify shared memory contents and then inform shmbench.
 *
 * By Kamran Karimi
 */

#include "shmbm.h"


int msg_resultid;


void send_line(char *message)
{
 struct msg_result result_mess; 

 if(msg_resultid != -1)
 {
  result_mess.mtype = MSG_TYPE;
  strcpy(result_mess.mtext, message);
  if(msgsnd(msg_resultid, (struct msgbuf *)&result_mess, MSG_RESULT_SIZE, 0) < 0)
  {
   fprintf(stderr, "shmmark: msgsnd() failed BECAUSE %s\n", strerror(errno));
   exit(20);
  }
 }
 else
  fprintf(stderr, message);  
}


void main(int argc, char **argv)
{
 int semid;
 int shmid;
 int shmidx, num, i, reverse = 0;
 int total, content;
 char *shmptr;
 char buff[MSG_RESULT_SIZE];
 struct sigaction sact;
 clock_t start;
 double ticks_sec;
 struct sembuf sem[1];

 if(argc < 4)
 {
  fprintf(stderr,"USAGE: shmmark <total> <num> <fill> [-r]\n\n");
  fprintf(stderr,"<total> is the number of shmmark processes in the benchmark\n");
  fprintf(stderr,"<num> is the number of this process\n");
  fprintf(stderr,"<fill> is the value written to the shared memory\n");
  fprintf(stderr,"'-r': writes to the shared memory from the end to the beginning\n");
  exit(20);
 }
 total = atoi(argv[1]);
 if(total <= 0)
 {
  fprintf(stderr, "Invalid total number of processes\n");
  exit(20);
 }
 num = atoi(argv[2]);
 if(num < 0)
 {
  fprintf(stderr, "Invalid process number\n");
  exit(20);
 }
 content = atoi(argv[3]);
 if(argc == 5 && !strcasecmp(argv[4], "-r"))
  reverse = 1;
 ticks_sec = (double) sysconf(_SC_CLK_TCK);
 
 sact.sa_handler = SIG_IGN;
 sigemptyset(&sact.sa_mask);
 sact.sa_flags = 0;
   
 if(sigaction(DIPC_SIG_READER, &sact, NULL) < 0)
 {
  fprintf(stderr,"shmmark#%d: Can't Catch Signal", num);
  fprintf(stderr,"  BECAUSE: %s\n",strerror(errno));
 }
 
 if(sigaction(DIPC_SIG_WRITER, &sact, NULL) < 0)
 {
  fprintf(stderr,"shmmark#%d: Can't Catch Signal", num);
  fprintf(stderr,"  BECAUSE: %s\n",strerror(errno));
 }

 semid = semget(SEM_KEY, total, SEM_MODE);
 if(semid < 0)
 {
  fprintf(stderr,"shmmark#%d: semget() failed BECAUSE %s\n",num,strerror(errno));
  exit(20);
 }
 
 msg_resultid = msgget(MSG_RESULT_KEY, MSG_MODE);
 if(msg_resultid < 0)
 {
  sprintf(buff,"shmmark: msgget() failed BECAUSE %s", strerror(errno));
  send_line(buff);
  exit(20);
 }

 fprintf(stderr,"shmmark#%d: Started...\n", num);
 for(shmidx = 0; shmidx < SHM_NUM; shmidx++)
 {
  shmid = shmget(test_shm[shmidx].key, test_shm[shmidx].size, SHM_MODE);
  if(shmid < 0) 
  {
   fprintf(stderr,"shmmark#%d: shmget() failed BECAUSE %s\n",num,strerror(errno));
   exit(20);
  }
  shmptr = (char *)shmat(shmid,0,0);
  if(shmptr == (char *) -1)
  {
   fprintf(stderr,"shmmark#%d: shmat() failed BECAUSE %s\n",num, strerror(errno));
   exit(20);
  }
 
  sem[0].sem_num = num;
  sem[0].sem_op = -1;
  sem[0].sem_flg = SEM_UNDO;
 
  if(semop(semid, &sem[0], 1) < 0)
  {
   fprintf(stderr,"shmmark#%d: semop() failed BECAUSE %s\n", num, strerror(errno));
   exit(20);
  }
  
  start = times(NULL);
  if(reverse)
  {
   int up;
   
   up = test_shm[shmidx].size / total * total;
   if(up + num > test_shm[shmidx].size)
    i = up - total + num;
   else
    i = up + num; 
   
   for( ; i >= num; i -= total)
    shmptr[i] = content;
  }
  else
  {  
   for(i = num; i < test_shm[shmidx].size; i += total)
    shmptr[i] = content;
  }  
  test_shm[shmidx].time = (times(NULL) - start) / ticks_sec;
  shmdt((char *)shmptr);
 
  sprintf(buff,"shmmark#%d: Wrote to a shared memory of size %d bytes in %1.2f seconds",
                            num, test_shm[shmidx].size, test_shm[shmidx].time);
  send_line(buff);
 }
}
