Sebuah timerfd menjadi readable setelah timernya mengalami ekspirasi. Notifikasi ini bukan pemanggilan signal handler dan bukan pesan byte stream. Linux mencatat ekspirasi yang pending pada objek timer lalu mengekspos status tersebut melalui file descriptor, sehingga timer dapat berada pada batas readiness yang sama dengan socket, pipe, dan descriptor lain.

Interface ini lebih dari sekadar penggantian mekanisme notifikasi. Pilihan clock menetapkan domain waktu, flag arming menetapkan apakah deadline bersifat relatif atau absolut, dan setiap read yang berhasil melaporkan jumlah ekspirasi yang terakumulasi sejak read sukses sebelumnya atau konfigurasi ulang timer.

Pilihan clock menetapkan domain waktu timer

timerfd_create() membuat objek timer dan mengembalikan descriptor yang merujuk kepadanya. Argumen clockid memilih clock yang menjadi dasar pergerakan timer. CLOCK_MONOTONIC tidak dapat diatur dan tidak menghitung waktu saat sistem berada dalam suspend. CLOCK_BOOTTIME juga monotonic, tetapi menghitung waktu suspend.

CLOCK_REALTIME mengikuti wall clock sistem yang dapat diatur. Karena itu, timer absolut pada clock tersebut memiliki semantik berbeda dari deadline monotonic. Penyesuaian wall clock secara diskontinu dapat memindahkan nilai clock melewati deadline absolut, sedangkan clock monotonic tidak terpengaruh perubahan pengaturan wall clock.

Varian alarm, CLOCK_REALTIME_ALARM dan CLOCK_BOOTTIME_ALARM, dapat membangunkan sistem yang sedang suspend ketika pemanggil memiliki CAP_WAKE_ALARM. Properti itu melekat pada pilihan clock tersebut; pembuatan timerfd CLOCK_MONOTONIC biasa tidak menyiratkan perilaku wake-from-suspend.

Arming memisahkan delay relatif dari deadline absolut

timerfd_settime() memakai itimerspec yang berisi ekspirasi awal pada it_value dan interval pengulangan opsional pada it_interval. it_value bernilai nol menonaktifkan timer. Nilai bukan nol mengaktifkannya, sedangkan interval nol membuat timer one-shot.

Tanpa TFD_TIMER_ABSTIME, it_value ditafsirkan sebagai durasi relatif terhadap nilai clock yang dipilih pada saat pemanggilan. Dengan TFD_TIMER_ABSTIME, nilai tersebut ditafsirkan sebagai koordinat clock absolut.

struct itimerspec spec = {
    .it_value = { .tv_sec = 5, .tv_nsec = 0 },
    .it_interval = { .tv_sec = 1, .tv_nsec = 0 },
};

int fd = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC | TFD_NONBLOCK);
timerfd_settime(fd, 0, &spec, NULL);

Timer ini pertama kali mengalami ekspirasi lima detik setelah diaktifkan, lalu berulang dengan interval satu detik. Kernel dapat menjadwalkan eksekusi setelah titik ekspirasi; API merepresentasikan status ekspirasi, bukan janji bahwa kode userspace berjalan tepat pada deadline.

Deadline absolut berguna ketika rearming berulang perlu menargetkan koordinat clock tertentu alih-alih mengakumulasi delay relatif. Perilakunya tetap bergantung pada clock yang dipilih, sehingga deadline absolut CLOCK_REALTIME dan deadline absolut CLOCK_MONOTONIC bukan kontrak waktu yang dapat dipertukarkan.

Read mengembalikan jumlah ekspirasi yang terakumulasi

read() yang berhasil memberikan integer unsigned delapan byte dalam host byte order. Nilainya adalah jumlah ekspirasi yang terjadi sejak timer terakhir dikonfigurasi atau sejak read sukses sebelumnya.

uint64_t expirations;
ssize_t n = read(fd, &expirations, sizeof(expirations));

Untuk timer periodik, konsumsi yang terlambat tidak memerlukan satu record antrean per tick. Jika lima interval mengalami ekspirasi sebelum consumer melakukan read, counter yang dikembalikan dapat bernilai 5. Jumlah tersebut mempertahankan tick periodik yang terlewat sebagai agregat, tetapi tidak menyimpan timestamp terpisah untuk setiap ekspirasi.

Saat tidak ada ekspirasi pending, descriptor blocking akan menunggu. Dengan TFD_NONBLOCK, read yang sama gagal dengan EAGAIN. Buffer yang lebih kecil dari delapan byte menghasilkan EINVAL.

Agregasi ini penting pada kode event loop. Satu notifikasi readiness dapat mewakili beberapa interval timer, sehingga memperlakukan setiap event readable sebagai tepat satu periode yang berlalu akan membuang informasi yang sudah diberikan kernel.

Readiness mencerminkan status ekspirasi pending

select(), poll(), dan epoll() melaporkan timerfd sebagai readable ketika satu atau lebih ekspirasi pending. Read yang berhasil mengonsumsi jumlah ekspirasi yang terakumulasi. Jika belum ada ekspirasi berikutnya, descriptor tidak lagi readable.

Descriptor tersebut merepresentasikan status level:

ekspirasi pending = 0  -> tidak readable
ekspirasi pending > 0  -> readable
read berhasil           -> konsumsi jumlah pending

Batas ini menyatu dengan event loop karena status timer dan readiness I/O memakai interface multiplexing yang sama. Hal itu tidak membuat eksekusi timer sinkron dengan pemrosesan I/O. Latensi scheduler, descriptor lain yang ready, dan kerja aplikasi dapat menunda read setelah kernel menandai timerfd sebagai readable.

Salinan descriptor yang diwarisi melalui fork() merujuk ke objek timer yang sama. Read melalui descriptor tersebut mengonsumsi status ekspirasi dari objek bersama, bukan dari salinan timer independen. Ketika semua descriptor yang merujuk ke objek ditutup, timer dinonaktifkan dan resource kernel dilepas.

Pembatalan wall clock mengekspos perubahan clock diskontinu

Untuk timer absolut berbasis CLOCK_REALTIME atau CLOCK_REALTIME_ALARM, TFD_TIMER_CANCEL_ON_SET meminta pembatalan jika real-time clock mengalami perubahan diskontinu. Setelah perubahan tersebut, read yang sedang berlangsung atau read berikutnya dapat gagal dengan ECANCELED.

Error tersebut adalah transisi status clock yang dapat diamati, bukan ekspirasi timer biasa. Kode yang memakai cancel-on-set perlu membedakan ECANCELED dari EAGAIN dan dari read jumlah ekspirasi yang berhasil.

Flag ini hanya berlaku bersama TFD_TIMER_ABSTIME pada real-time clock yang didukung. Flag tersebut tidak mengubah timer monotonic menjadi detektor perubahan clock dan tidak berlaku pada timer real-time relatif.

Jumlah ekspirasi menetapkan batas interface

timerfd menggabungkan tiga kontrak terpisah: clock Linux yang dipilih, konfigurasi timer, dan readiness descriptor yang ditopang jumlah ekspirasi terakumulasi. File descriptor tidak membawa payload per ekspirasi, dan readiness tidak menjamin eksekusi userspace yang segera.

Batas mekanismenya presisi. Pergerakan waktu tetap merupakan properti clock, ekspirasi tetap merupakan properti timer, dan konsumsi menjadi I/O descriptor biasa. Event loop dapat memasukkan timer tanpa delivery signal asinkron sambil tetap mengamati kerja periodik yang tertunda melalui jumlah yang dikembalikan read().