//H2O problemi modellenmistir.O atomları  prosesler icerisinde %3. sira

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/wait.h>
#include <sys/sem.h>
#include <sys/shm.h>
#include <unistd.h>
#include <signal.h>
#include <stdlib.h>
#include <stdio.h>
#include <time.h>      // srand()

#define KEYSEM 1
#define KEYSEM2 2
#define SHM_OXYGEN 3
#define SHM_HYDROGEN 4
#define KEY_OXYQUEUE 5
#define KEY_HYDROQUEUE 6
#define KEY_ESIK 7
#define ATOMNUM 36

int sonsem;
int Mutex,oxyQueue,hydroQueue,esik, oxygen_p, *oxygen,hydrogen_p,*hydrogen;
void *adres;
struct sembuf artir, eksilt;

/* Sadece cocuklarin uyarilmasi icin, 12 numarali sinyali alan cocuk uyanir. */ 
void sinyal12(void)
{
}

void bag(int pid, int i)
{
   if((i+1)%3==0)
      printf("Oxygen Atomu baglaniyor: %d\n",pid);
   else
      printf("Hydrogen Atomu baglaniyor: %d\n",pid);
}


//semaforlar uzerinde P islemi yapar, s>0 => eksiltir, s=0 => proses bloke (s uzerinde bekle) 
/* KB icinde calisan proses isini bitirene kadar bekler                      */
void sem_wait(int semid, int val)
{
   struct sembuf semafor;
   
   semafor.sem_num=0;
   semafor.sem_op=(-1*val);
   semafor.sem_flg=1;
   // tum islemleri &semafor uzerine yansitir 
   semop(semid, &semafor,1);
}


//semaforlar uzerinde V islemi yapar, kaynagin isinin bittigini ve
//varsa bekleyen prosese verilebilecegini gosterir, 
//bekleyen proc. yoksa semafor degerini 1 arttirir
/* KB icinde olmadigina dair isaret birakir                                  */
void sem_signal(int semid, int val)
{
   struct sembuf semafor;
   
   semafor.sem_num=0;
   semafor.sem_op=val;
   semafor.sem_flg=1;
   semop(semid, &semafor,1);
}


int main (void) {
  
   int i, f, Atom[ATOMNUM],rastgele;
   
   // random seed; her defasinda farkli random sayilar uretmek icin
   srand((unsigned)time(NULL));
  
   signal(12, (sighandler_t) sinyal12);  

   for (i=0; i<ATOMNUM; i++){
      
      f=fork();
      if (f==-1){
         printf("FORK hata....\n");
         exit(1);
      }
    
      if (f==0)
         break;
      Atom[i]=f;  
   }
   if (f!=0){
      
      /*****************************************************************************/
      /*          Anne gerekli kaynaklari yaratmaya basliyor                       */
      /* semget() ile bir semafor grubu yaratilir ve semafor id'sini geri dondurur */
      /*****************************************************************************/
      
      // Senkronizasyon amacli bir semafor iceren bir semafor grubu yaratir "sonsem"	   
      // Anne prosesin her bir cocugun da paylasilan kaynaklari kullanmalarinin sonlanmasi 
      // ile senkronize olmalarini saglar
 
      // Senkronizasyon icin yaratildi ilk degeri 0	  
      sonsem=semget(KEYSEM2, 1, 0700|IPC_CREAT);
      // sonsem semafor grubundan 0 nolu semaforun degerini 0 yap
      semctl(sonsem, 0, SETVAL,0);

      // Karsilikli dislama icin yaratildi ilk deger 1
      // ortak tampon alanina erisim, karsilikli dislama
      Mutex=semget(KEYSEM,1,0700|IPC_CREAT);
      semctl(Mutex,0,SETVAL,1);

      // Senkronizasyon icin yaratildi ilk degeri 0	  
      oxyQueue=semget(KEY_OXYQUEUE,1,0700|IPC_CREAT);
      semctl(oxyQueue,0,SETVAL,0);   

      // Senkronizasyon icin yaratildi ilk degeri 0	  
      hydroQueue=semget(KEY_HYDROQUEUE,1,0700|IPC_CREAT);
      semctl(hydroQueue,0,SETVAL,0);  

      // sayma semaforu ilk degeri 3
      // yeteri kadar atom yoksa, birbirini beklemeli (3 atom da gelmis olmali)
      // sayma semaforu, ilk deger kaynak sayisina esit olmali 
      esik=semget(KEY_ESIK,1,0700|IPC_CREAT);
      semctl(esik,0,SETVAL,3);  
 
      /*****************************************************************************/
      /* shmget() belirtilen bellek alanina erisir veya "size" kadar alan olusturur*/
      /* bu fonk. bir id dondurur ve bu id ile ilgili paylasilan bellege erisir    */
      /*****************************************************************************/
      

      oxygen_p=shmget(SHM_OXYGEN,sizeof(int),0700|IPC_CREAT);    
      oxygen=(int *) shmat(oxygen_p,0,0);
      *oxygen=0;
      // hydrogen degeri tutan adresi iade etti. 
      shmdt(oxygen);

      hydrogen_p=shmget(SHM_HYDROGEN,sizeof(int),0700|IPC_CREAT);    
      // id (pba) ile belirtilen bellek alanini arasinda baglantiyi kurdu
      // Boylece paylasilan bellek alaninda nxm boyutunda bir matris yaratmis oldu
      hydrogen=(int *) shmat(hydrogen_p,0,0);
      *hydrogen=0;
      
      // hydrogen degeri tutan adresi iade etti. 
      shmdt(hydrogen);

      printf(" H ve O Atomlariolusturuldu ve H2O islemi baslayacak .......\n");
      
      // Anne cocuk prosesleri yaratmisti ve herbiri askida durumundaydi
      // Anne, cocuklarina kaynaklarin hazir olduguna iliskin haber yolluyor
      // 12 nolu sinyalle tum cocuklar tek tek uyandiriliyor.
      for (i=0; i<ATOMNUM; i++)
         kill(Atom[i],12);

      // Anne proses askiya aliniyor,  
      // herbir cocugunun da kaynak kullaniminin sonlanmasini beklemeye basliyor
      // Anne sonsem=0 olana kadar burada bekleyecek. Anne sonsem=-n degerinde askida
      sem_wait(sonsem,ATOMNUM);

      // sonsem=0 oldu, Cocuklarin hepsi sonlandi. Anne artik kaynaklarini iade etmeye baslayabilir.
      printf("Su molekullerinin olusturulmasi tamamlandi\n");
    
      // Senkronizasyon amacli yaratilan semafor grubunu "sonsem" siler ve 
      // bu kaynaga ulasmak icin bekleyen butun islemlere bir ileti yollar
      semctl(sonsem,0,IPC_RMID,0);
      semctl(Mutex,0,IPC_RMID,0);
      semctl(oxyQueue,0,IPC_RMID,0);
      semctl(hydroQueue,0,IPC_RMID,0);
      
      shmctl(oxygen_p,IPC_RMID,0);     
      shmctl(hydrogen_p,IPC_RMID,0);  
      shmctl(esik,IPC_RMID,0);       
      
      exit(0); 
   }
   else{
    
      pause();
      
      /*****************************************************************************/
      /* Cocuk proseslerin her biri, annenin yaratmis oldugu semafor gruplarini    */ 
      /* ve paylasilan bellek alaninin tanimlayici numara bilgilerini ogreniyor    */
      /* herbir cocuk paylasilan bellek bolgesini kendi adres uzatyina ekliyor     */
      /*****************************************************************************/
      
      // Karsilikli dislama amacli yaratilan semafor grubunu ogrendi
      Mutex=semget(KEYSEM,1,0);
      // Senkronizasyon amacli yaratilan semaforu grubunu ogrendi
      sonsem=semget(KEYSEM2,1,0);
      oxyQueue=semget(KEY_OXYQUEUE,1,0);
      hydroQueue=semget(KEY_HYDROQUEUE,1,0);
      esik=semget(KEY_ESIK,1,0);

      // yaratilan paylasilan bellegi ogrendi
      oxygen_p=shmget(SHM_OXYGEN,sizeof(int),0);
      // ve baglantiyi kurdu
      oxygen=(int *) shmat(oxygen_p,0,0);   
      hydrogen_p=shmget(SHM_HYDROGEN,sizeof(int),0);
      hydrogen=(int *) shmat(hydrogen_p,0,0);   

   if((i+1)%3==0){  // Oxygen 
        
      /*****************************************************************************/
      /* herbir cocuk icin kritik bolge islemlerinin basladigi yer. Her cocuk,     */
      /* paylasilan bellek bolgesindeki degeri kendi lokal degiskenine alip        */
      /* degerini degistiriyor ve tekrar geri yaziyor                              */
      /*****************************************************************************/
     
      // Herbir cocuk kilit semafor grubundaki degeri 1  azaltiyor.

      sem_wait(Mutex,1);  // Mutex Wait (Mux Basliyor)
	   *oxygen += 1;		  // oxygen++        
         
      srand(getpid());
      rastgele=(int)rand()%5;
      
      printf("i+1 :: %d \t rastgele :: %d \n",i+1 ,rastgele);
      printf("O :: %d\t H:: %d \n",*oxygen, *hydrogen); 
      
      printf("Baglanmak icin Oxygen atomu geldi %d bekleme suresi %d: \n",getpid(),rastgele);
      sleep(rastgele);
         
      if(*hydrogen >=2) {
		   
         sem_signal(hydroQueue,2);
         *hydrogen -= 2;	
         sem_signal(oxyQueue,1);
		   *oxygen -=1;
      }
	   
      else {
		   sem_signal(Mutex,1); // Mutex Signal (Mux Bitti)
	   }
        
      sem_wait(oxyQueue,1); //Oxygen kuyruguna katil
      bag(getpid(),i);
      sem_wait(esik,1);
      sem_signal(Mutex,1);
   }
   
   else{		//Hydrogen
   
      sem_wait(Mutex,1);
      *hydrogen+= 1;
      
      srand(getpid());
      rastgele=(int)rand()%5;
      
      printf("Baglanmak icin Hydrogen atomu geldi %d bekleme suresi %d: \n",getpid(),rastgele);
      sleep(rastgele);
      
      if((*hydrogen>=2)&&(*oxygen>=1)) {
      
         sem_signal(hydroQueue,2);
         *hydrogen -= 2;	
         sem_signal(oxyQueue,1);
         *oxygen -=1;
      }
	
      else {
         sem_signal(Mutex,1);
      }
        
      sem_wait(hydroQueue,1);      
      bag(getpid(),i);
      sem_wait(esik,1);
   
   }
    
      /*****************************************************************************/
      /* cocuk prosesler paylasilan bellegi adres uzayindan cikarirlar, silerler   */ 
      /* Daha sonra anne prosese paylasilan kaynaklarin kullanimlarinin bittigini  */ 
      /* belirtiyorlar ve sonlaniyorlar                                            */
      /*****************************************************************************/
   shmdt(hydrogen);
   shmdt(oxygen);
   sem_signal(esik,1);
      
   // Her cocuk calisip sonsem semaforunun degerini 1 arttirdi {(-n), (-+1), ... ,1, 0} 
   // Anne proses sem_wait uzerinde kilitlenmisti, sonsem=0 gorunce calismaya baslar, 
   sem_signal(sonsem,1);
    
   exit(0); 	
   
   }
  
   return(0);
}  

