Request io_uring biasa memiliki siklus hidup sederhana: userspace mengirim satu SQE dan kemudian menerima satu CQE. Operasi multishot mengubah hubungan tersebut. Satu request yang dikirim dapat tetap aktif di kernel dan menghasilkan beberapa completion queue entry ketika event yang cocok terjadi.

Persistensi itu mengubah asumsi satu CQE per request menjadi protokol siklus hidup yang eksplisit. State penentunya dibawa oleh IORING_CQE_F_MORE: ketika flag ini ada, request asal masih dapat menghasilkan completion lain; ketika flag tidak ada, request multishot tersebut telah berakhir.

Satu submission dapat mewakili operasi yang terus aktif

Submission queue dan completion queue biasanya membentuk aliran banyak request:

SQE A ──► CQE A
SQE B ──► CQE B
SQE C ──► CQE C

Request multishot mengubah kardinalitasnya:

                 ┌── CQE 1 + IORING_CQE_F_MORE
satu SQE multishot├── CQE 2 + IORING_CQE_F_MORE
                 ├── CQE 3 + IORING_CQE_F_MORE
                 └── CQE 4

CQE terakhir tidak memiliki IORING_CQE_F_MORE. Ketiadaan flag itu menjadi batas siklus hidup. Userspace tidak boleh menyimpulkan bahwa request masih bertahan hanya dari hasil yang sukses atau dari jenis operasinya.

Model ini sesuai untuk operasi dengan sumber event yang secara alami berulang. Multishot accept dapat melaporkan banyak koneksi masuk. Multishot receive dapat melaporkan banyak kedatangan data. Multishot poll dapat terus melaporkan event readiness yang cocok. Setup dan batasan tepatnya berbeda menurut opcode, tetapi aturan siklus hidup completion tetap sama.

IORING_CQE_F_MORE menyatakan state request, bukan state payload

Sebuah CQE memuat hasil operasi dan flags. Pada request multishot, kedua field itu menjawab hal yang berbeda.

cqe->res menjelaskan hasil yang direpresentasikan oleh completion tersebut. Pada multishot accept, hasil sukses dapat berisi file descriptor yang diterima. Pada operasi receive, nilainya dapat merepresentasikan jumlah byte yang ditransfer. Pada poll, nilainya merepresentasikan event mask.

cqe->flags & IORING_CQE_F_MORE menjelaskan apakah CQE tambahan masih dapat datang dari request yang sama.

Completion loop karena itu harus memproses kedua dimensi tersebut:

struct io_uring_cqe *cqe;

while (io_uring_peek_cqe(&ring, &cqe) == 0) {
    bool active = cqe->flags & IORING_CQE_F_MORE;

    if (cqe->res < 0)
        handle_request_error(cqe->res);
    else
        handle_result(cqe);

    if (!active)
        mark_request_inactive(cqe->user_data);

    io_uring_cqe_seen(&ring, cqe);
}

Kode tersebut bersifat skematis, tetapi pemisahannya penting. Pemrosesan hasil dan tracking siklus hidup request saling berkaitan tanpa menjadi hal yang sama.

Multishot accept menghapus submission accept yang berulang

Alur asynchronous accept konvensional harus menyiapkan request accept baru setelah request sebelumnya selesai. Multishot accept mempertahankan operasi accept tetap aktif saat koneksi baru masuk.

Dengan liburing, setup-nya dapat dibuat ringkas:

struct io_uring_sqe *sqe = io_uring_get_sqe(&ring);

io_uring_prep_multishot_accept(
    sqe,
    listen_fd,
    NULL,
    NULL,
    0
);

sqe->user_data = ACCEPT_TOKEN;

Setiap koneksi yang diterima dapat menghasilkan CQE lain. Untuk io_uring_prep_multishot_accept(), cqe->res yang sukses berisi file descriptor yang dipasang. Aplikasi tetap bertanggung jawab atas pengelolaan resource untuk descriptor yang diterima tersebut.

Request persisten menghapus kebutuhan menyiapkan SQE accept berulang kali. Mekanisme ini tidak menghapus pemrosesan completion, kebijakan admission koneksi, pembersihan descriptor, atau kebutuhan mendeteksi berakhirnya request multishot.

Terminasi memerlukan submission baru jika layanan harus berlanjut

Request multishot bersifat persisten, bukan permanen. Error, cancellation eksplisit, atau kondisi terminasi khusus operasi dapat mengakhirinya. CQE terakhir tidak membawa IORING_CQE_F_MORE.

Server karena itu tidak dapat memperlakukan SQE awal sebagai registration yang hidup selamanya:

CQE dengan MORE
    ├── proses event
    └── pertahankan state request aktif

CQE tanpa MORE
    ├── proses hasil terakhir
    ├── tandai request tidak aktif
    └── kirim pengganti jika kebijakan memerlukan layanan berlanjut

Batas ini juga penting saat cancellation. Membatalkan operasi multishot mengakhiri request yang terus aktif; aplikasi harus memperhitungkan CQE cancellation secara terpisah dari terminal completion milik operasi multishot.

user_data umum dipakai untuk menghubungkan completion dengan state aplikasi. Karena beberapa CQE dapat membawa identitas request yang sama sepanjang waktu, state tersebut tidak dapat dilepas setelah completion sukses pertama.

Varian receive menambah batas siklus hidup buffer

Operasi multishot receive menggabungkan state request persisten dengan buffer selection. Multishot receive dapat mengambil buffer dari provided buffer group, dan setiap completion dapat mengidentifikasi buffer yang dipilih melalui CQE flags.

Kondisi ini menghasilkan dua siklus hidup independen:

siklus hidup request:
submit ──► CQE ──► CQE ──► CQE terminal

siklus hidup buffer:
tersedia ──► dipilih ──► dipakai aplikasi ──► dikembalikan ke pool

Request yang tetap aktif tidak membuat buffer yang sudah dikonsumsi otomatis dapat dipakai kembali. Userspace harus mengembalikan buffer sesuai mekanisme provided buffer sebelum buffer tersebut dapat melayani receive berikutnya.

Kehabisan buffer dapat mengakhiri multishot receive. Desain yang hanya melacak socket readiness tetapi mengabaikan pengisian ulang buffer dapat kehilangan persistensi yang diharapkan dari request awal.

Tekanan completion berpindah ke CQ

Mengurangi submission berulang tidak mengurangi jumlah event yang harus diamati aplikasi. Sumber multishot yang sibuk dapat menghasilkan CQE dengan cepat, sehingga kapasitas completion queue dan perilaku draining tetap menjadi bagian desain.

Ini merupakan titik tekanan yang berbeda dari overhead submission SQE:

oneshot:
event → kirim SQE → CQE → kirim SQE → CQE

multishot:
kirim SQE sekali → CQE → CQE → CQE → CQE
                         ↑ tekanan completion tetap ada

Aplikasi tetap memerlukan kapasitas CQ yang memadai dan konsumsi completion yang cukup cepat untuk workload-nya. Multishot mengubah perilaku re-arming request; mekanisme ini tidak mengubah event eksternal yang berulang menjadi satu completion.

State kernel persisten mengubah asumsi ownership

Request one-shot dapat mendorong desain yang mengikat state aplikasi ke satu completion yang diharapkan. Request multishot memerlukan interval ownership yang lebih panjang. State yang direferensikan melalui user_data, bookkeeping cancellation, dan resource khusus operasi dapat tetap relevan sepanjang banyak CQE.

Siklus hidup yang aman terikat pada terminal completion, bukan completion pertama:

alokasikan state request
kirim SQE multishot
        ├── CQE + MORE
        ├── CQE + MORE
        ├── CQE + MORE
        └── CQE terminal
        lepas atau aktifkan ulang state

Perbedaan itu merupakan batas utama yang dibawa multishot I/O. Submission-nya tunggal, completion-nya jamak, dan IORING_CQE_F_MORE memberi tahu userspace saat rangkaian di sisi kernel telah mencapai akhir.