eventfd normal mengubah seluruh counter yang bernilai nonzero menjadi satu hasil read lalu mereset counter ke nol. Saat dibuat dengan EFD_SEMAPHORE, hanya sisi read yang berubah: setiap read yang berhasil mengembalikan nilai 64-bit 1 dan mengurangi counter yang dikelola kernel sebanyak satu.
Perbedaan ini membuat beberapa unit yang dikumpulkan writer tetap dapat dikonsumsi secara terpisah. Objeknya tetap sebuah eventfd, dengan counter, aturan write, lifetime descriptor, dan integrasi readiness yang sama.
Counter tetap berupa objek 64-bit di kernel
eventfd() membuat objek yang berisi unsigned 64-bit counter. Argumen initval menetapkan nilai awal, sedangkan file descriptor yang dikembalikan mereferensikan objek tersebut.
int efd = eventfd(0, EFD_SEMAPHORE | EFD_NONBLOCK | EFD_CLOEXEC);Write memberikan tepat delapan byte berisi uint64_t. Nilai tersebut ditambahkan ke counter. EFD_SEMAPHORE tidak mengubah operasi ini.
uint64_t n = 3;
write(efd, &n, sizeof(n));Setelah write tersebut, counter yang bermula dari nol berisi tiga. Perbedaannya terlihat saat reader mengonsumsi state itu.
Semaphore mode mengonsumsi satu unit per read
Tanpa EFD_SEMAPHORE, read yang berhasil ketika counter nonzero mengembalikan nilai counter saat itu lalu meresetnya ke nol.
Dengan semaphore mode, read yang berhasil mengembalikan 1 dan mengurangi counter satu unit.
counter sebelum read nilai hasil read counter setelah read
3 1 2
2 1 1
1 1 0Buffer read tetap harus cukup untuk integer delapan byte. Buffer yang lebih kecil menghasilkan EINVAL; semaphore mode tidak mengubah descriptor menjadi byte stream.
Integer hasil read memakai host byte order, sama seperti read eventfd biasa.
Counter nol menentukan blocking dan readiness
Ketika counter bernilai nol, blocking read menunggu sampai counter menjadi nonzero. Jika descriptor memakai EFD_NONBLOCK, read yang sama gagal dengan EAGAIN.
Kondisi ini memberi batas readiness langsung. Descriptor readable ketika counter lebih besar dari nol. Dalam semaphore mode, satu read yang berhasil dapat membuat descriptor tetap readable karena counter mungkin masih di atas nol.
counter = 4
|
+-- read -> 1, counter = 3 [masih readable]
+-- read -> 1, counter = 2 [masih readable]
+-- read -> 1, counter = 1 [masih readable]
`-- read -> 1, counter = 0 [tidak readable]Perilaku tersebut penting pada loop epoll, poll, atau select. Satu notifikasi readiness tidak berarti satu read menguras seluruh unit yang telah terkumpul.
Write dapat menambah unit secara batch
Read bergaya semaphore tidak mewajibkan writer hanya menulis nilai satu. Writer dapat menambahkan nilai lebih besar dalam satu write selama masih memenuhi batas counter eventfd.
Write bernilai lima yang diikuti lima read semaphore akan mengonsumsi count yang sama satu unit demi satu unit. Batas batching di sisi producer terpisah dari batas konsumsi di sisi reader.
Sifat ini cocok ketika counter merepresentasikan token kerja yang tersedia atau count lain yang perlu diklaim satu unit oleh consumer. Namun, ini bukan message queue: lima unit tidak membawa lima payload terpisah, metadata ordering, atau identitas producer.
Nilai maksimum menyisakan batas khusus
Write normal hanya dapat menaikkan counter sampai satu kurang dari nilai maksimum unsigned 64-bit. Menulis UINT64_MAX secara langsung tidak valid dan gagal dengan EINVAL.
Jika penambahan nilai write akan melewati rentang counter yang diizinkan, blocking write menunggu sampai read menyediakan ruang yang cukup. Dengan EFD_NONBLOCK, operasi tersebut gagal dengan EAGAIN.
Semaphore mode tidak mengubah batas producer ini. Mode tersebut mengubah laju read dalam mengurangi counter, sehingga dapat memengaruhi seberapa cepat ruang tersedia bagi writer yang tertahan.
Beberapa reader berbagi satu counter
Descriptor duplikat yang mereferensikan objek eventfd yang sama berbagi counter yang sama. Kondisi serupa berlaku setelah fork() ketika descriptor yang diwariskan masih menunjuk objek tersebut.
Dengan EFD_SEMAPHORE, reader concurrent yang berhasil masing-masing mengonsumsi satu unit dari counter bersama. Kernel menjalankan operasi counter sebagai bagian dari read eventfd; aplikasi tidak memerlukan rangkaian load-and-decrement terpisah di userspace untuk counter tersebut.
Properti ini tidak memberi jaminan scheduling yang lebih luas. Interface tidak menjanjikan distribusi adil di antara thread atau process yang menunggu. Yang ditetapkan adalah konsumsi counter, bukan kebijakan fairness.
Readiness eventfd menyatu dengan loop berbasis descriptor
eventfd dapat dipantau melalui select, poll, dan epoll. Counter dapat dipakai sebagai sinyal sinkronisasi di dalam event loop bersama socket, timer, dan sumber lain yang direpresentasikan descriptor.
Semaphore mode mempertahankan integrasi ini sambil mengubah jumlah yang dikonsumsi satu read. Event loop yang ingin memproses seluruh unit yang tersedia dapat terus melakukan read sampai read() nonblocking mengembalikan EAGAIN. Loop yang sengaja hanya mengklaim satu unit dapat berhenti setelah satu read berhasil.
Keduanya merupakan kebijakan aplikasi di atas aturan readiness yang sama. Kernel mengekspos apakah counter nonzero; kernel tidak menentukan berapa unit yang harus dikonsumsi worker sebelum kembali memproses event lain.
Mode mengubah granularitas konsumsi, bukan identitas event
EFD_SEMAPHORE memiliki ruang lingkup sempit. Mode ini mengubah read yang menguras counter menjadi read satu unit. Mode tersebut tidak menempelkan payload pada unit, mengidentifikasi writer, mempertahankan batas tiap write, atau menyediakan kontrak fairness antarpembaca.
Batas ini membedakan eventfd dari IPC berorientasi message. State yang disimpan adalah sebuah count. Semaphore mode membuat count tersebut dapat dibagi antar-read, sehingga setiap read yang berhasil mengklaim satu unit sementara sisanya tetap tersedia melalui descriptor yang sama.