ruby-ldap
ruby-ldap copied to clipboard
GIL is not released when calling blocking IO functions from C
C functions like ldap_open
block until a connection is opened. If an upstream LDAP is bogged down, or someone attempts to connect to a server that doesn't support LDAP and lets the connection time out, all other ruby threads are blocked until the ldap_open
call returns. Here is some code to demonstrate this:
#!/usr/bin/env ruby
require 'ldap'
# Start a thread that prints 'beep!' every second.
beeper = Thread.new do
loop do
puts "beep!"
sleep 1
end
end
# sleep the main thread so the beeper can get a few messages out
sleep 3
# Google.com hangs for a rather long time when we try to open
# a connection on port 389.
puts "about to connect to google.com:389..."
conn = LDAP::Conn.open 'google.com', 389
puts "connected???" # looks like we've hacked google!
Expected result: The beeper
thread will continue to print "beep!"
every second while the ldap connection is being opened.
Actual result: The beeper
thread stops until the call to LDAP::Conn.open
finishes.
I encountered this issue on a project for work where users can perform certain LDAP actions through a web user interface. Because the project uses a threaded webserver (Puma), a connection attempt that takes a while will block all other requests to the server from being processed. While switching to a multi-process webserver is an obvious solution, it would be better for this library to perform the IO with the GIL released so that other threads can make progress, which is the behavior of other blocking native IO in ruby.
Hello @jjmason, could you please try a version from branch gh-37
?