Skip to content

poll: Write back only revents to userspace, matching Linux behavior#12851

Open
tanyifeng wants to merge 1 commit intogoogle:masterfrom
tanyifeng:poll-revents-writeback
Open

poll: Write back only revents to userspace, matching Linux behavior#12851
tanyifeng wants to merge 1 commit intogoogle:masterfrom
tanyifeng:poll-revents-writeback

Conversation

@tanyifeng
Copy link
Copy Markdown
Contributor

@tanyifeng tanyifeng commented Apr 2, 2026

doPoll() was modifying pfd.Events (adding POLLHUP|POLLERR) and writing back the entire pollfd struct. Linux only writes back revents via unsafe_put_user() in do_sys_poll(). The polluted events field broke libevent's poll_del(), causing a busy-loop in tmux (CPU 96.6%).

Fix: use an internal mask for POLLHUP|POLLERR in initReadiness() and pollBlock(); write back only the 2-byte revents field per fd in doPoll().

@tanyifeng
Copy link
Copy Markdown
Contributor Author

Reproduction

# In gVisor (runsc) — CPU spikes to ~95%
docker run -d --name tmux-test --runtime=runsc ubuntu:22.04 sleep 3600
docker exec tmux-test bash -c "apt-get update -qq && apt-get install -y -qq tmux procps >/dev/null 2>&1"
docker exec tmux-test bash -c "tmux new-session -d -s test && sleep 30 && ps aux | grep tmux"
# tmux: 94.7% CPU

# In Linux (runc) — CPU stays at 0%
docker run -d --name tmux-ctrl --runtime=runc ubuntu:22.04 sleep 3600
docker exec tmux-ctrl bash -c "apt-get update -qq && apt-get install -y -qq tmux procps >/dev/null 2>&1"
docker exec tmux-ctrl bash -c "tmux new-session -d -s test && sleep 30 && ps aux | grep tmux"
# tmux: 0.0% CPU

// reventsOffsetInFD is the byte offset of the REvents field within
// linux.PollFD. Since REvents is the last field (int16), its offset
// equals the struct size minus the size of int16.
reventsOffsetInFD = sizeofPollFD - (*primitive.Int16)(nil).SizeBytes()
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you use unsafe.Offsetof instead?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, I pushed the code to use unsafe.Offsetof(linux.PollFD{}.REvents), Thanks!

doPoll() was modifying pfd.Events (adding POLLHUP|POLLERR) and writing
back the entire pollfd struct. Linux only writes back revents via
unsafe_put_user() in do_sys_poll(). The polluted events field broke
libevent's poll_del(), causing a busy-loop in tmux (CPU 96.6%).

Fix: use an internal mask for POLLHUP|POLLERR in initReadiness() and
pollBlock(); write back only the 2-byte revents field per fd in doPoll().

Signed-off-by: Tan Yifeng <yiftan@tencent.com>
@tanyifeng tanyifeng force-pushed the poll-revents-writeback branch from 7cc0267 to 6529fb0 Compare April 7, 2026 06:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants