Skip to content

libuv adapter: stop polling immediately only from the event loop thread - #14

Open
alexey-milovidov wants to merge 1 commit into
ClickHouse/v3.13.0from
ClickHouse/v3.13.0-libuv-read-loop-thread
Open

alexey-milovidov wants to merge 1 commit into
ClickHouse/v3.13.0from
ClickHouse/v3.13.0-libuv-read-loop-thread

Conversation

@alexey-milovidov

Copy link
Copy Markdown
Member

Since upstream b89b44d, natsLibuv_Read calls uv_poll_stop on removal before it decides whether to schedule the event. natsConnection_Close reaches it from the thread that closes the connection, so uv_poll_stop modifies the poll watcher of a loop that is running in another thread. Libuv is not thread-safe: ThreadSanitizer reports a data race between uv__io_stop and uv__io_poll, and the DROP of a NATS table hung.

This change stops polling immediately only from the event loop thread, and only while the poll handle still exists. From other threads, the scheduled removal stops the poll on the loop thread.

Found in ClickHouse/ClickHouse#119507:
https://s3.amazonaws.com/clickhouse-test-reports/praktika.html?PR=119507&sha=acb8e8bda856c88eda8525e5abbeec03074b0ce4&name_0=PR&name_1=Integration%20tests%20%28amd_tsan%2C%20targeted%29

🤖 Generated with Claude Code

Since upstream b89b44d ("Libuv may crash if connection destroyed while
consuming"), `natsLibuv_Read` calls `uv_poll_stop` on removal before it
decides whether to schedule the event. `natsConnection_Close` reaches it
from the thread that closes the connection, so `uv_poll_stop` modifies the
poll watcher of a loop running in another thread. Libuv is not
thread-safe: ThreadSanitizer reports a data race between `uv__io_stop` and
`uv__io_poll`, and the corrupted watcher state can hang the close.

Stop polling immediately only from the event loop thread, and only while
the poll handle still exists; from other threads the scheduled removal
stops the poll on the loop thread. The connection stays retained until
the adapter detaches, so a read event delivered in between is harmless.

Found by ClickHouse CI:
https://s3.amazonaws.com/clickhouse-test-reports/praktika.html?PR=119507&sha=acb8e8bda856c88eda8525e5abbeec03074b0ce4&name_0=PR&name_1=Integration%20tests%20%28amd_tsan%2C%20targeted%29

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
alexey-milovidov added a commit to actueleai/ClickHouse that referenced this pull request Oct 9, 2026
Since upstream nats.c b89b44d0 (part of v3.13.0), `natsLibuv_Read`
calls `uv_poll_stop` from whichever thread removes the read event. On
`DROP TABLE`, `natsConnection_Close` runs in the query thread while the
`NATSHandler` loop thread is inside `uv_run`, so ThreadSanitizer reports
a data race between `uv__io_stop` and `uv__io_poll`, and the `DROP` hung.

Point `contrib/nats-io` at a fork commit that stops polling immediately
only from the event loop thread: ClickHouse/nats.c#14

CI report:
https://s3.amazonaws.com/clickhouse-test-reports/praktika.html?PR=119507&sha=acb8e8bda856c88eda8525e5abbeec03074b0ce4&name_0=PR&name_1=Integration%20tests%20%28amd_tsan%2C%20targeted%29
PR: ClickHouse#119507

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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.

1 participant