Commits

Michael Granger  committed c1e35a0

Add PG::Connection#socket_io to return a memoized IO for the underlying socket.

This keeps the IO in scope until the Connection is garbage-collected, preventing
Ruby from auto-closing the connection to PostgreSQL.

  • Participants
  • Parent commits dbfbd4a

Comments (1)

  1. Lars Kanis

    Great! I would have named it "socket_io", too and travis finally shines green. If I get some leisure, I'll look for a way to get it working on windows. But this will not happen within the next couple of weeks.

Files changed (7)

 --- 
-exclude: !ruby/regexp /(?:\.(hg|hoe|bundle|irb|pry|rvm|tm|DS_Store)|tmp|misc)/
+exclude: !ruby/regexp /(?:\.(hg|hoe|bundle|irb|pry|rvm|tm|DS_Store|travis\.yml)|tmp|misc)/

File ext/pg_connection.c

  *    conn.socket() -> Fixnum
  *
  * Returns the socket's file descriptor for this connection.
- * IO.for_fd() can be used to build a proper IO object to the socket.
+ * <tt>IO.for_fd()</tt> can be used to build a proper IO object to the socket.
+ * If you do so, you will likely also want to set <tt>autoclose=false</tt>
+ * on it to prevent Ruby from closing the socket to PostgreSQL if it
+ * goes out of scope. Alternatively, you can use #socket_io, which
+ * creates an IO that's associated with the connection object itself,
+ * and so won't go out of scope until the connection does.
  *
  * *Note:* On Windows the file descriptor is not really usable,
  * since it can not be used to build a Ruby IO object.

File lib/pg/connection.rb

 	end
 
 
+	### Fetch a memoized IO object created from the Connection's underlying socket.
+	### Using this avoids the problem of the underlying connection being closed by
+	### Ruby when an IO created using <tt>IO.for_fd(conn.socket)</tt> goes out of scope.
+	def socket_io
+		unless @socket_io
+			@socket_io = IO.for_fd( self.socket )
+			@socket_io.autoclose = false if @socket_io.respond_to?( :autoclose= )
+		end
+
+		return @socket_io
+	end
+
 end # class PG::Connection
 
 # Backward-compatible alias

File sample/async_api.rb

 
 # Now grab a reference to the underlying socket so we know when the
 # connection is established
-socket = IO.for_fd( conn.socket )
+socket = conn.socket_io
 
 # Track the progress of the connection, waiting for the socket to become readable/writable
 # before polling it

File sample/async_copyto.rb

 conn = PG.connect( :dbname => 'test' )
 conn.setnonblocking( true )
 
-socket = IO.for_fd( conn.socket )
+socket = conn.socket_io
 
 $stderr.puts "Running COPY command ..."
 buf = ''

File sample/async_mixed.rb

 abort "Connect failed: %s" % [ conn.error_message ] unless conn.status == PG::CONNECTION_OK
 
 # Now grab a reference to the underlying socket to select() on while the query is running
-socket = IO.for_fd( conn.socket )
+socket = conn.socket_io
 
 # Send the (asynchronous) query
 output_progress "Sending query"

File spec/pg/connection_spec.rb

 	it "can connect asynchronously", :unix do
 		tmpconn = described_class.connect_start( @conninfo )
 		tmpconn.should be_a( described_class )
-		socket = IO.for_fd( tmpconn.socket )
+		socket = tmpconn.socket_io
 		status = tmpconn.connect_poll
 
 		while status != PG::PGRES_POLLING_OK
 		described_class.connect_start(@conninfo) do |tmpconn|
 			tmpconn.should be_a( described_class )
 			conn = tmpconn
-			socket = IO.for_fd(tmpconn.socket)
+			socket = tmpconn.socket_io
 			status = tmpconn.connect_poll
 
 			while status != PG::PGRES_POLLING_OK
 		serv = TCPServer.new( '127.0.0.1', 54320 )
 		conn = described_class.connect_start( '127.0.0.1', 54320, "", "", "me", "xxxx", "somedb" )
 		[PG::PGRES_POLLING_WRITING, PG::CONNECTION_OK].should include conn.connect_poll
-		select( nil, [IO.for_fd(conn.socket)], nil, 0.2 )
+		select( nil, [conn.socket_io], nil, 0.2 )
 		serv.close
 		if conn.connect_poll == PG::PGRES_POLLING_READING
-			select( [IO.for_fd(conn.socket)], nil, nil, 0.2 )
+			select( [conn.socket_io], nil, nil, 0.2 )
 		end
 		conn.connect_poll.should == PG::PGRES_POLLING_FAILED
 	end