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

// Καθολικές μεταβλητές για συγχρονισμό και κατάσταση
int counter = 0;
pthread_mutex_t lock;
// Αρχικοποίηση μεταβλητής συνθήκης με στατικό τρόπο
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;

// Συνάρτηση για το πρώτο νήμα (αυτό που περιμένει)
void *f1(void* unused) {
    pthread_mutex_lock(&lock); // Κλείδωμα του mutex
    
    // Χρήση "while" αντί για "if" για την αποφυγή spurious wakeups (ψευδών αφυπνίσεων)
    while (counter == 0) {
        // Αποδεσμεύει το mutex και θέτει το νήμα σε αναμονή
        pthread_cond_wait(&cond, &lock);
    }
    
    // Αφού ικανοποιηθεί η συνθήκη, εκτελείται η εργασία
    counter = 5000 / counter;
    printf("Νέος μετρητής (f1): %d\n", counter);
    
    pthread_mutex_unlock(&lock); // Ξεκλείδωμα του mutex
    return NULL;
}

// Συνάρτηση για το δεύτερο νήμα (αυτό που ειδοποιεί)
void *f2(void* unused) {
    pthread_mutex_lock(&lock); // Κλείδωμα του mutex
    
    counter = 1000;
    printf("Πρώτη ενημέρωση (f2): %d\n", counter);
    
    // Στέλνει σήμα στο νήμα που περιμένει ότι η συνθήκη άλλαξε
    pthread_cond_signal(&cond);
    
    pthread_mutex_unlock(&lock); // Ξεκλείδωμα του mutex
    return NULL;
}

int main() {
    pthread_t t1, t2; // Αναγνωριστικά νημάτων
    
    // Αρχικοποίηση του mutex
    pthread_mutex_init(&lock, NULL); 
    
    // Δημιουργία των νημάτων
    pthread_create(&t1, NULL, &f1, NULL); 
    pthread_create(&t2, NULL, &f2, NULL); 
    
    // Το αρχικό νήμα (main) περιμένει να τερματίσουν και τα δύο νήματα
    pthread_join(t1, NULL); 
    pthread_join(t2, NULL); 
    
    // Αποδέσμευση πόρων
    pthread_mutex_destroy(&lock); 
    pthread_cond_destroy(&cond); 
    
    return 0;
}
