POSIX SYSTEM CALLS & MULTITHREADING LAB

10 C Code Examples

Kompilasi 10 program C murni berstandar POSIX (Linux/UNIX/GCC) untuk ujian praktik dan teori OS: System calls manajemen proses, Inter-Process Communication (IPC), Multithreading Pthread, serta penanganan konkurensi (Mutex, Semaphore, dan Algoritma Klasik Sinkronisasi). Seluruh kode valid, compilable, dilengkapi penjelasan per-baris dan expected output.

1. Fork & Process Creation (`fork.c`)

PROCESS MANAGEMENT

Mendemonstrasikan pembuatan proses anak (child process) menggunakan system call fork(). Menunjukkan perbedaan nilai kembalian pid di proses induk (parent) dan anak.

fork_example.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>

int main() {
    pid_t pid;
    printf("[PARENT] PID utama: %d, bersiap memanggil fork()...\n", getpid());

    pid = fork();

    if (pid < 0) {
        // Fork gagal dialokasikan oleh kernel
        perror("Fork gagal dieksekusi");
        exit(EXIT_FAILURE);
    } 
    else if (pid == 0) {
        // Blok eksekusi proses anak (Child Process)
        printf("[CHILD] Halo dari Proses Anak!\n");
        printf("[CHILD] PID Anak: %d, Parent PID (PPID): %d\n", getpid(), getppid());
    } 
    else {
        // Blok eksekusi proses induk (Parent Process)
        printf("[PARENT] Halo dari Proses Induk!\n");
        printf("[PARENT] PID Induk: %d, Baru saja melahirkan anak dengan PID: %d\n", getpid(), pid);
    }

    printf("[ALL] Baris ini dieksekusi oleh kedua proses (PID: %d)\n", getpid());
    return 0;
}
🔍 Penjelasan Baris Kunci:
  • pid = fork();: Kernel menduplikasi PCB, stack, heap, dan deskriptor proses. Setelah baris ini, ada 2 proses konkuren.
  • if (pid == 0): Di proses anak, kernel menyuplai nilai 0 untuk membedakan perannya.
  • else (pid > 0): Di proses parent, kernel menyuplai PID dari anak yang baru dibuat.
  • getpid() & getppid(): Mendapatkan PID diri sendiri dan PID induk.
📟 Expected Terminal Output:
$ gcc -o fork fork_example.c && ./fork
[PARENT] PID utama: 4520, bersiap memanggil fork()...
[PARENT] Halo dari Proses Induk!
[PARENT] PID Induk: 4520, Baru saja melahirkan anak dengan PID: 4521
[ALL] Baris ini dieksekusi oleh kedua proses (PID: 4520)
[CHILD] Halo dari Proses Anak!
[CHILD] PID Anak: 4521, Parent PID (PPID): 4520
[ALL] Baris ini dieksekusi oleh kedua proses (PID: 4521)

2. Wait & Process Synchronization (`wait.c`)

SYNC & STATUS

Mencegah Zombie Process dengan memanggil wait() di proses orang tua, menahan eksekusi hingga anak tuntas dan mengevaluasi status exit code.

wait_example.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>

int main() {
    pid_t pid = fork();

    if (pid < 0) {
        perror("Fork error");
        exit(1);
    } 
    else if (pid == 0) {
        printf("[CHILD PID %d] Mulai komputasi berat selama 2 detik...\n", getpid());
        sleep(2);
        printf("[CHILD] Selesai. Keluar dengan status exit code 42.\n");
        exit(42); // Nilai status exit khusus
    } 
    else {
        int status;
        printf("[PARENT] Menunggu anak (PID %d) selesai dengan wait()...\n", pid);
        pid_t child_pid = wait(&status);

        printf("[PARENT] Anak (PID %d) telah selesai dieksekusi!\n", child_pid);
        if (WIFEXITED(status)) {
            printf("[PARENT] Status Exit Code Anak: %d\n", WEXITSTATUS(status));
        } else if (WIFSIGNALED(status)) {
            printf("[PARENT] Anak terhenti secara paksa oleh sinyal: %d\n", WTERMSIG(status));
        }
    }
    return 0;
}
🔍 Penjelasan Baris Kunci:
  • wait(&status): Memblokir pemanggil hingga state proses anak berubah. Mengembalikan PID anak yang terminate.
  • WIFEXITED(status): Bernilai true jika proses anak keluar secara normal (misal lewat exit()).
  • WEXITSTATUS(status): Mengekstrak 8-bit terendah dari nilai exit code anak (dalam contoh: 42).
📟 Expected Terminal Output:
$ gcc -o wait wait_example.c && ./wait
[PARENT] Menunggu anak (PID 4832) selesai dengan wait()...
[CHILD PID 4832] Mulai komputasi berat selama 2 detik...
[CHILD] Selesai. Keluar dengan status exit code 42.
[PARENT] Anak (PID 4832) telah selesai dieksekusi!
[PARENT] Status Exit Code Anak: 42

3. UNIX Anonymous Pipe (IPC) (`pipe.c`)

HALFDUPLEX IPC

Saluran komunikasi satu arah (unidirectional) di dalam memori kernel antar-proses berkerabat (Parent-Child) menggunakan 2 file descriptor: read end (fd[0]) dan write end (fd[1]).

pipe_ipc.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/wait.h>

#define BUFFER_SIZE 64

int main() {
    int fd[2]; // fd[0] = Read end, fd[1] = Write end
    char write_msg[] = "SECRET_PAYLOAD_IPC_0x89A";
    char read_msg[BUFFER_SIZE];

    // 1. Buat pipa saluran kernel sebelum fork
    if (pipe(fd) == -1) {
        perror("Pembuatan pipe gagal");
        return 1;
    }

    pid_t pid = fork();

    if (pid < 0) {
        perror("Fork error");
        return 1;
    } 
    else if (pid == 0) {
        // CHILD: Bertindak sebagai Writer
        close(fd[0]); // Tutup ujung baca yang tidak digunakan
        printf("[CHILD] Mengirim data ke pipe: '%s'\n", write_msg);
        write(fd[1], write_msg, strlen(write_msg) + 1);
        close(fd[1]); // Tutup setelah selesai menulis (kirim EOF)
        exit(0);
    } 
    else {
        // PARENT: Bertindak sebagai Reader
        close(fd[1]); // Tutup ujung tulis yang tidak digunakan
        wait(NULL);   // Tunggu anak selesai menulis
        read(fd[0], read_msg, BUFFER_SIZE);
        printf("[PARENT] Menerima data dari pipe: '%s'\n", read_msg);
        close(fd[0]); // Tutup ujung baca
    }
    return 0;
}
🔍 Penjelasan Baris Kunci:
  • pipe(fd): Mengalokasikan buffer melingkar (circular buffer) di kernel dan mengisi array fd[0] (read) dan fd[1] (write).
  • close(fd[0]) / close(fd[1]): Menutup channel yang tidak terpakai sangat penting agar pembaca menerima indikator EOF (End-Of-File) saat writer menutup ujung tulis.
📟 Expected Terminal Output:
$ gcc -o pipe pipe_ipc.c && ./pipe
[CHILD] Mengirim data ke pipe: 'SECRET_PAYLOAD_IPC_0x89A'
[PARENT] Menerima data dari pipe: 'SECRET_PAYLOAD_IPC_0x89A'

4. POSIX / System V Shared Memory (`shm.c`)

FASTEST IPC

Metode IPC tercepat karena dua proses memetakan segmen halaman memori fisik RAM yang sama ke dalam address space virtual mereka, menghindari overhead context switch kernel.

shared_memory.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/wait.h>

#define SHM_SIZE 1024

int main() {
    // 1. Dapatkan identifier unik untuk shared memory segment
    key_t key = ftok("/tmp", 65);
    int shmid = shmget(key, SHM_SIZE, 0666 | IPC_CREAT);

    if (shmid < 0) {
        perror("shmget gagal");
        exit(1);
    }

    pid_t pid = fork();

    if (pid == 0) {
        // CHILD: Attach ke shared memory dan menulis pesan
        char *str = (char*) shmat(shmid, (void*)0, 0);
        printf("[CHILD] Menulis ke Shared Memory...\n");
        strncpy(str, "StudyHub SOC Kernel Shared Memory Buffer v2.0", SHM_SIZE);
        shmdt(str); // Detach memori
        exit(0);
    } 
    else {
        // PARENT: Tunggu anak selesai lalu membaca isi memori
        wait(NULL);
        char *str = (char*) shmat(shmid, (void*)0, 0);
        printf("[PARENT] Membaca dari Shared Memory: '%s'\n", str);
        shmdt(str); // Detach memori

        // Hancurkan segmen shared memory dari sistem
        shmctl(shmid, IPC_RMID, NULL);
    }
    return 0;
}
🔍 Penjelasan Baris Kunci:
  • shmget(): Mengalokasikan segmen memori bersama di kernel.
  • shmat(): Memetakan (attach) alamat memori kernel ke virtual pointer di user space.
  • shmdt(): Melepaskan kaitan (detach) segmen dari proses.
  • shmctl(shmid, IPC_RMID, NULL): Menghapus segmen secara permanen agar tidak terjadi memory leak di RAM.
📟 Expected Terminal Output:
$ gcc -o shm shared_memory.c && ./shm
[CHILD] Menulis ke Shared Memory...
[PARENT] Membaca dari Shared Memory: 'StudyHub SOC Kernel Shared Memory Buffer v2.0'

5. POSIX Thread Creation & Join (`pthread.c`)

MULTITHREADING

Membuat beberapa thread pekerja konkuren menggunakan pustaka standar pthread, melewatkan argumen ID thread, dan menunggu penyelesaiannya lewat pthread_join().

pthread_example.c
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>

#define NUM_THREADS 3

void* thread_worker(void* arg) {
    long tid = (long)arg;
    printf("[THREAD %ld] Berjalan aktif di core CPU...\n", tid);
    sleep(1);
    printf("[THREAD %ld] Tugas komputasi selesai.\n", tid);
    pthread_exit((void*)(tid * 100)); // Mengembalikan exit status
}

int main() {
    pthread_t threads[NUM_THREADS];
    void* status;

    printf("[MAIN] Membuat %d worker thread...\n", NUM_THREADS);

    for (long i = 0; i < NUM_THREADS; i++) {
        int rc = pthread_create(&threads[i], NULL, thread_worker, (void*)i);
        if (rc) {
            printf("Error pembuatan thread: %d\n", rc);
            exit(EXIT_FAILURE);
        }
    }

    // Menunggu setiap thread tuntas (Join)
    for (int i = 0; i < NUM_THREADS; i++) {
        pthread_join(threads[i], &status);
        printf("[MAIN] Thread %d telah bergabung kembali dengan return value: %ld\n", i, (long)status);
    }

    printf("[MAIN] Seluruh thread sukses dieksekusi!\n");
    return 0;
}
🔍 Penjelasan Baris Kunci:
  • pthread_create(&tid, attr, start_routine, arg): Membuka thread eksekusi baru. Butuh link compiler -lpthread.
  • pthread_join(tid, &retval): Memblokir thread utama sampai thread target selesai, sekaligus mengambil return value-nya.
📟 Expected Terminal Output:
$ gcc -o pthread pthread_example.c -lpthread && ./pthread
[MAIN] Membuat 3 worker thread...
[THREAD 0] Berjalan aktif di core CPU...
[THREAD 1] Berjalan aktif di core CPU...
[THREAD 2] Berjalan aktif di core CPU...
[THREAD 0] Tugas komputasi selesai.
[THREAD 1] Tugas komputasi selesai.
[THREAD 2] Tugas komputasi selesai.
[MAIN] Thread 0 telah bergabung kembali dengan return value: 0
[MAIN] Thread 1 telah bergabung kembali dengan return value: 100
[MAIN] Thread 2 telah bergabung kembali dengan return value: 200
[MAIN] Seluruh thread sukses dieksekusi!

6. Mutex Lock (Mencegah Race Condition) (`mutex.c`)

MUTUAL EXCLUSION

Mencegah race condition ketika 2 thread serentak memodifikasi variabel penghitung global bersama (shared counter) dengan mengunci critical section menggunakan pthread_mutex_lock.

mutex_counter.c
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>

#define ITERATIONS 1000000

long shared_counter = 0;
pthread_mutex_t lock; // Kunci Mutex

void* increment_task(void* arg) {
    for (int i = 0; i < ITERATIONS; i++) {
        // --- AWAL CRITICAL SECTION ---
        pthread_mutex_lock(&lock);
        shared_counter++; // Modifikasi aman, eksklusif 1 thread
        pthread_mutex_unlock(&lock);
        // --- AKHIR CRITICAL SECTION ---
    }
    return NULL;
}

int main() {
    pthread_t t1, t2;

    // Inisialisasi Mutex
    if (pthread_mutex_init(&lock, NULL) != 0) {
        printf("Inisialisasi mutex gagal\n");
        return 1;
    }

    pthread_create(&t1, NULL, increment_task, NULL);
    pthread_create(&t2, NULL, increment_task, NULL);

    pthread_join(t1, NULL);
    pthread_join(t2, NULL);

    pthread_mutex_destroy(&lock);

    printf("Ekspektasi Counter : %d\n", ITERATIONS * 2);
    printf("Hasil Riil Counter : %ld\n", shared_counter);

    if (shared_counter == ITERATIONS * 2) {
        printf("✅ SUKSES: Tidak terjadi Race Condition berkat Mutex!\n");
    } else {
        printf("❌ GAGAL: Terjadi Data Corruption Race Condition!\n");
    }
    return 0;
}
🔍 Penjelasan Baris Kunci:
  • pthread_mutex_lock(&lock): Thread meminta kunci. Jika kunci sedang dipegang thread lain, thread pemanggil diblokir tidur (sleep).
  • pthread_mutex_unlock(&lock): Melepas kunci dan membangunkan thread lain di antrean.
  • Tanpa mutex, operasi shared_counter++ yang terdiri dari 3 instruksi assembly (MOV, ADD, MOV) akan saling menimpa secara acak (data corruption).
📟 Expected Terminal Output:
$ gcc -o mutex mutex_counter.c -lpthread && ./mutex
Ekspektasi Counter : 2000000
Hasil Riil Counter : 2000000
✅ SUKSES: Tidak terjadi Race Condition berkat Mutex!

7. POSIX Counting Semaphore (`semaphore.c`)

RESOURCE COUNTING

Mengontrol akses terbatas terhadap kolam sumber daya (Resource Pool) yang hanya berkapasitas 2 slot serentak menggunakan sem_init, sem_wait, dan sem_post.

semaphore_pool.c
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <semaphore.h>
#include <unistd.h>

#define TOTAL_CLIENTS 5
#define MAX_CONCURRENT_SLOTS 2 // Hanya 2 thread boleh masuk bersamaan

sem_t slot_semaphore;

void* client_access(void* arg) {
    long id = (long)arg;
    printf("[CLIENT %ld] Mengantre menunggu izin slot...\n", id);

    // Operasi wait(S) / P(S): Mengurangi counter semaphore
    sem_wait(&slot_semaphore);

    printf("--> [CLIENT %ld] MENDAPATKAN SLOT! Sedang memakai layanan...\n", id);
    sleep(2); // Simulasi pemrosesan
    printf("<-- [CLIENT %ld] Selesai memakai. Melepaskan slot.\n", id);

    // Operasi signal(S) / V(S): Menaikkan counter semaphore
    sem_post(&slot_semaphore);

    return NULL;
}

int main() {
    pthread_t clients[TOTAL_CLIENTS];

    // Inisialisasi semaphore dengan nilai awal 2
    sem_init(&slot_semaphore, 0, MAX_CONCURRENT_SLOTS);

    for (long i = 0; i < TOTAL_CLIENTS; i++) {
        pthread_create(&clients[i], NULL, client_access, (void*)i);
    }

    for (int i = 0; i < TOTAL_CLIENTS; i++) {
        pthread_join(clients[i], NULL);
    }

    sem_destroy(&slot_semaphore);
    printf("[MAIN] Seluruh client selesai dilayani dengan tertib.\n");
    return 0;
}
🔍 Penjelasan Baris Kunci:
  • sem_init(&sem, 0, 2): Argumen kedua bernilai 0 berarti semaphore dibagi antar-thread dalam satu proses (bukan antar-proses). Argumen ketiga adalah kapasitas awal (2).
  • sem_wait(): Mengurangi slot. Jika slot bernilai 0, thread tertahan tidur.
  • sem_post(): Menambah slot dan membangunkan salah satu thread yang tertidur.
📟 Expected Terminal Output:
$ gcc -o sem semaphore_pool.c -lpthread && ./sem
[CLIENT 0] Mengantre menunggu izin slot...
--> [CLIENT 0] MENDAPATKAN SLOT! Sedang memakai layanan...
[CLIENT 1] Mengantre menunggu izin slot...
--> [CLIENT 1] MENDAPATKAN SLOT! Sedang memakai layanan...
[CLIENT 2] Mengantre menunggu izin slot...
<-- [CLIENT 0] Selesai memakai. Melepaskan slot.
--> [CLIENT 2] MENDAPATKAN SLOT! Sedang memakai layanan...

8. Producer-Consumer (Bounded Buffer) (`prod_cons.c`)

CLASSIC IPC CONCURRENCY

Solusi masalah klasik Bounded Buffer dengan 2 Semaphore (empty dan full) serta 1 Mutex untuk mencegah overflow dan underflow pada buffer berukuran tetap.

producer_consumer.c
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <semaphore.h>
#include <unistd.h>

#define BUFFER_SIZE 4
#define TOTAL_ITEMS 8

int buffer[BUFFER_SIZE];
int in = 0, out = 0;

sem_t sem_empty; // Menghitung ruang buffer kosong
sem_t sem_full;  // Menghitung jumlah item yang siap dibaca
pthread_mutex_t buffer_mutex;

void* producer(void* arg) {
    for (int i = 1; i <= TOTAL_ITEMS; i++) {
        sem_wait(&sem_empty); // Tunggu sampai ada ruang kosong
        pthread_mutex_lock(&buffer_mutex);

        buffer[in] = i * 10;
        printf("[PRODUCER] Menaruh item %d di slot buffer [%d]\n", buffer[in], in);
        in = (in + 1) % BUFFER_SIZE;

        pthread_mutex_unlock(&buffer_mutex);
        sem_post(&sem_full); // Kabarkan ada item baru

        usleep(100000); // Istirahat 100ms
    }
    return NULL;
}

void* consumer(void* arg) {
    for (int i = 1; i <= TOTAL_ITEMS; i++) {
        sem_wait(&sem_full); // Tunggu sampai ada item
        pthread_mutex_lock(&buffer_mutex);

        int item = buffer[out];
        printf("    [CONSUMER] Mengambil item %d dari slot [%d]\n", item, out);
        out = (out + 1) % BUFFER_SIZE;

        pthread_mutex_unlock(&buffer_mutex);
        sem_post(&sem_empty); // Kabarkan ada ruang kosong

        usleep(250000); // Consumer sedikit lebih lambat
    }
    return NULL;
}

int main() {
    pthread_t prod_thread, cons_thread;

    sem_init(&sem_empty, 0, BUFFER_SIZE); // Awal: semua 4 slot kosong
    sem_init(&sem_full, 0, 0);            // Awal: 0 item
    pthread_mutex_init(&buffer_mutex, NULL);

    pthread_create(&prod_thread, NULL, producer, NULL);
    pthread_create(&cons_thread, NULL, consumer, NULL);

    pthread_join(prod_thread, NULL);
    pthread_join(cons_thread, NULL);

    sem_destroy(&sem_empty);
    sem_destroy(&sem_full);
    pthread_mutex_destroy(&buffer_mutex);
    return 0;
}
🔍 Penjelasan Baris Kunci:
  • sem_empty: Bernilai 4 saat awal. Jika buffer penuh (0 slot kosong), producer terblokir.
  • sem_full: Bernilai 0 saat awal. Jika buffer kosong, consumer tertidur menunggu producer.
  • in = (in + 1) % BUFFER_SIZE: Pointer circular buffer untuk operasi efisien antrean FIFO.
📟 Expected Terminal Output:
$ gcc -o prod_cons producer_consumer.c -lpthread && ./prod_cons
[PRODUCER] Menaruh item 10 di slot buffer [0]
    [CONSUMER] Mengambil item 10 dari slot [0]
[PRODUCER] Menaruh item 20 di slot buffer [1]
[PRODUCER] Menaruh item 30 di slot buffer [2]
    [CONSUMER] Mengambil item 20 dari slot [1]

9. Readers-Writers Problem (`readers_writers.c`)

DATABASE ACCESS SYNC

Sinkronisasi akses dataset: Multiple Readers diizinkan membaca serentak, namun saat Writer masuk, seluruh proses lain dicegah masuk (eksklusif murni).

readers_writers.c
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>

pthread_mutex_t rw_mutex; // Kunci eksklusif untuk Penulis / Writer
pthread_mutex_t count_mutex; // Kunci untuk memproteksi variabel reader_count
int reader_count = 0;
int database_data = 100;

void* reader(void* arg) {
    long id = (long)arg;

    // Masuk reader
    pthread_mutex_lock(&count_mutex);
    reader_count++;
    if (reader_count == 1) {
        // Reader pertama mengunci penulis agar tidak bisa menulis saat membaca
        pthread_mutex_lock(&rw_mutex);
    }
    pthread_mutex_unlock(&count_mutex);

    // --- MEMBACA DATABASE BERSAMA ---
    printf("  [READER %ld] Membaca data bernilai: %d (Total Reader Aktif: %d)\n", id, database_data, reader_count);
    usleep(150000);

    // Keluar reader
    pthread_mutex_lock(&count_mutex);
    reader_count--;
    if (reader_count == 0) {
        // Reader terakhir melepaskan kunci penulis
        pthread_mutex_unlock(&rw_mutex);
    }
    pthread_mutex_unlock(&count_mutex);
    return NULL;
}

void* writer(void* arg) {
    long id = (long)arg;

    pthread_mutex_lock(&rw_mutex); // Kunci seluruh akses!
    database_data += 50;
    printf("--> [WRITER %ld] Mengubah nilai data menjadi: %d (EKSKLUSIF)\n", id, database_data);
    usleep(200000);
    pthread_mutex_unlock(&rw_mutex);

    return NULL;
}

int main() {
    pthread_t r[4], w[2];
    pthread_mutex_init(&rw_mutex, NULL);
    pthread_mutex_init(&count_mutex, NULL);

    pthread_create(&r[0], NULL, reader, (void*)1);
    pthread_create(&w[0], NULL, writer, (void*)1);
    pthread_create(&r[1], NULL, reader, (void*)2);
    pthread_create(&r[2], NULL, reader, (void*)3);
    pthread_create(&w[1], NULL, writer, (void*)2);
    pthread_create(&r[3], NULL, reader, (void*)4);

    for (int i = 0; i < 4; i++) pthread_join(r[i], NULL);
    for (int i = 0; i < 2; i++) pthread_join(w[i], NULL);

    pthread_mutex_destroy(&rw_mutex);
    pthread_mutex_destroy(&count_mutex);
    return 0;
}
🔍 Penjelasan Baris Kunci:
  • if (reader_count == 1) pthread_mutex_lock(&rw_mutex): Reader pertama menyalakan gembok untuk seluruh kawanan pembaca berikutnya.
  • if (reader_count == 0) pthread_mutex_unlock(&rw_mutex): Hanya saat pembaca terakhir keluar, pintu dibuka kembali untuk Penulis (Writer).
📟 Expected Terminal Output:
$ gcc -o rw readers_writers.c -lpthread && ./rw
  [READER 1] Membaca data bernilai: 100 (Total Reader Aktif: 1)
--> [WRITER 1] Mengubah nilai data menjadi: 150 (EKSKLUSIF)
  [READER 2] Membaca data bernilai: 150 (Total Reader Aktif: 2)
  [READER 3] Membaca data bernilai: 150 (Total Reader Aktif: 2)
--> [WRITER 2] Mengubah nilai data menjadi: 200 (EKSKLUSIF)

10. Dining Philosophers (Pencegahan Deadlock) (`philosophers.c`)

DEADLOCK PREVENTION

Implementasi masalah 5 Filsuf makan menggunakan strategi Resource Ordering (filsuf bernomor ganjil mengambil sumpit kanan terlebih dahulu) guna mematahkan kondisi Circular Wait secara matematis.

dining_philosophers.c
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>

#define NUM_PHILOSOPHERS 5

pthread_mutex_t chopsticks[NUM_PHILOSOPHERS];

void* philosopher(void* arg) {
    long id = (long)arg;
    int left_fork = id;
    int right_fork = (id + 1) % NUM_PHILOSOPHERS;

    // Berpikir sejenak
    printf("[FILSUF %ld] Sedang merenung dan berpikir...\n", id);
    usleep(100000);

    // STRATEGI PENCEGAHAN DEADLOCK:
    // Filsuf bernomor ganjil mengambil kanan dulu; genap mengambil kiri dulu
    if (id % 2 == 0) {
        pthread_mutex_lock(&chopsticks[left_fork]);
        pthread_mutex_lock(&chopsticks[right_fork]);
    } else {
        pthread_mutex_lock(&chopsticks[right_fork]);
        pthread_mutex_lock(&chopsticks[left_fork]);
    }

    // --- SESI MAKAN ---
    printf("🍝 [FILSUF %ld] Sedang makan lezat dengan sumpit (%d & %d)!\n", id, left_fork, right_fork);
    usleep(200000);

    // Melepaskan sumpit
    pthread_mutex_unlock(&chopsticks[left_fork]);
    pthread_mutex_unlock(&chopsticks[right_fork]);

    printf("[FILSUF %ld] Selesai makan, meletakkan kedua sumpit.\n", id);
    return NULL;
}

int main() {
    pthread_t phils[NUM_PHILOSOPHERS];

    for (int i = 0; i < NUM_PHILOSOPHERS; i++) {
        pthread_mutex_init(&chopsticks[i], NULL);
    }

    for (long i = 0; i < NUM_PHILOSOPHERS; i++) {
        pthread_create(&phils[i], NULL, philosopher, (void*)i);
    }

    for (int i = 0; i < NUM_PHILOSOPHERS; i++) {
        pthread_join(phils[i], NULL);
    }

    for (int i = 0; i < NUM_PHILOSOPHERS; i++) {
        pthread_mutex_destroy(&chopsticks[i]);
    }

    printf("✅ SUKSES: Seluruh 5 filsuf selesai makan tanpa terjebak Deadlock!\n");
    return 0;
}
🔍 Penjelasan Baris Kunci:
  • Jika semua filsuf mengambil sumpit kiri secara serempak, timbul Circular Wait di mana masing-masing menahan 1 sumpit dan menunggu sumpit kanan tetangga (Deadlock total).
  • Dengan membuat filsuf ganjil mengambil sumpit berurutan sebaliknya (kanan dulu baru kiri), rantai sirkular terputus dan minimal satu filsuf dipastikan berhasil meraih 2 sumpit sekaligus untuk makan.
📟 Expected Terminal Output:
$ gcc -o phils dining_philosophers.c -lpthread && ./phils
[FILSUF 0] Sedang merenung dan berpikir...
[FILSUF 1] Sedang merenung dan berpikir...
🍝 [FILSUF 0] Sedang makan lezat dengan sumpit (0 & 1)!
🍝 [FILSUF 2] Sedang makan lezat dengan sumpit (2 & 3)!
[FILSUF 0] Selesai makan, meletakkan kedua sumpit.
🍝 [FILSUF 1] Sedang makan lezat dengan sumpit (1 & 2)!
✅ SUKSES: Seluruh 5 filsuf selesai makan tanpa terjebak Deadlock!