Skip to content

Commit

Permalink
Make pgconn_connect_poll close the socket prior to calling
Browse files Browse the repository at this point in the history
`PQconnectPoll`

Since `PQconnectPoll` can change the underlying socket and thus the file
descriptor, closing the socket after can manifest a race condition
where libpq has closed the socket and another thread has opened a socket
with the recycled file descriptor. When the original socket is closed
from Ruby, the VM will notify the new thread that the FD has closed
(erroneously) resulting in an exception.

The same for `PQresetPoll`.
  • Loading branch information
jcalvert authored and larskanis committed Apr 9, 2024
1 parent 21285ba commit 21a064b
Show file tree
Hide file tree
Showing 2 changed files with 17 additions and 2 deletions.
4 changes: 2 additions & 2 deletions ext/pg_connection.c
Original file line number Diff line number Diff line change
Expand Up @@ -515,9 +515,9 @@ static VALUE
pgconn_connect_poll(VALUE self)
{
PostgresPollingStatusType status;
status = gvl_PQconnectPoll(pg_get_pgconn(self));

pgconn_close_socket_io(self);
status = gvl_PQconnectPoll(pg_get_pgconn(self));

return INT2FIX((int)status);
}
Expand Down Expand Up @@ -615,9 +615,9 @@ static VALUE
pgconn_reset_poll(VALUE self)
{
PostgresPollingStatusType status;
status = gvl_PQresetPoll(pg_get_pgconn(self));

pgconn_close_socket_io(self);
status = gvl_PQresetPoll(pg_get_pgconn(self));

return INT2FIX((int)status);
}
Expand Down
15 changes: 15 additions & 0 deletions spec/pg/connection_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -577,6 +577,21 @@
conn.close
end

it "doesn't notify the wrong thread about closed socket (Bug #564)" do
10.times do
10.times.map do
Thread.new do
Thread.current.report_on_exception = false
expect do
threaded_conn = PG.connect( @conninfo + " sslcert=tmp_test_specs/data/ruby-pg-ca-cert" )
res = threaded_conn.exec("SELECT 1")
threaded_conn.close
end.not_to raise_error
end
end.each(&:join)
end
end

it "can use conn.reset_start to restart the connection" do
ios = IO.pipe
conn = described_class.connect_start( @conninfo )
Expand Down

0 comments on commit 21a064b

Please sign in to comment.