/* 
 * shmbench.c 
 *
 * shmbench will prepare a shared memory and a semaphore, and then invoke
 * other processes (shmmark). They will all access the shared memory, and 
 * then use the semaphore to inform shmbench they are done.
 *
 * By Kamran Karimi
 */  

#include "shmbm.h"

int semid = -1;
int msg_resultid = -1;

static void sig_handler(int signo)
{
 int count;

 if(semid != -1)
  semctl(semid, 1, IPC_RMID, 0);
  
 if(msg_resultid != -1)
  msgctl(msg_resultid, IPC_RMID, 0);
  
 for(count = 0; count < SHM_NUM; count++)
 {
  if(test_shm[count].id != -1)
   shmctl(test_shm[count].id, IPC_RMID, 0);
 }  
 exit(5);
}

void main(int argc, char **argv)
{
 int total, semidx, shmidx, count, i;
 ushort *seminit;
 struct sigaction sact;
 struct sembuf *sem;
 struct msg_result result_mess;
 
 if(argc < 2)
 {
  fprintf(stderr,"USAGE: shmbench <total>\n\n");
  fprintf(stderr,"<total> is the number of shmmark processes in the benchmark\n");
  fprintf(stderr,"This should be the same as the one given to shmmarks\n");
  exit(20);
 }
 total = atoi(argv[1]);
 if(total <= 0)
 {
  fprintf(stderr, "Invalid argument\n");
  exit(20);
 }

 if((seminit = (ushort *)malloc(total * sizeof(ushort))) == NULL)
 {
  fprintf(stderr, "Can't allocate memory\n");
  exit(1);
 }
 if((sem = (struct sembuf *)malloc(total * sizeof(struct sembuf))) == NULL)
 {
  fprintf(stderr, "Can't allocate memory\n");
  exit(1);
 }
  
 sact.sa_handler = SIG_IGN;
 sigemptyset(&sact.sa_mask);
 sact.sa_flags = 0;

 if(sigaction(SIGURG, &sact, NULL) < 0)
 {
  fprintf(stderr,"shmbench: Can't Catch Signal");
  fprintf(stderr,"  BECAUSE: %s\n",strerror(errno));
 }
 
 if(sigaction(SIGPWR, &sact, NULL) < 0)
 {
  fprintf(stderr,"shmbench: Can't Catch Signal");
  fprintf(stderr,"  BECAUSE: %s\n",strerror(errno));
 }

 sact.sa_handler = sig_handler;
 if(sigaction(SIGINT, &sact, NULL) < 0)
 {
  fprintf(stderr,"shmbench: Can't Catch Signal");
  fprintf(stderr,"  BECAUSE: %s\n",strerror(errno));
 }

 msg_resultid = msgget(MSG_RESULT_KEY, MSG_MODE | IPC_CREAT);
 if(msg_resultid < 0) 
 {
  fprintf(stderr,"shmbench: msgget() failed BECAUSE %s\n", strerror(errno));
  raise(SIGINT);
 }

 semid = semget(SEM_KEY, total, SEM_MODE | IPC_CREAT);
 if(semid < 0)
 {
  fprintf(stderr,"shmbench: semget() failed BECAUSE %s\n", strerror(errno));
  raise(SIGINT);
 }

 for(semidx = 0; semidx < total; semidx++)
  seminit[semidx] = 0;
 
 if(semctl(semid, total, SETALL, seminit) < 0)
 {
  fprintf(stderr, "shmbench: semctl() failed BECAUSE %s\n", strerror(errno));
  raise(SIGINT);
 } 
 for(shmidx = 0; shmidx < SHM_NUM; shmidx++)
 {
  test_shm[shmidx].id = shmget(test_shm[shmidx].key, test_shm[shmidx].size, 
                                                       SHM_MODE | IPC_CREAT);
  if(test_shm[shmidx].id < 0) 
  {
   fprintf(stderr,"shmbench: shmget() failed BECAUSE %s\n", strerror(errno));
   raise(SIGINT);
  }
  test_shm[shmidx].ptr = (char *)shmat(test_shm[shmidx].id, 0, 0);
  if(test_shm[shmidx].ptr == (char *) -1)
  {
   fprintf(stderr,"shmbench: shmat() failed BECAUSE %s\n", strerror(errno));
   raise(SIGINT);
  }
  for(i = 0; i < test_shm[shmidx].size; i++)
   test_shm[shmidx].ptr[i] = 121;
 }
 if(fork() == 0)
 { /* child */
  if(execlp("sh","sh","shm_s", NULL) < 0)
   fprintf(stderr,"shmbench: execlp failed!\n");
  exit(0);
 }
 for(shmidx = 0; shmidx < SHM_NUM; shmidx++)
 {
  for(semidx = 0; semidx < total; semidx++)
  { 
   sem[semidx].sem_num = semidx;
   sem[semidx].sem_op = 1;
   sem[semidx].sem_flg = SEM_UNDO;
  }
   
  if(semop(semid, sem, total) < 0)
  {
   fprintf(stderr,"shmbench: semop() failed BECAUSE %s\n", strerror(errno));
   raise(SIGINT);
  }
 
  for(count = 0; count < total; count++)
  {
   if(msgrcv(msg_resultid, (struct msgbuf *)&result_mess, MSG_RESULT_SIZE, 0, 0) < 0)
   {
    fprintf(stderr,"shmbench: msgrcv() failed BECAUSE %s\n", strerror(errno));
    raise(SIGINT);
   } 
   fprintf(stderr,"%s\n", result_mess.mtext);  
  }   
 }

/*
 for(shmidx = 0; shmidx < SHM_NUM; shmidx++)
 {
  int count;
  
  for(count = 0; count <= test_shm[shmidx].size-total; count += 4096)
  {
   fprintf(stderr,"shmbench: addresses %d till %d hold:\n", count, count + total);
   for(i = 0; i < total; i++)
   {
    fprintf(stderr," %d ", test_shm[shmidx].ptr[count + i]);
   }
   fprintf(stderr,"\n----------------------------------\n");
  }
 }
*/
   
 raise(SIGINT);
}
