#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <pthread.h>

pthread_mutex_t resource[5];
char res_str[] = "00000";

int pick(){return rand()%5;}
int pick2(){return rand()%2+1;}

void* t1 (void * p) {
  int r1, r2;
  while(1) {
    r1=pick(); r2=pick();
    sleep(pick2());
    pthread_mutex_trylock(&resource[r1]); // tryunlock istället för lock
    res_str[r1]='1';
    pthread_mutex_trylock(&resource[r2]); // tryunlock istället för lock
    res_str[r2]='1';
    sleep(pick2());
    res_str[r1]='0';
    res_str[r2]='0';
    pthread_mutex_unlock(&resource[r1]);
    pthread_mutex_unlock(&resource[r2]);
  }
}

void* t2 (void * p) {
  int r1, r2;
  while(1) {
    r1=pick(); r2=pick();
    sleep(pick2());
    pthread_mutex_trylock(&resource[r1]); // tryunlock istället för lock
    res_str[r1]='2';
    pthread_mutex_trylock(&resource[r2]); // tryunlock istället för lock
    res_str[r2]='2';
    sleep(pick2());
    res_str[r1]='0';
    res_str[r2]='0';
    pthread_mutex_unlock(&resource[r1]);
    pthread_mutex_unlock(&resource[r2]);
  }
}

// det krävs 3 villkor för att deadlock ska uppstå. Den första är circular wait där tråd1 väntar på tråd2 och tråd2 väntar på tråd3 och tråd3 väntar då på tråd1 osv
// som ska då släppa sina resurser. Eftersom alla väntar på den andra i ett cyckliskt förlopp så kommer ingen av trådarna släppa sina resurser. Den andra villkoren
// är då no preemption, detta betyder att operativ systemet inte har något sätt att ta tillbaka resursna från trådarna som har gjort en mutex lock. Den tredje är mutual
// exclusion där ingen av trådarna kan påverkar andra resurser om dem har blivit låsta. När vi då gör mutex lock uppfyller vi redan de två sista villkoren.
// Detta leder oss till sista villkoren circular wait. Eftersom trådarna slumpvis låser resursena kan det hända precis som i den första körningen dvs tråd1 har låst resurs
// 2 och väntar på resurs 3, men tråd2 har låst resurs3 och väntar på resurs 2. UNIX erbbjuder trylock som är en variant av lock. Den fungerar precis som lock.
// då en resurs är fri så kommer den låsa den precis som den vanliga mutex lock. Däremot om den försöker låsa en resurs som är redan låst då return:a den direkt
// och fortsätter köra programmet. På så sätt behöver inte trådarna stå stilla och väntar på varandra då det sker en circular wait.

main() {
  int i=0,n;
  pthread_t id1, id2;
  pthread_create(&id1,NULL,&t1,NULL);
  pthread_create(&id2,NULL,&t2,NULL);
  while(i++<30)
  {
    sleep(1);
    printf("%2d: %s\n", i, res_str);
  }
}
