io_uring decouples request preparation, submission, execution, and result retrieval via shared Submission Queue (SQ) and Completion Queue (CQ). It allows amortizing overhead through batching and unifies handling of various asynchronous operations; whether it is actually faster depends on the workload, device, queue depth, and implementation.
The basic framework was introduced in Linux 5.1, with subsequent additions of operations and features. Do not assume that all opcodes, flag combinations, and filesystem paths are supported just because the kernel version is higher than 5.1. Practical applications should probe capabilities and handle failures during creation or request submission.
SQ and CQ Carry Control Information
The application prepares SQEs, filling in opcode, fd, buffer address, length, offset, and user_data, among other fields; the kernel consumes these to execute them and returns results such as user_data, res, and flags via CQEs. Sharing ring queues reduces the copying of control information back and forth, but it does not mean that data payloads automatically become zero-copy.
Userspace and the kernel advance their respective production and consumption indices, requiring correct memory barriers. This is typically managed by liburing to avoid manually manipulating head/tail with plain integers. When multiple application threads operate on the same ring concurrently, they must also adhere to the concurrency contracts of the library and the mode used. io_uring(7)
Four Results That Must Not Be Confused
Stage
Returned Result
What is NOT yet guaranteed
get_sqe
Obtains a writable SQE, or NULL
I/O has not started
submit
Submission result or negative error code
Does not mean the request itself succeeded
wait_cqe
Obtains a completion record, or waits for interface error
Obtaining a record does not mean res indicates success
CQE res
The operation result; negative errno on failure
Successful read/write may still be a short transfer
When io_uring_wait_cqe returns 0, you should check cqe->res again; if res is -ENOENT, the error comes from that specific operation and cannot be inferred from the current thread's errno. A read operation res of 0 usually indicates EOF, while a positive value is the actual number of bytes read. wait_cqe, prep_read
A Complete Single-Shot Read Program
Save as uring_read.c. The program reads at most 4096 bytes from the specified file starting at offset 0, outputting the actual byte count; it is not a loop to read the entire file. Fatal errors during initialization or waiting will terminate this demo process; long-running services need to implement independent cancellation and reclamation paths.
cc -Wall -Wextra uring_read.c -luring -o uring_read./uring_read input.bin
The buffer remains alive after submission until the CQE for this single-shot request arrives. After cqe_seen, the original CQE slot can be reused, so the program saves res and user_data first. The tag is an application-associated key; the kernel does not increment reference counts for objects pointed to by tags.
Explicit file offsets avoid contention on implicit file positions among multiple concurrent requests. To read a file completely, you should adjust the offset and remaining length based on the actual returned length, and handle EOF, errors, and cancellation; do not treat every short read as a failure.
SQ is empty; can buffers be reused?
Prepare and submit two requests: SQ empties. Prepare a third and all buffers now have owners. Complete R2 before R1; CQ follows completion order, while IDs retain association. The model returns buffers only after completion consumption, not when SQ slots become free.
Preparing the visual
SQ is empty; can buffers be reused?
SQ slots, in-flight requests and buffer ownership are separate accounts. SQ consumption frees a slot while the request can still need its buffer.
{"id":"uring-slot-request-lifetime","title":"SQ is empty; can buffers be reused?","summary":"SQ slots, in-flight requests and buffer ownership are separate accounts. SQ consumption frees a slot while the request can still need its buffer.","height":1000,"html":"<h2 data-i18n=\"heading\"></h2><p class=\"intro\" data-i18n=\"intro\"></p><div class=\"actions\"><button id=\"prepare\" data-i18n=\"prepare\"></button><button id=\"submit\" data-i18n=\"submit\"></button><button id=\"reap\" data-i18n=\"reap\"></button><button id=\"reset\" data-i18n=\"reset\"></button></div><div class=\"track-row\"><span data-i18n=\"sq\"></span><div id=\"sq\" class=\"track\"></div></div><p class=\"hint\" data-i18n=\"buffers\"></p><div id=\"buffers\" class=\"buffers\"></div><div class=\"track-row\"><span data-i18n=\"cq\"></span><div id=\"cq\" class=\"track\"></div></div><div class=\"metrics\"><div><span data-i18n=\"free\"></span><strong id=\"free\"></strong></div><div><span data-i18n=\"inflight\"></span><strong id=\"inflight\"></strong></div></div><details class=\"scope\"><summary data-i18n=\"scopeLabel\"></summary><p data-i18n=\"scope\"></p></details>","css":".intro{margin:8px 0 18px;color:var(--muted);font-size:14px}.caption,.hint{font-size:12px;color:var(--muted);margin-top:12px}.control{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:6px 12px;align-items:center;margin:14px 0;font-size:13px}.control label{font-size:13px}.control output{color:var(--accent);min-width:5em;text-align:right;font-variant-numeric:tabular-nums}.control input{grid-column:1/-1;width:100%;margin:0}.presets{display:flex;gap:4px;align-items:stretch}.presets button{flex:1 1 0;min-width:0;overflow-wrap:anywhere;font-size:13px}.actions{display:flex;gap:8px;flex-wrap:wrap;margin:14px 0}.actions button{flex:1;min-width:0;font-size:13px;min-height:42px}.copy{display:grid;font-size:14px;margin:16px 0}.copy>*{grid-area:1/1;margin:0}.reserve{visibility:hidden}.scope{margin-top:14px;color:var(--muted);font-size:12px}.scope summary{padding:8px 0;cursor:pointer}.scope p{margin-top:10px}.two{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:12px;margin:16px 0}.card{border:1px solid var(--rule);border-radius:9px;padding:14px;min-width:0}.card header{font-size:14px;font-weight:600;margin-bottom:10px}.card button{font-size:12px;width:100%;min-width:0}.card small{display:block;font-size:12px;color:var(--muted)}.card code{font-size:12px;overflow-wrap:anywhere}.metrics{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:12px;margin:16px 0}.metrics>div{border-left:2px solid var(--accent);padding-left:10px;min-width:0}.metrics span{display:block;min-height:3.2em;color:var(--muted);font-size:12px}.metrics strong{font-size:23px;display:block;font-weight:500;overflow-wrap:anywhere;font-variant-numeric:tabular-nums}.formula{border-top:1px solid var(--rule);padding-top:12px;margin-top:14px;font-size:13px;font-variant-numeric:tabular-nums;overflow-wrap:anywhere}.value{font:500 20px var(--viz-font-ui)}.legend{display:flex;flex-wrap:wrap;gap:6px 14px;font-size:12px;color:var(--muted);margin:12px 0}.legend span:before{content:'';display:inline-block;width:8px;height:8px;border-radius:2px;background:var(--accent);margin-right:6px}.legend .second:before{background:var(--second)}.check{display:flex;align-items:center;gap:8px;font-size:13px;margin:12px 0}.check input{width:16px;height:16px;flex-shrink:0;accent-color:var(--accent)}select,input[type=text],input[type=number]{background:var(--paper);color:var(--ink);font:inherit;font-size:13px;padding:10px;border:1px solid var(--rule);border-radius:7px;max-width:100%;min-width:0}select{width:100%;min-height:42px}.band{height:20px;display:flex;background:var(--surface);border-radius:4px;overflow:hidden}.band i{height:100%;display:block;background:var(--accent);transition:width .25s}.band i.second{background:var(--second)}button:disabled{cursor:default}.status{font-size:13px;min-height:4.8em}.mono{font-family:var(--viz-font-mono);font-variant-numeric:tabular-nums}.muted{color:var(--muted)}\n@media(max-width:480px){.two{gap:8px}.card{padding:12px 9px}.actions button,.presets button{font-size:12px}.metrics strong{font-size:20px}.metrics{gap:8px}}\n@media(prefers-reduced-motion:reduce){*,*::before,*::after{transition:none!important;animation:none!important}}\n.track-row{margin:16px 0}.track-row>span{display:block;font-size:12px;color:var(--muted);margin-bottom:7px}.track{display:flex;gap:5px;min-width:0}.cell{flex:1;min-width:0;min-height:48px;border:1px solid var(--rule);border-radius:5px;background:var(--surface);display:flex;align-items:center;justify-content:center;text-align:center;padding:5px 3px;font-size:12px;overflow-wrap:anywhere;font-variant-numeric:tabular-nums}.cell.on{background:var(--accent-soft);border-color:var(--accent)}.cell.gold{background:var(--second-soft);border-color:var(--second)}.cell.empty{border-style:dashed;color:var(--muted)}.cell.error{border-color:var(--second);text-decoration:line-through}.choices{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:6px;margin:14px 0}.choices button{min-width:0;padding:10px 4px;font-size:12px;overflow-wrap:anywhere}.choices button[aria-current=true]{border-color:var(--accent);background:var(--accent-soft)}.relation{display:grid;grid-template-columns:minmax(0,1fr) 28px minmax(0,1fr);align-items:center;gap:6px;margin:12px 0}.relation .cell{min-height:72px}.relation>i{text-align:center;font-style:normal;color:var(--accent)}.readout{font-size:13px;white-space:pre-line;min-height:6.4em;overflow-wrap:anywhere}.plot{position:relative;border-left:1px solid var(--rule);border-bottom:1px solid var(--rule);padding:8px 0;margin:18px 0}.plot-row{height:28px;position:relative;margin:4px 0;background:var(--surface)}.plot-row i{position:absolute;inset-block:5px;background:var(--accent);min-width:2px;transition:width .2s,left .2s}.plot-row b{position:relative;padding:4px 6px;font:11px/28px var(--viz-font-ui);background:var(--paper);border-radius:3px}.axis{display:flex;justify-content:space-between;font-size:11px;color:var(--muted)}\n\n/* Action labels must remain readable across locales and narrow embeds. */\n.actions{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:8px}\n.actions button{overflow-wrap:anywhere;white-space:normal;min-height:58px;padding:8px}\nbutton{overflow-wrap:anywhere}\n@media(min-width:640px){.actions{grid-template-columns:repeat(3,minmax(0,1fr))}}\n\n.metrics span{overflow-wrap:anywhere}\n.panel{border:1px solid var(--rule);border-radius:9px;padding:12px;margin:14px 0;min-width:0}.panel h3{font-size:13px;margin:0 0 10px;color:var(--muted)}.source{font:13px/1.8 var(--viz-font-mono);white-space:pre-wrap;overflow-wrap:anywhere;margin:12px 0}.source mark{background:var(--accent-soft);color:var(--ink);border-bottom:2px solid var(--accent)}.source .fault{background:var(--second-soft);border-color:var(--second)}.diagram{display:block;width:100%;height:auto;max-height:320px;margin:14px 0;overflow:visible}.diagram text{fill:var(--ink);font:12px var(--viz-font-ui)}.diagram .edge{stroke:var(--muted);stroke-width:1.5;fill:none}.diagram .edge.on{stroke:var(--accent);stroke-width:3}.diagram .node{fill:var(--surface);stroke:var(--rule);stroke-width:1.5}.diagram .node.on{fill:var(--accent-soft);stroke:var(--accent)}.diagram .node.gold{fill:var(--second-soft);stroke:var(--second)}.log{font:12px/1.8 var(--viz-font-mono);white-space:pre-line;overflow-wrap:anywhere;min-height:7.2em}.table{display:grid;gap:5px;margin:14px 0}.table .tr{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:5px}.table .tr>*{padding:8px 5px;background:var(--surface);border-radius:4px;min-width:0;overflow-wrap:anywhere;font-size:12px}.table .tr .on{background:var(--accent-soft)}.table .tr .gold{background:var(--second-soft)}.choices button{min-height:44px}.metrics strong{min-height:1.6em}.code-line{display:block}.code-line.on{background:var(--accent-soft);border-left:2px solid var(--accent);padding-left:6px}.status-badge{font-size:12px;color:var(--accent)}.tile-grid{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:6px}.tile-grid .cell{min-height:40px}.actions button{min-height:48px}.form-field{display:grid;gap:8px;margin:14px 0;font-size:13px}.form-field input{width:100%;font-family:var(--viz-font-ui)}.metrics.three{grid-template-columns:repeat(3,minmax(0,1fr))}.formula{min-height:3.2em}.presets{flex-wrap:wrap}.presets button{flex-basis:25%;min-height:44px}\n\n.budget{display:flex;height:28px;margin:16px 0;background:var(--surface);border-radius:5px;overflow:hidden}.budget i{display:block;background:var(--accent);transition:width .2s}.budget i.gold{background:var(--second)}.intervals{margin:16px 0}.interval{position:relative;height:36px;background:var(--surface);margin:6px 0;border-radius:4px;overflow:hidden}.interval i{position:absolute;height:100%;background:var(--accent-soft);border-left:2px solid var(--accent)}.interval i.gold{background:var(--second-soft);border-color:var(--second)}.interval span{position:relative;z-index:1;font:12px/36px var(--viz-font-ui);padding-left:7px}.table .tr>*{min-height:4.8em}.formula{white-space:pre-line;min-height:7.2em}.actions:has(>button:only-child){grid-template-columns:1fr}.metrics strong{min-height:3.2em}.rangegrid{display:grid;grid-template-columns:repeat(12,minmax(0,1fr));gap:3px;margin:14px 0}.rangegrid .cell{min-height:30px;padding:3px 1px;font-size:10px}.message{min-height:7.2em;white-space:pre-line;font:13px/1.8 var(--viz-font-ui);overflow-wrap:anywhere}\n/* Allow CJK hanging punctuation without overflowing a metric's border box. */\n.metrics>div{padding-right:4px}.metrics span{line-break:anywhere}\n\n.buffers{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:8px}.buffers button{min-width:0;min-height:100px;font-size:12px;white-space:pre-line;overflow-wrap:anywhere}#sq{display:grid;grid-template-columns:repeat(2,minmax(0,1fr))}#cq{display:grid;grid-template-columns:repeat(3,minmax(0,1fr))}","js":"const $=s=>document.querySelector(s);const set=(id,v)=>$('#'+id).textContent=v;const t=k=>viz.t(k);function cells(id,values){$('#'+id).replaceChildren(...values.map(v=>{const e=document.createElement('div');e.className='cell '+(v.cls||'');e.textContent=v.text;e.title=v.title||v.text;return e;}));}function pressed(mode){document.querySelectorAll('[data-mode]').forEach(e=>e.setAttribute('aria-pressed',String(e.dataset.mode===mode)));}const esc=s=>String(s).replace(/[&<>\"']/g,c=>({'&':'&','<':'<','>':'>','\"':'"',\"'\":'''}[c]));const fmt=s=>'{'+[...s].sort().join(', ')+'}';function graph(id,nodes,edges){const el=$('#'+id);el.setAttribute('viewBox','0 0 360 200');el.innerHTML='<defs><marker id=\"arrow-'+id+'\" viewBox=\"0 0 10 10\" refX=\"8\" refY=\"5\" markerWidth=\"5\" markerHeight=\"5\" orient=\"auto-start-reverse\"><path d=\"M 0 0 L 10 5 L 0 10 z\" fill=\"var(--muted)\"/></marker></defs>'+edges.map(e=>{const a=nodes[e[0]],b=nodes[e[1]],dx=b.x-a.x,dy=b.y-a.y,d=Math.hypot(dx,dy)||1;if(edges.some(r=>r[0]===e[1]&&r[1]===e[0])){const nx=-dy/d,ny=dx/d;return '<path class=\"edge '+(e[2]?'on':'')+'\" d=\"M '+(a.x+dx/d*22+nx*12)+' '+(a.y+dy/d*22+ny*12)+' Q '+((a.x+b.x)/2+nx*38)+' '+((a.y+b.y)/2+ny*38)+' '+(b.x-dx/d*25+nx*12)+' '+(b.y-dy/d*25+ny*12)+'\" marker-end=\"url(#arrow-'+id+')\"/>';}return '<line class=\"edge '+(e[2]?'on':'')+'\" x1=\"'+(a.x+dx/d*25)+'\" y1=\"'+(a.y+dy/d*25)+'\" x2=\"'+(b.x-dx/d*28)+'\" y2=\"'+(b.y-dy/d*28)+'\" marker-end=\"url(#arrow-'+id+')\"/>';}).join('')+nodes.map(n=>'<g><circle class=\"node '+(n.cls||'')+'\" cx=\"'+n.x+'\" cy=\"'+n.y+'\" r=\"25\"/><text x=\"'+n.x+'\" y=\"'+(n.y+4)+'\" text-anchor=\"middle\">'+esc(n.name)+'</text>'+(n.sub?'<text x=\"'+n.x+'\" y=\"'+(n.y+43)+'\" text-anchor=\"middle\">'+esc(n.sub)+'</text>':'')+'</g>').join('');}function table(id,rows){$('#'+id).classList.add('table');$('#'+id).replaceChildren(...rows.map(r=>{const row=document.createElement('div');row.className='tr';r.forEach(x=>{const c=document.createElement('div');if(typeof x==='object'){c.textContent=x.text;c.className=x.cls||'';}else c.textContent=x;row.append(c)});return row;}));}function band(id,parts,total){$('#'+id).replaceChildren(...parts.map((p,i)=>{const e=document.createElement('i');e.style.width=(100*p.value/total)+'%';e.className=i%2?'gold':'on';e.title=p.name+': '+p.value;return e}));}function intervals(id,items,total){$('#'+id).replaceChildren(...items.map(p=>{const row=document.createElement('div');row.className='interval';const bar=document.createElement('i');bar.style.left=(100*p.start/total)+'%';bar.style.width=(100*(p.end-p.start)/total)+'%';bar.className=p.cls||'';const label=document.createElement('span');label.textContent=p.label;row.append(bar,label);return row;}));}let slots=[null,null,null],sq=[],cq=[],serial=0;for(let i=0;i<3;i++){const e=document.createElement('button');e.dataset.buffer=i;e.onclick=()=>{slots[i].state='done';cq.push(i);draw()};$('#buffers').append(e)}function draw(){cells('sq',Array.from({length:2},(_,i)=>({text:sq[i]!==undefined?'R'+slots[sq[i]].id:'—',cls:sq[i]!==undefined?'on':'empty'})));cells('cq',Array.from({length:3},(_,i)=>({text:cq[i]!==undefined?'R'+slots[cq[i]].id+' / 4 B':'—',cls:cq[i]!==undefined?'gold':'empty'})));document.querySelectorAll('[data-buffer]').forEach(e=>{const i=+e.dataset.buffer,r=slots[i];e.textContent='B'+i+'\\n'+(r?'R'+r.id+'\\n'+t(r.state):t('idle'));e.disabled=!r||r.state!=='running';});set('free',slots.filter(x=>!x).length);set('inflight',slots.filter(Boolean).length);$('#prepare').disabled=sq.length===2||slots.every(Boolean);$('#submit').disabled=!sq.length;$('#reap').disabled=!cq.length;$('#buffers').dataset.state=JSON.stringify({slots,sq,cq});}$('#prepare').onclick=()=>{const i=slots.indexOf(null);slots[i]={id:++serial,state:'prepared'};sq.push(i);draw()};$('#submit').onclick=()=>{sq.forEach(i=>slots[i].state='running');sq=[];draw()};$('#reap').onclick=()=>{slots[cq.shift()]=null;draw()};$('#reset').onclick=()=>{slots=[null,null,null];sq=[];cq=[];serial=0;draw()};draw();","audio":false,"strings":{"buffers":"Buffer pool: click an in-flight request to complete it","complete":"Complete","cq":"CQE arrival order","done":"CQE awaiting consumption","free":"Free buffers","heading":"SQ is empty; can buffers be reused?","idle":"Free","inflight":"Buffers owned by requests","intro":"Prepare, submit, complete in any order, then consume CQEs.","prepare":"Prepare request","prepared":"Prepared","reap":"Consume oldest CQE","reset":"Reset","running":"In flight","scope":"SQ capacity two, three buffers, ordinary one-shot reads returning four bytes. Buffers returned when CQEs are consumed; no multishot, overflow or zero-copy notifications.","scopeLabel":"Model scope and assumptions","sq":"SQ slots","submit":"Submit and consume SQ"}}
Completion Order and Dependencies
Standard independent requests may complete out of order; you cannot directly match them to business objects by submission queue index. user_data usually encodes request ID, type, or object generation to prevent old completions from landing on reused objects.
When sequential dependencies are needed, use mechanisms like link and drain according to semantics, but they are not database transactions. Side effects already produced by a previous write will not automatically roll back just because a subsequent request fails. For write persistence, you also need to submit appropriate synchronization operations and observe their results.
SQPOLL and IOPOLL Are Two Different Directions
SQPOLL has a kernel thread poll the submission queue. The application still needs to publish prepared SQEs to the queue; when using liburing, you still call the appropriate submit path, and cannot just get_sqe/prep and do nothing. When the polling thread sleeps and sets NEED_WAKEUP, the library wakes it up as needed. io_uring_submit
IOPOLL, on the other hand, polls for completion of supported I/O, depending on device, filesystem, and access mode conditions. The two can be discussed separately; do not treat "polling submission" and "polling device completion" as the same feature.
Continuous polling can significantly consume CPU, not just a fixed 5%–10% of one core. DEFER_TASKRUN defers the corresponding work to the specified path for entering the kernel to collect events, requiring constraints like SINGLE_ISSUER and correct event advancement methods; it is not simply "disabling interrupts to become faster." Advanced flags should be checked against version compatibility item by item. liburing setup docs
Registered Resources: Prepaying Some Management Costs
Registering buffers can reduce the work of repeatedly pinning user pages in eligible paths, but consumes resources and incurs registration/unregistration costs. When using them, you must select the corresponding fixed-buffer operations and indices; registering once does not mean all standard reads automatically enjoy the same path. register_buffers
Registering files establishes a file table for the ring; when using IOSQE_FIXED_FILE, the corresponding value in the SQE is an index within this table, not a standard process fd. Updates, unregistration, and their relationship with in-flight requests must be handled according to the interface protocol. register_files
Buffers, descriptor objects, and request states all have lifecycles. You cannot free(buffer) just because submission succeeded, nor can you immediately reuse an old request_id just because a cancellation request has been submitted.
Cancellation and Timeouts: Observe the Termination of the Original Request
A cancellation operation is another request and will have its own completion result. It may succeed, find that the target has already completed, or fail to cancel immediately; the completion record of the original operation still needs to be handled according to the interface. Applications should track both the "cancellation requested" and "original request terminated" states. prep_cancel
link timeout can limit the wait time for associated operations, but it does not automatically establish timeout isolation for the entire ring. An independent slow request usually does not naturally block all other independent requests from completing; whether there is a dependency chain is determined by the submission method.
Standard one-off operations often correspond to one CQE, but multishot accept or recv can produce multiple; you should check IORING_CQE_F_MORE to determine if the request will continue. Some zero-copy sends require additional notifications to determine when data buffers can be reused; do not treat all CQEs as the final point for resource reclamation. Multishot accept
Cancellation Races: Record Both Results Separately
The following table is limited to canceling one standard single-shot request by unique user_data, without using flags to cancel all matching items. C is the cancellation request, R is the original request; cancellation itself does not roll back external side effects already completed by R. Cancellation Results
C's Result
Can be interpreted as
Handling of R
0
Found and successfully canceled
Still collect and process R's completion, do not treat C as R
-ENOENT
Target not found, may have already completed or ID is incorrect
Check if R has been settled, troubleshoot tags, do not directly double-free
-EALREADY
Advanced to a stage where it cannot be canceled immediately
Continue waiting for R to terminate, keep its required resources
Other errors
Cancellation request itself failed
Proceed or terminate session based on R's state
If the business timeout means you simply no longer need the result, you can mark R as "result discard," but its I/O lifecycle still needs to complete. Business state end and kernel access buffer end are not the same thing; this is also why asynchronous interfaces must clearly define ownership relationships.
Multishot: Separate Individual Results from Request Termination
Below is the offline CQE model, where MORE represents the boolean value corresponding to IORING_CQE_F_MORE, not reading the actual ring. Two successful accepts yield two fds, and the final cancellation completion ends this multishot request. The example only records numbers, not creating or closing real sockets.
importerrnoactive=Trueaccepted=[]terminal=Nonecqes=[(101,True),(102,True),(-errno.ECANCELED,False)]forresult,moreincqes:assertactiveifresult>=0:accepted.append(result)ifnotmore:active=Falseterminal=resultassertaccepted==[101,102]assertnotactiveandterminal==-errno.ECANCELED# Successful results can also be the last one; do not use result >= 0 to judge if still active.
last_success, more=103, Falseassertlast_success>=0andnotmore
The successful result of a real accept is the connection resource already obtained, which needs to be handed over to connection management or closed; request termination does not automatically close previously accepted connections. To judge whether to continue waiting for subsequent CQEs of this request, check MORE, not just the sign of res. Multishot accept
Cancel completion is not original completion
Consume only C: retain the buffer. R settles the original request. If read wins, its four-byte result is not undone by later cancellation. Consumption order illustrates independent accounting, not guaranteed CQE ordering on every kernel.
Preparing the visual
Cancel completion is not original completion
Cancellation and the original operation each have a result. Even cancel res=0 requires processing the original completion; an earlier success can leave cancel with ENOENT.
{"id":"uring-cancel-two-results","title":"Cancel completion is not original completion","summary":"Cancellation and the original operation each have a result. Even cancel res=0 requires processing the original completion; an earlier success can leave cancel with ENOENT.","height":1000,"html":"<h2 data-i18n=\"heading\"></h2><p class=\"intro\" data-i18n=\"intro\"></p><div class=\"presets\" role=\"group\" data-i18n-label=\"winner\"><button data-mode=\"cancel\" data-i18n=\"cancel\"></button><button data-mode=\"success\" data-i18n=\"success\"></button></div><section class=\"panel\"><h3 data-i18n=\"results\"></h3><div id=\"results\"></div></section><div class=\"actions\"><button id=\"reapC\" data-i18n=\"reapC\"></button><button id=\"reapR\" data-i18n=\"reapR\"></button><button id=\"reset\" data-i18n=\"reset\"></button></div><div class=\"metrics\"><div><span data-i18n=\"buffer\"></span><strong id=\"buffer\"></strong></div></div><details class=\"scope\"><summary data-i18n=\"scopeLabel\"></summary><p data-i18n=\"scope\"></p></details>","css":".intro{margin:8px 0 18px;color:var(--muted);font-size:14px}.caption,.hint{font-size:12px;color:var(--muted);margin-top:12px}.control{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:6px 12px;align-items:center;margin:14px 0;font-size:13px}.control label{font-size:13px}.control output{color:var(--accent);min-width:5em;text-align:right;font-variant-numeric:tabular-nums}.control input{grid-column:1/-1;width:100%;margin:0}.presets{display:flex;gap:4px;align-items:stretch}.presets button{flex:1 1 0;min-width:0;overflow-wrap:anywhere;font-size:13px}.actions{display:flex;gap:8px;flex-wrap:wrap;margin:14px 0}.actions button{flex:1;min-width:0;font-size:13px;min-height:42px}.copy{display:grid;font-size:14px;margin:16px 0}.copy>*{grid-area:1/1;margin:0}.reserve{visibility:hidden}.scope{margin-top:14px;color:var(--muted);font-size:12px}.scope summary{padding:8px 0;cursor:pointer}.scope p{margin-top:10px}.two{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:12px;margin:16px 0}.card{border:1px solid var(--rule);border-radius:9px;padding:14px;min-width:0}.card header{font-size:14px;font-weight:600;margin-bottom:10px}.card button{font-size:12px;width:100%;min-width:0}.card small{display:block;font-size:12px;color:var(--muted)}.card code{font-size:12px;overflow-wrap:anywhere}.metrics{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:12px;margin:16px 0}.metrics>div{border-left:2px solid var(--accent);padding-left:10px;min-width:0}.metrics span{display:block;min-height:3.2em;color:var(--muted);font-size:12px}.metrics strong{font-size:23px;display:block;font-weight:500;overflow-wrap:anywhere;font-variant-numeric:tabular-nums}.formula{border-top:1px solid var(--rule);padding-top:12px;margin-top:14px;font-size:13px;font-variant-numeric:tabular-nums;overflow-wrap:anywhere}.value{font:500 20px var(--viz-font-ui)}.legend{display:flex;flex-wrap:wrap;gap:6px 14px;font-size:12px;color:var(--muted);margin:12px 0}.legend span:before{content:'';display:inline-block;width:8px;height:8px;border-radius:2px;background:var(--accent);margin-right:6px}.legend .second:before{background:var(--second)}.check{display:flex;align-items:center;gap:8px;font-size:13px;margin:12px 0}.check input{width:16px;height:16px;flex-shrink:0;accent-color:var(--accent)}select,input[type=text],input[type=number]{background:var(--paper);color:var(--ink);font:inherit;font-size:13px;padding:10px;border:1px solid var(--rule);border-radius:7px;max-width:100%;min-width:0}select{width:100%;min-height:42px}.band{height:20px;display:flex;background:var(--surface);border-radius:4px;overflow:hidden}.band i{height:100%;display:block;background:var(--accent);transition:width .25s}.band i.second{background:var(--second)}button:disabled{cursor:default}.status{font-size:13px;min-height:4.8em}.mono{font-family:var(--viz-font-mono);font-variant-numeric:tabular-nums}.muted{color:var(--muted)}\n@media(max-width:480px){.two{gap:8px}.card{padding:12px 9px}.actions button,.presets button{font-size:12px}.metrics strong{font-size:20px}.metrics{gap:8px}}\n@media(prefers-reduced-motion:reduce){*,*::before,*::after{transition:none!important;animation:none!important}}\n.track-row{margin:16px 0}.track-row>span{display:block;font-size:12px;color:var(--muted);margin-bottom:7px}.track{display:flex;gap:5px;min-width:0}.cell{flex:1;min-width:0;min-height:48px;border:1px solid var(--rule);border-radius:5px;background:var(--surface);display:flex;align-items:center;justify-content:center;text-align:center;padding:5px 3px;font-size:12px;overflow-wrap:anywhere;font-variant-numeric:tabular-nums}.cell.on{background:var(--accent-soft);border-color:var(--accent)}.cell.gold{background:var(--second-soft);border-color:var(--second)}.cell.empty{border-style:dashed;color:var(--muted)}.cell.error{border-color:var(--second);text-decoration:line-through}.choices{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:6px;margin:14px 0}.choices button{min-width:0;padding:10px 4px;font-size:12px;overflow-wrap:anywhere}.choices button[aria-current=true]{border-color:var(--accent);background:var(--accent-soft)}.relation{display:grid;grid-template-columns:minmax(0,1fr) 28px minmax(0,1fr);align-items:center;gap:6px;margin:12px 0}.relation .cell{min-height:72px}.relation>i{text-align:center;font-style:normal;color:var(--accent)}.readout{font-size:13px;white-space:pre-line;min-height:6.4em;overflow-wrap:anywhere}.plot{position:relative;border-left:1px solid var(--rule);border-bottom:1px solid var(--rule);padding:8px 0;margin:18px 0}.plot-row{height:28px;position:relative;margin:4px 0;background:var(--surface)}.plot-row i{position:absolute;inset-block:5px;background:var(--accent);min-width:2px;transition:width .2s,left .2s}.plot-row b{position:relative;padding:4px 6px;font:11px/28px var(--viz-font-ui);background:var(--paper);border-radius:3px}.axis{display:flex;justify-content:space-between;font-size:11px;color:var(--muted)}\n\n/* Action labels must remain readable across locales and narrow embeds. */\n.actions{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:8px}\n.actions button{overflow-wrap:anywhere;white-space:normal;min-height:58px;padding:8px}\nbutton{overflow-wrap:anywhere}\n@media(min-width:640px){.actions{grid-template-columns:repeat(3,minmax(0,1fr))}}\n\n.metrics span{overflow-wrap:anywhere}\n.panel{border:1px solid var(--rule);border-radius:9px;padding:12px;margin:14px 0;min-width:0}.panel h3{font-size:13px;margin:0 0 10px;color:var(--muted)}.source{font:13px/1.8 var(--viz-font-mono);white-space:pre-wrap;overflow-wrap:anywhere;margin:12px 0}.source mark{background:var(--accent-soft);color:var(--ink);border-bottom:2px solid var(--accent)}.source .fault{background:var(--second-soft);border-color:var(--second)}.diagram{display:block;width:100%;height:auto;max-height:320px;margin:14px 0;overflow:visible}.diagram text{fill:var(--ink);font:12px var(--viz-font-ui)}.diagram .edge{stroke:var(--muted);stroke-width:1.5;fill:none}.diagram .edge.on{stroke:var(--accent);stroke-width:3}.diagram .node{fill:var(--surface);stroke:var(--rule);stroke-width:1.5}.diagram .node.on{fill:var(--accent-soft);stroke:var(--accent)}.diagram .node.gold{fill:var(--second-soft);stroke:var(--second)}.log{font:12px/1.8 var(--viz-font-mono);white-space:pre-line;overflow-wrap:anywhere;min-height:7.2em}.table{display:grid;gap:5px;margin:14px 0}.table .tr{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:5px}.table .tr>*{padding:8px 5px;background:var(--surface);border-radius:4px;min-width:0;overflow-wrap:anywhere;font-size:12px}.table .tr .on{background:var(--accent-soft)}.table .tr .gold{background:var(--second-soft)}.choices button{min-height:44px}.metrics strong{min-height:1.6em}.code-line{display:block}.code-line.on{background:var(--accent-soft);border-left:2px solid var(--accent);padding-left:6px}.status-badge{font-size:12px;color:var(--accent)}.tile-grid{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:6px}.tile-grid .cell{min-height:40px}.actions button{min-height:48px}.form-field{display:grid;gap:8px;margin:14px 0;font-size:13px}.form-field input{width:100%;font-family:var(--viz-font-ui)}.metrics.three{grid-template-columns:repeat(3,minmax(0,1fr))}.formula{min-height:3.2em}.presets{flex-wrap:wrap}.presets button{flex-basis:25%;min-height:44px}\n\n.budget{display:flex;height:28px;margin:16px 0;background:var(--surface);border-radius:5px;overflow:hidden}.budget i{display:block;background:var(--accent);transition:width .2s}.budget i.gold{background:var(--second)}.intervals{margin:16px 0}.interval{position:relative;height:36px;background:var(--surface);margin:6px 0;border-radius:4px;overflow:hidden}.interval i{position:absolute;height:100%;background:var(--accent-soft);border-left:2px solid var(--accent)}.interval i.gold{background:var(--second-soft);border-color:var(--second)}.interval span{position:relative;z-index:1;font:12px/36px var(--viz-font-ui);padding-left:7px}.table .tr>*{min-height:4.8em}.formula{white-space:pre-line;min-height:7.2em}.actions:has(>button:only-child){grid-template-columns:1fr}.metrics strong{min-height:3.2em}.rangegrid{display:grid;grid-template-columns:repeat(12,minmax(0,1fr));gap:3px;margin:14px 0}.rangegrid .cell{min-height:30px;padding:3px 1px;font-size:10px}.message{min-height:7.2em;white-space:pre-line;font:13px/1.8 var(--viz-font-ui);overflow-wrap:anywhere}\n/* Allow CJK hanging punctuation without overflowing a metric's border box. */\n.metrics>div{padding-right:4px}.metrics span{line-break:anywhere}\n\n#results .tr{grid-template-columns:.7fr 1.2fr 1fr}.metrics{grid-template-columns:1fr}.metrics strong{font-size:18px}","js":"const $=s=>document.querySelector(s);const set=(id,v)=>$('#'+id).textContent=v;const t=k=>viz.t(k);function cells(id,values){$('#'+id).replaceChildren(...values.map(v=>{const e=document.createElement('div');e.className='cell '+(v.cls||'');e.textContent=v.text;e.title=v.title||v.text;return e;}));}function pressed(mode){document.querySelectorAll('[data-mode]').forEach(e=>e.setAttribute('aria-pressed',String(e.dataset.mode===mode)));}const esc=s=>String(s).replace(/[&<>\"']/g,c=>({'&':'&','<':'<','>':'>','\"':'"',\"'\":'''}[c]));const fmt=s=>'{'+[...s].sort().join(', ')+'}';function graph(id,nodes,edges){const el=$('#'+id);el.setAttribute('viewBox','0 0 360 200');el.innerHTML='<defs><marker id=\"arrow-'+id+'\" viewBox=\"0 0 10 10\" refX=\"8\" refY=\"5\" markerWidth=\"5\" markerHeight=\"5\" orient=\"auto-start-reverse\"><path d=\"M 0 0 L 10 5 L 0 10 z\" fill=\"var(--muted)\"/></marker></defs>'+edges.map(e=>{const a=nodes[e[0]],b=nodes[e[1]],dx=b.x-a.x,dy=b.y-a.y,d=Math.hypot(dx,dy)||1;if(edges.some(r=>r[0]===e[1]&&r[1]===e[0])){const nx=-dy/d,ny=dx/d;return '<path class=\"edge '+(e[2]?'on':'')+'\" d=\"M '+(a.x+dx/d*22+nx*12)+' '+(a.y+dy/d*22+ny*12)+' Q '+((a.x+b.x)/2+nx*38)+' '+((a.y+b.y)/2+ny*38)+' '+(b.x-dx/d*25+nx*12)+' '+(b.y-dy/d*25+ny*12)+'\" marker-end=\"url(#arrow-'+id+')\"/>';}return '<line class=\"edge '+(e[2]?'on':'')+'\" x1=\"'+(a.x+dx/d*25)+'\" y1=\"'+(a.y+dy/d*25)+'\" x2=\"'+(b.x-dx/d*28)+'\" y2=\"'+(b.y-dy/d*28)+'\" marker-end=\"url(#arrow-'+id+')\"/>';}).join('')+nodes.map(n=>'<g><circle class=\"node '+(n.cls||'')+'\" cx=\"'+n.x+'\" cy=\"'+n.y+'\" r=\"25\"/><text x=\"'+n.x+'\" y=\"'+(n.y+4)+'\" text-anchor=\"middle\">'+esc(n.name)+'</text>'+(n.sub?'<text x=\"'+n.x+'\" y=\"'+(n.y+43)+'\" text-anchor=\"middle\">'+esc(n.sub)+'</text>':'')+'</g>').join('');}function table(id,rows){$('#'+id).classList.add('table');$('#'+id).replaceChildren(...rows.map(r=>{const row=document.createElement('div');row.className='tr';r.forEach(x=>{const c=document.createElement('div');if(typeof x==='object'){c.textContent=x.text;c.className=x.cls||'';}else c.textContent=x;row.append(c)});return row;}));}function band(id,parts,total){$('#'+id).replaceChildren(...parts.map((p,i)=>{const e=document.createElement('i');e.style.width=(100*p.value/total)+'%';e.className=i%2?'gold':'on';e.title=p.name+': '+p.value;return e}));}function intervals(id,items,total){$('#'+id).replaceChildren(...items.map(p=>{const row=document.createElement('div');row.className='interval';const bar=document.createElement('i');bar.style.left=(100*p.start/total)+'%';bar.style.width=(100*(p.end-p.start)/total)+'%';bar.className=p.cls||'';const label=document.createElement('span');label.textContent=p.label;row.append(bar,label);return row;}));}let winner='cancel',seen={C:false,R:false};function draw(){pressed(winner);table('results',[['C',winner==='cancel'?'0':'−ENOENT',t(seen.C?'seen':'pending')],['R',winner==='cancel'?'−ECANCELED':'4 B',t(seen.R?'seen':'pending')]]);set('buffer',t(seen.R?'free':'held'));$('#buffer').dataset.free=seen.R;$('#reapC').disabled=seen.C;$('#reapR').disabled=seen.R;}function reset(){seen={C:false,R:false};draw()}for(const n of ['C','R'])$('#reap'+n).onclick=()=>{seen[n]=true;draw()};document.querySelectorAll('[data-mode]').forEach(e=>e.onclick=()=>{winner=e.dataset.mode;reset()});$('#reset').onclick=reset;draw();","audio":false,"strings":{"buffer":"Application buffer lifetime","cancel":"Cancellation wins","free":"Reusable: R settled","heading":"Cancel completion is not original completion","held":"Retain: R not settled","intro":"Choose the winning kernel event, then consume the two CQEs in either order.","pending":"Pending","reapC":"Consume cancel CQE","reapR":"Consume read CQE","reset":"Reset","results":"Two independent results","scope":"Unique user_data, one-shot read. Two valid races: successful cancellation or prior original completion; excludes EALREADY and other flags/timings.","scopeLabel":"Model scope and assumptions","seen":"Consumed","success":"Read completes first","winner":"Winning event"}}
Batching and Backpressure
When no SQE can be obtained, you need to submit prepared items and continue advancing based on the number of in-flight requests; CQ also needs to be collected and consumption position advanced in time. Waiting for multiple completions and actually traversing batches are different actions; do not interpret the name wait_cqes as having already consumed all CQEs for the application.
Queue depth controls concurrent in-flight work. Too small may fail to fill the device, while too large may increase queuing delay, memory usage, and tail latency; there is no universal "256–512 is enough." First estimate concurrent needs using completion time and target throughput, then measure saturation points and tail latency.
Estimate Quantity Using Throughput and Latency, Plus Buffer Accounting
Assume the target throughput is 20,000 requests per second, and the average in-flight time is 2 ms; the average number of in-flight requests in a stable state is approximately 40. This is a capacity estimate of throughput multiplied by average time, not a tail latency guarantee, nor does it mean the kernel must configure SQ depth to 40.
If each standard read request exclusively occupies a 64 KiB buffer, an in-flight limit of 64 corresponds to at least 4 MiB of payload capacity, not to mention request objects, rings, alignment, and other queues. SQ empty slots are only submission capacity: after an SQE has been consumed by the kernel, the request may still be executing, so you cannot treat "getting another SQE" as "in-flight buffer budget automatically decreasing."
Applications should independently maintain the number of in-flight requests and the available buffer pool; when the limit is reached, collect completions first or pause upstream. For multishot, also account for the number of connections/messages already accepted but not yet processed; a single active SQE can continuously generate business resources, and their accumulation cannot be limited solely by SQ depth.
When comparing with libaio or POSIX AIO, fix buffered/direct modes, block sizes, devices, threads, and synchronization guarantees. The asynchrony of Linux native AIO is limited in some buffered paths, which does not mean that syntactically submitting buffered file I/O is absolutely impossible; POSIX AIO is an interface specification, and the thread implementation of Linux libc is just one specific implementation.
The request API provides an asynchronous model, which does not mean that every operation is executed asynchronously by device hardware; some work may be completed instantly or handed over to kernel worker threads. Prioritize verifying behavior, resource reclamation, and backpressure before discussing how many system calls are reduced.