Sebuah eventfd dapat menyerap beberapa write notifikasi sebelum userspace melayani descriptor. Kernel menyimpan write tersebut dalam counter 64-bit, sehingga readiness merepresentasikan state counter yang tertunda, bukan antrean dengan satu record untuk setiap notifikasi.

Perbedaan ini penting di event loop. Producer dapat menambah nilai saat consumer sedang sibuk, lalu read berikutnya dapat melebur state yang terakumulasi menjadi satu hasil. Dengan EFD_SEMAPHORE, objek yang sama menyediakan aturan konsumsi berbeda tanpa mengubah model readiness-nya.

Objeknya adalah counter di balik file descriptor

eventfd(initval, flags) membuat objek eventfd dan mengembalikan file descriptor yang merujuk kepadanya. Kernel menginisialisasi counter dari initval. Descriptor dapat dipantau oleh select(), poll(), dan epoll bersama socket serta sumber readiness lain.

Satu write memasok tepat satu nilai integer 8 byte. Kernel menambahkan nilai tersebut ke counter. Nilai UINT64_MAX ditolak, dan write userspace biasa tidak dapat menaikkan counter melampaui UINT64_MAX - 1.

uint64_t increment = 3;
ssize_t n = write(efd, &increment, sizeof(increment));

if (n != sizeof(increment)) {
    /* handle failure */
}

Jika penambahan akan melewati batas maksimum yang diizinkan, write blocking menunggu sampai sebuah read menyediakan kapasitas. Dengan EFD_NONBLOCK, kondisi yang sama gagal dengan EAGAIN.

Counter dengan demikian membawa kuantitas sekaligus state notifikasi. Menulis 3 tidak setara dengan menulis tiga byte ke pipe; operasi tersebut secara atomik menambahkan nilai numerik tiga ke counter eventfd.

Read default menguras nilai yang terakumulasi

Tanpa EFD_SEMAPHORE, read yang berhasil mengembalikan nilai counter nonzero saat itu sebagai satu integer 8 byte dalam byte order host, lalu mereset counter ke nol.

uint64_t pending;
ssize_t n = read(efd, &pending, sizeof(pending));

if (n == sizeof(pending)) {
    process_notifications(pending);
}

Misalkan producer menulis 2, 4, dan 1 sebelum consumer berjalan. Jika tidak ada read di antara ketiga write tersebut, counter mencapai 7. Read berikutnya dalam mode default mengembalikan 7, lalu menyisakan counter bernilai nol.

Ini adalah agregasi, bukan preservasi batas antar-producer. Consumer dapat memperoleh total numerik yang terkumpul, tetapi tidak dapat menentukan apakah total itu berasal dari satu write bernilai 7, tujuh write bernilai 1, atau kombinasi lain. Protokol yang memerlukan payload per event atau ordering membutuhkan kanal data terpisah.

Saat counter bernilai nol, read blocking menunggu sampai nilainya menjadi nonzero. Read nonblocking gagal dengan EAGAIN.

EFD_SEMAPHORE mengubah granularitas konsumsi

Membuat objek dengan EFD_SEMAPHORE mengubah read yang berhasil. Setiap read mengembalikan nilai 1 dan mengurangi counter satu, bukan mengembalikan seluruh counter lalu meresetnya.

int efd = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK | EFD_SEMAPHORE);

Jika counter bernilai 7, diperlukan tujuh read semaphore-mode yang berhasil untuk menurunkannya ke nol. Readiness tetap terkait dengan kondisi counter nonzero; flag tersebut mengubah seberapa banyak state yang dikonsumsi oleh satu read.

Mode ini dapat merepresentasikan unit pekerjaan yang tersedia atau permit ketika hitungan unit sudah memadai. Counter tetap tidak mengidentifikasi producer individual atau menempelkan metadata pada setiap unit. Yang direkam adalah kuantitas, bukan provenance.

Readiness mengikuti state dan kapasitas counter

Sebuah eventfd readable ketika counternya lebih besar dari nol. Descriptor writable ketika nilai sedikitnya 1 dapat ditambahkan tanpa blocking. Kondisi ini memungkinkan event loop bereaksi terhadap state notifikasi tertunda maupun saturasi counter.

Jalur write() userspace biasa tidak dapat membuat counter overflow karena penambahan yang akan melewati UINT64_MAX - 1 akan blocking atau menghasilkan EAGAIN. Linux mendokumentasikan kondisi overflow terpisah untuk signal post eventfd dari kernel; poll() melaporkan POLLERR untuk state luar biasa itu, dan read mengembalikan UINT64_MAX.

Kasus overflow dari sisi kernel tersebut berbeda dari aplikasi yang berulang kali memanggil write(). Menyamakan kedua jalur akan memberikan perilaku pada write userspace yang secara eksplisit dicegah oleh API.

Duplikasi descriptor mempertahankan objek bersama

Setelah fork(), salinan descriptor eventfd yang diwariskan merujuk ke objek eventfd yang sama. Duplikasi descriptor memiliki konsekuensi serupa: seluruh referensi berbagi satu counter, bukan membuat counter independen.

Karakter ini membuat eventfd cocok untuk jalur sinkronisasi lintas thread atau proses yang berkerabat, tetapi ownership tetap harus eksplisit. Read melalui referensi mana pun mengonsumsi state counter yang terlihat oleh seluruh referensi lain ke objek tersebut.

EFD_CLOEXEC menetapkan FD_CLOEXEC saat pembuatan dan menghindari langkah fcntl() terpisah yang rentan race ketika descriptor tidak boleh bertahan melewati execve(). EFD_NONBLOCK dengan cara serupa menetapkan status nonblocking saat pembuatan.

Hitungan notifikasi dan state pekerjaan adalah konsep terpisah

Counter eventfd sering dipakai sebagai jembatan wakeup untuk struktur data lain. Dalam desain tersebut, counter memberi sinyal bahwa pekerjaan mungkin tersedia, sedangkan queue atau ring memuat record sebenarnya. Nilai numerik eventfd tidak otomatis sama dengan jumlah record yang saat itu dapat dikonsumsi.

Batas ini terutama penting ketika subsystem lain mengirim notifikasi eventfd dengan semantik batching. Sebuah interface, misalnya, dapat memakai eventfd hanya sebagai petunjuk untuk memeriksa state completion miliknya sendiri. Correctness kemudian berasal dari menguras queue atau ring yang menjadi sumber otoritatif, bukan dari asumsi relasi satu-banding-satu antara increment eventfd dan objek aplikasi.

eventfd menyediakan counter ringkas yang dipelihara kernel dan ikut dalam descriptor readiness. Read default menukar agregasi dengan satu operasi drain; EFD_SEMAPHORE mempertahankan konsumsi unit demi unit. Kedua mode mengekspos batas inti yang sama: objek merekam state numerik yang tertunda, bukan log event berurutan.