Pemrograman C: Implementasi Semaphore dengan Busy-Waiting

Pada kesempatan ini kita akan memahami bagaimana semaphore bekerja. Perlu ditekankan bahwa tujuan penggunaan busy-waiting disini hanya untuk pembelajaran saja. Agar lebih mudah memahami bagaimana logika dibalik semaphore. Tentu sudah ada file header sungguhan untuk kebutuhan ini yaitu semaphore.h yang lebih efisien, menggunakan sistem penjadwalan dan tidak boros sumber daya CPU.

Memahami Semaphore di Sistem Operasi

Ada dua macam semaphore yaitu binary semaphore dan counting semaphore. Inti dari semaphore sebenarnya adalah sebuah variable dengan tipe integer yang digunakan bersama-sama antar thread dan bersifat atomic atau tidak boleh mengalami kondisi race. Semaphore berguna untuk sinkronisasi thread hingga antar proses. Tetapi kali ini kita hanya akan menyentuh pada tingkatan thread. Kita akan mencoba meniru fungsi-fungsi bawaan semaphore.h yaitu sem_init(), sem_wait() dan sem_post(), yang mana dalam hal ini menggunakan counting semaphore. Tetapi sebelumnya kita ketahui dulu pseudocode dari semaphore berdasarkan dari deskripsi di manual semaphore POSIX di https://man7.org/linux/man-pages/man3/sem_wait.3.html:

If the semaphore's value is greater than zero, then the decrement proceeds, and the function returns, immediately. If the semaphore currently has the value zero, then the call blocks until either it becomes possible to perform the decrement (i.e., the semaphore value rises above zero), or a signal handler interrupts the call.

  1. int counter = value; // shared 
  2.  
  3. wait() 
  4. { 
  5.     while (counter <= 0); // busy-waiting 
  6.     // baris berikut hanya akan dijalankan, atau menunggu 
  7.     // setelah variable counter memiliki nilai lebih dari 0 
  8.     counter--; 
  9. } 
  10.  
  11. signal() 
  12. { 
  13.    counter++; 
  14. } 

wait() dan signal() juga biasa dinotasikan sebagai P() dan V(), yang merupakan inisial dari istilah Belanda karena konsep semaphore awalnya dikemukakan oleh Edsger Wybe Dijkstra, seorang ilmuwan komputer berkebangsaan Belanda.

wait() digunakan untuk memasuki sesi kritis (critical section). value adalah nilai inisiasi yang diberikan untuk membatasi berapa jumlah thread yang boleh masuk critical section sekaligus. Nanti akan kita implementasikan di fungsi sem_init(). Setelah selesai di critical section, maka signal() harus dipanggil agar melepaskan spinlock pada thread lain yang sedang menunggu untuk masuk ke critical section (jika ada).

Berikut adalah prototype dari fungsi-fungsi semaphore yang akan kita buat:

  1. int sem_init( int value ); 
  2. int sem_wait( void ); 
  3. int sem_post( void ); 

Jika ingin latihan, sampai disini silahkan berhenti membaca dan buat implementasi dari prototype diatas. Jika sudah, bandingkan ketika menggunakan semaphore.h atau semaphore versi sendiri hasilnya harus sesuai.

Berikut adalah implementasi yang saya buat. Saya tidak menggunakan variable atomic tetapi menggunakan variable biasa yang didampingi oleh mutex agar tidak mengalami kondisi race.
  1. #include <pthread.h> 
  2. #include <stdio.h> 
  3. #include <stdlib.h> 
  4. #include <unistd.h> 
  5.  
  6. /* prototype, umumnya dideklarasikan di file header seperti semaphore.h */ 
  7. int sem_init( int value ); 
  8. int sem_wait( void ); 
  9. int sem_post( void ); 
  10.  
  11. /* variable global */ 
  12. int count = 2147483647; 
  13. pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; 
  14.  
  15. /* implementasi */ 
  16. int sem_init( int value ) 
  17. { 
  18.     count = value; 
  19.     return 0; 
  20. } 
  21.  
  22. int sem_wait( void ) 
  23. { 
  24.     while ( 1 ) { 
  25.         if ( pthread_mutex_lock( &mutex ) != 0 ) { 
  26.             return perror( "pthread_mutex_lock" ), 1; 
  27.         } 
  28.         if ( count > 0 ) { 
  29.             --count; 
  30.             if ( pthread_mutex_unlock( &mutex ) != 0 ) { 
  31.                 return perror( "pthread_mutex_unlock" ), 1; 
  32.             } 
  33.             return 0; 
  34.         } 
  35.         if ( pthread_mutex_unlock( &mutex ) != 0 ) { 
  36.             return perror( "pthread_mutex_unlock" ), 1; 
  37.         } 
  38.     } 
  39. } 
  40.  
  41. int sem_post( void ) 
  42. { 
  43.     if ( pthread_mutex_lock( &mutex ) != 0 ) { 
  44.         return perror( "pthread_mutex_lock" ), 1; 
  45.     } 
  46.     ++count; 
  47.     if ( pthread_mutex_unlock( &mutex ) != 0 ) { 
  48.         return perror( "pthread_mutex_unlock" ), 1; 
  49.     } 
  50.     return 0; 
  51. } 

Semoga bermanfaat!

#C #POSIX