do case state when :start then Start hostname resolution when :v4w then Wait for 50ms for IPv6 name resolution when :v6c, :v4c, :v46c then Start a new connection attempt when :v46w then Wait for the connection state to be confirmed and name resolution to complete when :success then Return the successfully connected socket when :failure then Raise an exception when :timeout then Raise a timeout exception end end end 5IFJNQMFNFOUBUJPOPG4PDLFUUDQ )FSFJTBCSJFGPWFSWJFXPG4PDLFUUDQXJUI)&W JNQMFNFOUBUJPO
loop do case state when :start then Start hostname resolution when :v4w then Wait for 50ms for IPv6 name resolution when :v6c, :v4c, :v46c then Start a new connection attempt when :v46w then Wait for the connection state to be confirmed and name resolution to complete when :success then Return the successfully connected socket when :failure then Raise an exception when :timeout then Raise a timeout exception end end end WD GBJMVSF TVDDFTT TUBSU WX UJNFPVU WX WD WD 5IFTUBUFUSBOTJUJPOEJBHSBNJTSFQSFTFOUFE CZDBMMJOHBDBTFTUBUFNFOUJOTJEF,FSOFMMPPQ
do case state when :start then Start hostname resolution when :v4w then Wait for 50ms for IPv6 name resolution when :v6c, :v4c, :v46c then Start a new connection attempt when :v46w then Wait for the connection state to be confirmed and name resolution to complete when :success then Return the successfully connected socket when :failure then Raise an exception when :timeout then Raise a timeout exception end end end 5IFDVSSFOUTUBUFJTTUPSFEJOBWBSJBCMFTUBUF TUBUF"WBSJBCMFUPTUPSFUIFDVSSFOUTUBUF 5IFDVSSFOUTUBUF 5IFTUBUFUSBOTJUJPO
do case state when :start then Start hostname resolution when :v4w then Wait for 50ms for IPv6 name resolution when :v6c, :v4c, :v46c then Start a new connection attempt when :v46w then Wait for the connection state to be confirmed and name resolution to complete when :success then Return the successfully connected socket when :failure then Raise an exception when :timeout then Raise a timeout exception end end end 5IFQSPDFTTPQFSBUFTBTGPMMPXT &YFDVUFUIFQSFEF fi OFEQSPDFTTJOHGPSUIFDVSSFOUTUBUF ➡︎ 4FUUIFOFYUTUBUF ➡︎ 1SPDFFEUPUIFOFYUMPPQJUFSBUJPO 5IFBDUJPOTQFSGPSNFEGPSFBDITUBUF TUBUF"WBSJBCMFUPTUPSFUIFDVSSFOUTUBUF 5IFDVSSFOUTUBUF 5IFTUBUFUSBOTJUJPO
loop do case state when :start then Start hostname resolution when :v4w then Wait for 50ms for IPv6 name resolution when :v6c, :v4c, :v46c then Start a new connection attempt when :v46w then Wait for the connection state to be confirmed and name resolution to complete when :success then Return the successfully connected socket when :failure then Raise an exception when :timeout then Raise a timeout exception end end end "UUIFTUBSUPGUIFNFUIPE UIFWBSJBCMFTUBUFJT JOJUJBMJ[FEXJUIUIFJOJUJBMTUBUFTUBSU TUBSUBTBJOJUJBMTUBUF
loop do case state when :start then Start hostname resolution when :v4w then Wait for 50ms for IPv6 name resolution when :v6c, :v4c, :v46c then Start a new connection attempt when :v46w then Wait for the connection state to be confirmed and name resolution to complete when :success then Return the successfully connected socket when :failure then Raise an exception when :timeout then Raise a timeout exception end end end WD GBJMVSF TVDDFTT TUBSU WX UJNFPVU WX WD WD *OUIF fi STUJUFSBUJPOPGUIFMPPQ UIFQSPDFTTPGTUBSUJTFYFDVUFE
ADDRESS_FAMILIES[family], :STREAM) hostname_resolution_queue.add_resolved(family, resolved_addrinfos) rescue => e # ... end end TUBSU)FSFJTUIFJNQMFNFOUBUJPOPG UIFOBNFSFTPMVUJPONFUIPE 5IFTUBUFTUBSU
ADDRESS_FAMILIES[family], :STREAM) hostname_resolution_queue.add_resolved(family, resolved_addrinfos) rescue => e # ... end end TUBSU$BMM"EESJOGPHFUBEESJOGPXIJDIQFSGPSNT IPTUOBNFSFTPMVUJPO 'PSIPTUOBNFSFTPMVUJPO 5IFTUBUFTUBSU
ADDRESS_FAMILIES[family], :STREAM) hostname_resolution_queue.add_resolved(family, resolved_addrinfos) rescue => e # ... end end TUBSU0ODFIPTUOBNFSFTPMVUJPOJTDPNQMFUF QVTIUIFPCUBJOFEBEESFTTFTJOUPUIFRVFVFTIBSFE CFUXFFOUIFDIJMEUISFBETBOEUIFNBJOUISFBE def add_resolved(family, resolved_addrinfos) @mutex.synchronize do @queue.push [family, resolved_addrinfos] @wpipe.putc HOSTNAME_RESOLUTION_QUEUE_UPDATED end end 5IFTUBUFTUBSU
ADDRESS_FAMILIES[family], :STREAM) hostname_resolution_queue.add_resolved(family, resolved_addrinfos) rescue => e # ... end end TUBSU"OEJUJTOFDFTTBSZUPOPUJGZUIFNBJOUISFBE UIBUOBNFSFTPMVUJPOIBTDPNQMFUFEJOUIFDIJMEUISFBE def add_resolved(family, resolved_addrinfos) @mutex.synchronize do @queue.push [family, resolved_addrinfos] @wpipe.putc HOSTNAME_RESOLUTION_QUEUE_UPDATED end end 5IFTUBUFTUBSU
ADDRESS_FAMILIES[family], :STREAM) hostname_resolution_queue.add_resolved(family, resolved_addrinfos) rescue => e # ... end end TUBSU5PEPUIJT XSJUFUIFOPUJ fi DBUJPOUPUIFXSJUFFOE PGBQJQFUIBUJTBMTPTIBSFEXJUIUIFNBJOUISFBE def add_resolved(family, resolved_addrinfos) @mutex.synchronize do @queue.push [family, resolved_addrinfos] @wpipe.putc HOSTNAME_RESOLUTION_QUEUE_UPDATED end end 5IFTUBUFTUBSU
Socket.hostname_resolution hostname_resolved, _, = IO.select( # ... family_name, res = hostname_resolution_queue.get # ... state = case family_name when :ipv6 then :v6c when :ipv4 then :v4w end selectable_addrinfos.add(family_name, res) # ... next TUBSU8IFOBOPUJ fi DBUJPOBSSJWFTUISPVHIUIFQJQF BOE*0TFMFDUVOCMPDLT UIFOSFUSJFWFUIFSFTPMWFE BEESFTTFT (FUUIFSFTVMUPGUIFIPTUOBNFSFTPMVUJPO 4UBSUIPTUOBNFSFTPMVUJPO 5IFTUBUFTUBSU (hostname_resolution_waiting, nil, nil, nil)
Socket.hostname_resolution hostname_resolved, _, = IO.select( # ... family_name, res = hostname_resolution_queue.get # ... state = case family_name when :ipv6 then :v6c when :ipv4 then :v4w end selectable_addrinfos.add(family_name, res) # ... next TUBSU*GUIFSFTPMWFEBEESFTTFTBSF*1W USBOTJUJPOUPWX*GJUJT*1W USBOTJUJPOUPWD %FUFSNJOFUIFOFYUTUBUF (POFYU 4UBSUIPTUOBNFSFTPMVUJPO 5IFTUBUFTUBSU (hostname_resolution_waiting, nil, nil, nil)
loop do case state when :start then Start hostname resolution when :v4w then Wait for 50ms for IPv6 name resolution when :v6c, :v4c, :v46c then Start a new connection attempt when :v46w then Wait for the connection state to be confirmed and name resolution to complete when :success then Return the successfully connected socket when :failure then Raise an exception when :timeout then Raise a timeout exception end end end WD GBJMVSF TVDDFTT TUBSU WX UJNFPVU WX WD WD WXJTTUBUFGPS3FTPMVUJPO%FMBZ XIJDIXBJUTVQUPNTGPSPOMZ*1WOBNFSFTPMVUJPO
loop do case state when :start then Start hostname resolution when :v4w then Wait for 50ms for IPv6 name resolution when :v6c, :v4c, :v46c then Start a new connection attempt when :v46w then Wait for the connection state to be confirmed and name resolution to complete when :success then Return the successfully connected socket when :failure then Raise an exception when :timeout then Raise a timeout exception end end end WD GBJMVSF TVDDFTT TUBSU WX UJNFPVU WX WD WD WD WD BOEWDBSFUIFTUBUF UPTUBSUBOFXDPOOFDUJPOBUUFNQU
do case state when :start then Start hostname resolution when :v4w then Wait for 50ms for IPv6 name resolution when :v6c, :v4c, :v46c then Start a new connection attempt when :v46w then Wait for the connection state to be confirmed and name resolution to complete when :success then Return the successfully connected socket when :failure then Raise an exception when :timeout then Raise a timeout exception end end end WD GBJMVSF TVDDFTT TUBSU WX UJNFPVU WX WD WD *OWX UIFQSPDFTTXBJUTGPSUIFDPOOFDUJPOUPCF FTUBCMJTIFE0S JGUIFPUIFSBEESFTTGBNJMZJTTUJMMSFTPMWJOH BUUIJTQPJOU JUBMTPXBJUTGPSUIBUSFTPMVUJPOUPDPNQMFUF 5IFTUBUFWX
loop do case state when :start then Start hostname resolution when :v4w then Wait for 50ms for IPv6 name resolution when :v6c, :v4c, :v46c then Start a new connection attempt when :v46w then Wait for the connection state to be confirmed and name resolution to complete when :success then Return the successfully connected socket when :failure then Raise an exception when :timeout then Raise a timeout exception end end end WD GBJMVSF TVDDFTT TUBSU WX UJNFPVU WX WD WD &WFOUVBMMZ POFPGUIFUFSNJOBMTUBUFT TVDDFTT GBJMVSF PSUJNFPVU JTSFBDIFE5IFO4PDLFUUDQ fi OJTIFTCZ FJUIFSSFUVSOJOHUIFDPOOFDUFETPDLFUPSSBJTJOH BOFYDFQUJPO EFQFOEJOHPOUIFTJUVBUJPO
do case state when :start then Start hostname resolution when :v4w then Wait for 50ms for IPv6 name resolution when :v6c, :v4c, :v46c then Start a new connection attempt when :v46w then Wait for the connection state to be confirmed and name resolution to complete when :success then Return the successfully connected socket when :failure then Raise an exception when :timeout then Raise a timeout exception end end end WD GBJMVSF TVDDFTT TUBSU WX UJNFPVU WX WD WD 5IFJNQMFNFOUBUJPOPG4PDLFUUDQXJUI)&W JTOPXDPNQMFUF 5IFJNQMFNFOUBUJPOPG4PDLFUUDQ
IO.select( # ... family_name, res = hostname_resolution_queue.get # ... state = case family_name when :ipv6 then :v6c when :ipv4 then :v4w end selectable_addrinfos.add(family_name, res) # ... next (hostname_resolution_waiting, nil, nil, remaining) -FUTSFWJFXTUBSUBHBJO GPDVTJOHPOXIBUIBQQFOT BGUFS*0TFMFDUVOCMPDLTUIBUJT BGUFSFJUIFSUIF*1W PS*1WSFTPMVUJPODPNQMFUFT 4UBSUIPTUOBNFSFTPMVUJPO
# ... family_name, res = hostname_resolution_queue.get # ... state = case family_name when :ipv6 then :v6c when :ipv4 then :v4w end selectable_addrinfos.add(family_name, res) # ... next (hostname_resolution_waiting, nil, nil, remaining) 5IFOFYUTUBUFJTEFUFSNJOFECBTFEPOXIFUIFS UIFBEESFTTSFUSJFWFGSPNUIFRVFVFJT*1WPS*1W 4UBSUIPTUOBNFSFTPMVUJPO (FUUIFSFTVMU "OFYBNQMFTPGUIFFYDFTTJWFMZBTUBUFUSBOTJUJPOMJLFJNQM %FUFSNJOFUIFOFYUTUBUF
# ... family_name, res = hostname_resolution_queue.get # ... state = case family_name when :ipv6 then :v6c when :ipv4 then :v4w end selectable_addrinfos.add(family_name, res) # ... next (hostname_resolution_waiting, nil, nil, remaining) 8IBUIBQQFOTJGCPUI*1WBOE*1WIBWF BMSFBEZDPNQMFUFEOBNFSFTPMVUJPOBUUIJTQPJOU 4UBSUIPTUOBNFSFTPMVUJPO "OFYBNQMFTPGUIFFYDFTTJWFMZBTUBUFUSBOTJUJPOMJLFJNQM
# ... family_name, res = hostname_resolution_queue.get # ... state = case family_name when :ipv6 then :v6c when :ipv4 then :v4w end selectable_addrinfos.add(family_name, res) # ... next 4UBSUIPTUOBNFSFTPMVUJPO *1W *OUIJTDBTF UIFRVFVFIBT CPUI*1WBOE*1WBEESFTTFT (hostname_resolution_waiting, nil, nil, remaining) "OFYBNQMFTPGUIFFYDFTTJWFMZBTUBUFUSBOTJUJPOMJLFJNQM *1W 5IFRVFVF
# ... family_name, res = hostname_resolution_queue.get # ... state = case family_name when :ipv6 then :v6c when :ipv4 then :v4w end selectable_addrinfos.add(family_name, res) # ... next 4UBSUIPTUOBNFSFTPMVUJPO 'PSFYBNQMF JGUIF*1WBEESFTTFTBSFSFUSJFWFEGSPNUIFRVFVF (hostname_resolution_waiting, nil, nil, remaining) "OFYBNQMFTPGUIFFYDFTTJWFMZBTUBUFUSBOTJUJPOMJLFJNQM *1W *1W 5IFRVFVF 3FUSJFWF*1WBEESFTTPOMZ
# ... family_name, res = hostname_resolution_queue.get # ... state = case family_name when :ipv6 then :v6c when :ipv4 then :v4w end selectable_addrinfos.add(family_name, res) # ... next 4UBSUIPTUOBNFSFTPMVUJPO %FUFSNJOFUIBU*1WXBTSFTPMWFE fi STU BOEUIFQSPDFTTUSBOTJUJPOTUPWX UPXBJUGPS*1WOBNFSFTPMVUJPO (hostname_resolution_waiting, nil, nil, remaining) %FUFSNJOFUIFOFYUTUBUFBTWX "OFYBNQMFTPGUIFFYDFTTJWFMZBTUBUFUSBOTJUJPOMJLFJNQM 3FUSJFWF*1WBEESFTTPOMZ
<Start hostname resolution in child threads> loop do if <The condition to start a connection> then <Start a connection> timeout = <Calculate the timeout value> IO.select(<name resolution>, <connections>, nil, <timeout>) if <Sockets are confirmed connected> then <Verify the connection> if <A name resolution is complete> then <Save the resolved IP addresses> if <The next loop cannot proceed> then <Raise an exception> end end -FUTSFWJFXUIFDIBOHFT
hostname resolution in child threads> loop do # Processes in this loop end end 5IFPWFSWJFXPGUIFJNQSPWFEWFSTJPOPG4PDLFUUDQ 'JSTU UIFJOJUJBMJ[BUJPOPGWBSJBCMFTGPSNBOBHJOH UJNFPVUT TVDIBT3FTPMVUJPO%FMBZBOE $POOFDUJPO"UUFNQU%FMBZ BSFOFXMZBEEFE "EEFE
= nil connection_attempt_delay_expires_at = nil <Start hostname resolution in child threads> loop do # Processes in this loop end end SFTPMVUJPO@EFMBZ@FYQJSFT@BUJTBWBSJBCMFUIBUTUPSFT UIFEFBEMJOFGPSXBJUJOHPO*1WOBNFSFTPMVUJPO BTQBSUPGUIF3FTPMVUJPO%FMBZ *OJUJBMJ[BUJPOPGUIFUJNFPVUWBSJBCMFT
= nil connection_attempt_delay_expires_at = nil <Start hostname resolution in child threads> loop do # Processes in this loop end end DPOOFDUJPO@BUUFNQU@EFMBZ@FYQJSFT@BUJTBWBSJBCMF UIBUTUPSFTUIFUJNFBUXIJDIUIFOFYUDPOOFDUJPO BUUFNQUDBOCFHJOBGUFSUIFQSFWJPVTPOFIBTTUBSUFE BTQBSUPG$POOFDUJPO"UUFNQU%FMBZ *OJUJBMJ[BUJPOPGUIFUJNFPVUWBSJBCMFT
= nil connection_attempt_delay_expires_at = nil <Start hostname resolution in child threads> loop do # Processes in this loop end end 5IFTFWBSJBCMFTBSFJOJUJBMMZTFUUPOJM 5IFWBMVFTXJMMCFTFUMBUFS XIFOXBJUJOHCFDPNFT OFDFTTBSZ *OJUJBMJ[FXJUIOJM
nil connection_attempt_delay_expires_at = nil <Start hostname resolution in child threads> loop do # Processes in this loop end end 4UBSUIPTUOBNFSFTPMVUJPOJODIJMEUISFBET /FYU UIFNFUIPEGPSOBNFSFTPMVUJPOJTDBMMFE 5IFJNQMFNFOUBUJPOBOEJOWPDBUJPOPGUIJTNFUIPEBSF NPTUMZUIFTBNFBTQSFWJPVTJNQMFNFOUBUJPO &YFDVUFUIFOBNFSFTPMVUJPONFUIPE
nil connection_attempt_delay_expires_at = nil <Start hostname resolution in child threads> loop do # Processes in this loop end end 4UBSUBMPPQ 4UBSUBMPPQ
<Start a connection> timeout = <Calculate the timeout value> IO.select(<name resolution>, <connections>, nil, <timeout>) if <Sockets are confirmed connected> then <Verify the connection> if <A name resolution is complete> then <Save the resolved IP addresses> if <The next loop cannot proceed> then <Raise an exception> end 5IFPWFSWJFXPGUIFQSPDFTTFTJOUIJTMPPQ 5IFDBTFTUBUFNFOUJOUIFQSFWJPVTJNQMFNFOUBUJPO XIJDIEJSFDUFEQSPDFTTJOHCBTFEPOUIFDVSSFOUTUBUF IBTCFFOSFQMBDFEXJUIJGTUBUFNFOUTJOTJEFUIFMPPQ 3FQMBDFDBTFXJUIJGT
<Start a connection> timeout = <Calculate the timeout value> IO.select(<name resolution>, <connections>, nil, <timeout>) if <Sockets are confirmed connected> then <Verify the connection> if <A name resolution is complete> then <Save the resolved IP addresses> if <The next loop cannot proceed> then <Raise an exception> end 5IFPWFSWJFXPGUIFQSPDFTTFTJOUIJTMPPQ 0OFBDIMPPQJUFSBUJPO DPOEJUJPOTBSFDIFDLFEBOE UIFOFDFTTBSZQSPDFTTJOHJTQFSGPSNFE 3FQMBDFDBTFXJUIJGT
<Start a connection> timeout = <Calculate the timeout value> IO.select(<name resolution>, <connections>, nil, <timeout>) if <Sockets are confirmed connected> then <Verify the connection> if <A name resolution is complete> then <Save the resolved IP addresses> if <The next loop cannot proceed> then <Raise an exception> end 5IF fi STUDPOEJUJPO 5IF fi STUDPOEJUJPODIFDLT XIFUIFSBOFXDPOOFDUJPOBUUFNQUDBOCFTUBSUFE 5IFSFRVJSFNFOUTGPSTUBSUJOHBOBUUFNQUBSF
# ... socket = Socket.new(addrinfo.pfamily, addrinfo.socktype, addrinfo.protocol) socket.bind(local_addrinfo) if local_addrinfo result = socket.connect_nonblock(addrinfo, exception: false) if result == :wait_writable connection_attempt_delay_expires_at = now + CONNECTION_ATTEMPT_DELAY # ... end end end DPOOFDUJPO@BUUFNQU@EFMBZ@FYQJSFT@BUJTTFUUP UIFDVSSFOUUJNF NT5IJTSFQSFTFOUTUIFUJNFBU XIJDIUIFOFYUDPOOFDUJPOBUUFNQUDBOCFHJO 5IFDVSSFOUUJNF NT 5IFDPOEJUJPOUPTUBSUBDPOOFDUJPO
# ... socket = Socket.new(addrinfo.pfamily, addrinfo.socktype, addrinfo.protocol) socket.bind(local_addrinfo) if local_addrinfo result = socket.connect_nonblock(addrinfo, exception: false) if result == :wait_writable connection_attempt_delay_expires_at = now + CONNECTION_ATTEMPT_DELAY # ... connecting_sockets[socket] = addrinfo # ... end end end 5IFOTUPSFUIFTPDLFUVTFEGPSUIFDPOOFDUJPOBUUFNQU FYJUUIJTJGTUBUFNFOU 4UPSFUIFTPDLFU 5IFDPOEJUJPOUPTUBSUBDPOOFDUJPO
<Start a connection> timeout = <Calculate the timeout value> IO.select(<name resolution>, <connections>, nil, <timeout>) if <Sockets are confirmed connected> then <Verify the connection> if <A name resolution is complete> then <Save the resolved IP addresses> if <The next loop cannot proceed> then <Raise an exception> end "GUFSUIF fi STUDPOEJUJPO 5IFOFYUTUFQJTUPDBMM*0TFMFDU
<Start a connection> timeout = <Calculate the timeout value> IO.select(<name resolution>, <connections>, nil, <timeout>) if <Sockets are confirmed connected> then <Verify the connection> if <A name resolution is complete> then <Save the resolved IP addresses> if <The next loop cannot proceed> then <Raise an exception> end $BMM*0TFMFDUUPXBJU *OUIFQSFWJPVTJNQMFNFOUBUJPO *0TFMFDUXBTDBMMFEJO UISFFEJ ff FSFOUTUBUFTTUBSU WX BOEWXUPXBJU GPSFJUIFSBDPOOFDUJPOPSOBNFSFTPMVUJPOUPDPNQMFUF
<Start a connection> timeout = <Calculate the timeout value> IO.select(<name resolution>, <connections>, nil, <timeout>) if <Sockets are confirmed connected> then <Verify the connection> if <A name resolution is complete> then <Save the resolved IP addresses> if <The next loop cannot proceed> then <Raise an exception> end $BMM*0TFMFDUUPXBJU *OUIFOFXJNQMFNFOUBUJPO JUJTDPOTPMJEBUFEJOUP BTJOHMFMPDBUJPO XIFSFJUXBJUTGPSFJUIFSBDPOOFDUJPO PSOBNFSFTPMVUJPOUPDPNQMFUF
<Start a connection> timeout = <Calculate the timeout value> IO.select(<name resolution>, <connections>, nil, <timeout>) if <Sockets are confirmed connected> then <Verify the connection> if <A name resolution is complete> then <Save the resolved IP addresses> if <The next loop cannot proceed> then <Raise an exception> end 5PTVQQPSUUIJT MPHJDIBTCFFOBEEFEUPDBMDVMBUF UIFBQQSPQSJBUFUJNFPVUWBMVFUPQBTTUP*0TFMFDU CBTFEPOUIFDVSSFOUTJUVBUJPO $BMM*0TFMFDUUPXBJU
user_specified_connect_timeout_at].compact.max end *OUIJTDBTF JGTPNFVTFSTQFDJ fi FEUJNFPVUWBMVFT BSFQSPWJEFEBTBOBSHVNFOUUP4PDLFUUDQ UIFMBSHFSWBMVFJTVTFE
end *GOPVTFSTQFDJ fi FEUJNFPVUJTHJWFO BXPSLBSPVOEJTBQQMJFEUPSFUVSOBWBMVFUIBU F ff FDUJWFMZXBJUTJOEF fi OJUFMZ EFUBJMTPNJUUFEIFSF $BMDVMBUFUIFUJNFPVUWBMVF
connecting_sockets.keys : nil, second_to_timeout(current_clock_time, ends_at), ) now = current_clock_time if expired?(now, resolution_delay_expires_at) resolution_delay_expires_at = nil end if expired?(now, connection_attempt_delay_expires_at) connection_attempt_delay_expires_at = nil end 0ODF*0TFMFDUVOCMPDLT UIF fi STUTUFQJTUP DIFDLUIFWBMVFTPGUIFUJNFPVUWBSJBCMFT SFTPMVUJPO@EFMBZ@FYQJSFT@BUJTQBTU DPOOFDUJPO@BUUFNQU@EFMBZ@FYQJSFT@BUJTQBTU
<Start a connection> timeout = <Calculate the timeout value> IO.select(<name resolution>, <connections>, nil, <timeout>) if <Sockets are confirmed connected> then <Verify the connection> if <A name resolution is complete> then <Save the resolved IP addresses> if <The next loop cannot proceed> then <Raise an exception> end $IFDLUIFSFTVMUPG*0TFMFDU /FYU CBTFEPOUIFSFTVMUPG*0TFMFDU DIFDLXIFUIFS ɾ0OFPSNPSFXSJUBCMFTPDLFUTBSFBWBJMBCMF ɾ"OBNFSFTPMVUJPOOPUJ fi DBUJPOIBTBSSJWFEPOUIFQJQF $IFDLUIFSFTVMU
if result.is_a? Exception # ... else resolution_store.add_resolved(family_name, result) end end # ... end *GBOBNFSFTPMVUJPOOPUJ fi DBUJPOIBTBSSJWFEPO UIFQJQF SFUSJFWFUIFSFTPMWFEBEESFTTFT BOETUPSFUIFN "OBNFSFTPMVUJPOOPUJ fi DBUJPOIBTBSSJWFE 0CUBJOSFTPMWFEBEESFTTFT 4UPSFUIFPCUBJOFEBEESFTTFT
<Start a connection> timeout = <Calculate the timeout value> IO.select(<name resolution>, <connections>, nil, <timeout>) if <Sockets are confirmed connected> then <Verify the connection> if <A name resolution is complete> then <Save the resolved IP addresses> if <The next loop cannot proceed> then <Raise an exception> end "UUIFFOEPGUIFMPPQ "UUIFFOEPGUIFMPPQ UIFSFJTBDIFDLUPEFUFSNJOF XIFUIFSUIFOFYUMPPQJUFSBUJPODBOQSPDFFE
raise last_error end if (expired?(now, user_specified_resolv_timeout_at) || ...) && (expired?(now, user_specified_connect_timeout_at) || ...) raise Errno::ETIMEDOUT, 'user specified timeout' end end end *GUIFVTFSTQFDJ fi FEUJNFPVUTIBWFCFFOFYDFFEFE SBJTFBUJNFPVUFYDFQUJPO 8IFUIFSUIFOFYUMPPQJUFSBUJPODBOQSPDFFE 3BJTFBUJNFPVUFYDFQUJPO
hostname resolution in child threads> loop do if <The condition to start a connection> then <Start a connection> timeout = <Calculate the timeout value> IO.select(<name resolution>, <connections>, nil, <timeout>) if <Sockets are confirmed connected> then <Verify the connection> if <A name resolution is complete> then <Save the resolved IP addresses> if <The next loop cannot proceed> then <Raise an exception> end end 5IJTSFQFBUTVOUJM4PDLFUUDQSFUVSOTBTVDDFTTGVMMZ DPOOFDUFETPDLFU 8IFUIFSUIFOFYUMPPQJUFSBUJPODBOQSPDFFE
hostname resolution in child threads> loop do if <The condition to start a connection> then <Start a connection> timeout = <Calculate the timeout value> IO.select(<name resolution>, <connections>, nil, <timeout>) if <Sockets are confirmed connected> then <Verify the connection> if <A name resolution is complete> then <Save the resolved IP addresses> if <The next loop cannot proceed> then <Raise an exception> end end 5IFJNQSPWFEWFSTJPOPG4PDLFUUDQ 5IFJNQSPWFEWFSTJPOPG4PDLFUUDQJTOPXDPNQMFUF
= server.addr[1] conn = Net::HTTP.new('localhost', port) conn.write_timeout = EnvUtil.apply_timeout_scale(0.01) conn.open_timeout = EnvUtil.apply_timeout_scale(0.1) th = Thread.new do assert_raise(Net::WriteTimeout) do assert_warning(/Content-Type did not set/) do conn.post('/', "a"*50_000_000) end end end assert th.join(EnvUtil.apply_timeout_scale(10)) } # ... end UFTUOFUIUUQUFTU@IUUQSC "TPG13IUUQTHJUIVCDPNSVCZSVCZQVMM 5IF8JOEPXTTQFDJ fi DQBSUTBSFPNJUUFE
= server.addr[1] conn = Net::HTTP.new('localhost', port) conn.write_timeout = EnvUtil.apply_timeout_scale(0.01) conn.open_timeout = EnvUtil.apply_timeout_scale(0.1) th = Thread.new do assert_raise(Net::WriteTimeout) do assert_warning(/Content-Type did not set/) do conn.post('/', "a"*50_000_000) end end end assert th.join(EnvUtil.apply_timeout_scale(10)) } # ... end *OUIJTUFTU BTFSWFSJT fi STUTUBSUFEPOMPDBMIPTU 4UBSUB5$1TFSWFS UFTUOFUIUUQUFTU@IUUQSC "TPG13IUUQTHJUIVCDPNSVCZSVCZQVMM 5IF8JOEPXTTQFDJ fi DQBSUTBSFPNJUUFE
= server.addr[1] conn = Net::HTTP.new('localhost', port) conn.write_timeout = EnvUtil.apply_timeout_scale(0.01) conn.open_timeout = EnvUtil.apply_timeout_scale(0.1) th = Thread.new do assert_raise(Net::WriteTimeout) do assert_warning(/Content-Type did not set/) do conn.post('/', "a"*50_000_000) end end end assert th.join(EnvUtil.apply_timeout_scale(10)) } # ... end 5IFO UIFDMJFOUTUJNFPVUWBMVFTBSFTFU BXSJUFUJNFPVUPGNT BOEBDPOOFDUUJNFPVUPGNT XSJUFUJNFPVUNT DPOOFDUUJNFPVUNT UFTUOFUIUUQUFTU@IUUQSC "TPG13IUUQTHJUIVCDPNSVCZSVCZQVMM 5IF8JOEPXTTQFDJ fi DQBSUTBSFPNJUUFE
= server.addr[1] conn = Net::HTTP.new('localhost', port) conn.write_timeout = EnvUtil.apply_timeout_scale(0.01) conn.open_timeout = EnvUtil.apply_timeout_scale(0.1) th = Thread.new do assert_raise(Net::WriteTimeout) do assert_warning(/Content-Type did not set/) do conn.post('/', "a"*50_000_000) end end end assert th.join(EnvUtil.apply_timeout_scale(10)) } # ... end 5IFDMJFOUUIFOTUBSUTBDPOOFDUJPOUPUIFTFSWFS 4UBSUBOFXDPOOFDUJPO UFTUOFUIUUQUFTU@IUUQSC "TPG13IUUQTHJUIVCDPNSVCZSVCZQVMM 5IF8JOEPXTTQFDJ fi DQBSUTBSFPNJUUFE
server.addr[1] conn = Net::HTTP.new('localhost', port) conn.write_timeout = EnvUtil.apply_timeout_scale(0.01) conn.open_timeout = EnvUtil.apply_timeout_scale(0.1) th = Thread.new do assert_raise(Net::WriteTimeout) do assert_warning(/Content-Type did not set/) do conn.post('/', "a"*50_000_000) end end end assert th.join(EnvUtil.apply_timeout_scale(10)) } # ... end 5FTU/FU)551@W@@DIVOLFEUFTU@UJNFPVU@EVSJOH@)551@TFTTJPO@XSJUF 4FOEBWFSZMPOHTUSJOH *GUIFDPOOFDUJPOJTTVDDFTTGVM JUBUUFNQUTUPTFOEBWFSZMPOHTUSJOH BTBSFRVFTUNFTTBHFUPUIFTFSWFS UFTUOFUIUUQUFTU@IUUQSC "TPG13IUUQTHJUIVCDPNSVCZSVCZQVMM 5IF8JOEPXTTQFDJ fi DQBSUTBSFPNJUUFE
= server.addr[1] conn = Net::HTTP.new('localhost', port) conn.write_timeout = EnvUtil.apply_timeout_scale(0.01) conn.open_timeout = EnvUtil.apply_timeout_scale(0.1) th = Thread.new do assert_raise(Net::WriteTimeout) do assert_warning(/Content-Type did not set/) do conn.post('/', "a"*50_000_000) end end end assert th.join(EnvUtil.apply_timeout_scale(10)) } # ... end 5IFFYQFDUBUJPOJTUIBUEVSJOHUIFTFOE NTXJMM QBTTBOE/FU8SJUF5JNFPVUBTBXSJUFUJNFPVU XJMMCFSBJTFE UFTUOFUIUUQUFTU@IUUQSC "TPG13IUUQTHJUIVCDPNSVCZSVCZQVMM 5IF8JOEPXTTQFDJ fi DQBSUTBSFPNJUUFE
Failure: > TestNetHTTP_v1_2_chunked#test_timeout_during_HTTP_session_write > [/.../ruby/test/net/http/test_http.rb:572]: > [Net::WriteTimeout] exception expected, > not #<Net::OpenTimeout: Failed to open TCP connection to ...>.
server.addr[1] conn = Net::HTTP.new('localhost', port) conn.write_timeout = EnvUtil.apply_timeout_scale(0.01) conn.open_timeout = EnvUtil.apply_timeout_scale(0.1) th = Thread.new do assert_raise(Net::WriteTimeout) do assert_warning(/Content-Type did not set/) do conn.post('/', "a"*50_000_000) end end end assert th.join(EnvUtil.apply_timeout_scale(10)) } # ... end "OTXFSJOHUIFRVFTUJPO8IZJTUIFUFTUGBJMJOH -FU`TUBLFMPPLBUUIFUFTUDPEFBHBJO 5IFTFSWFSJTTUBSUFECZQBTTJOHMPDBMIPTU UP5$14FSWFSOFX 4UBSUB5$1TFSWFS XJUIIPTUOBNFMPDBMIPTU UFTUOFUIUUQUFTU@IUUQSC "TPG13IUUQTHJUIVCDPNSVCZSVCZQVMM 5IF8JOEPXTTQFDJ fi DQBSUTBSFPNJUUFE
server.addr[1] conn = Net::HTTP.new('localhost', port) conn.write_timeout = EnvUtil.apply_timeout_scale(0.01) conn.open_timeout = EnvUtil.apply_timeout_scale(0.1) th = Thread.new do assert_raise(Net::WriteTimeout) do assert_warning(/Content-Type did not set/) do conn.post('/', "a"*50_000_000) end end end assert th.join(EnvUtil.apply_timeout_scale(10)) } # ... end *OUIFCBDLHSPVOE UIFTFSWFSSFTPMWFTUIFIPTUOBNF MPDBMIPTUBOECJOETJUTMJTUFOJOHTPDLFU UPPOFPGUIFSFTPMWFEBEESFTTFT CJOE 0OFPGUIFSFTPMWFEBEESFTTFT "MJTUFOJOHTPDLFU 4FSWFS "OTXFSJOHUIFRVFTUJPO8IZJTUIFUFTUGBJMJOH UFTUOFUIUUQUFTU@IUUQSC "TPG13IUUQTHJUIVCDPNSVCZSVCZQVMM 5IF8JOEPXTTQFDJ fi DQBSUTBSFPNJUUFE
server.addr[1] conn = Net::HTTP.new('localhost', port) conn.write_timeout = EnvUtil.apply_timeout_scale(0.01) conn.open_timeout = EnvUtil.apply_timeout_scale(0.1) th = Thread.new do assert_raise(Net::WriteTimeout) do assert_warning(/Content-Type did not set/) do conn.post('/', "a"*50_000_000) end end end assert th.join(EnvUtil.apply_timeout_scale(10)) } # ... end 0OBEVBMTUBDLIPTU UIFTFSWFSCJOETUP FJUIFS *1W PS *1W EFQFOEJOHPOXIJDIPOFJTSFTPMWFE fi STU "MJTUFOJOHTPDLFU 4FSWFS PS 8IJDIFWFSSFTPMWFT fi STU CJOE 8IFUIFS*1WPS*1WHFUTSFTPMWFE fi STU EFQFOETPOUIF04BOEOFUXPSLTFUUJOHT "OTXFSJOHUIFRVFTUJPO8IZJTUIFUFTUGBJMJOH UFTUOFUIUUQUFTU@IUUQSC "TPG13IUUQTHJUIVCDPNSVCZSVCZQVMM 5IF8JOEPXTTQFDJ fi DQBSUTBSFPNJUUFE
server.addr[1] conn = Net::HTTP.new('localhost', port) conn.write_timeout = EnvUtil.apply_timeout_scale(0.01) conn.open_timeout = EnvUtil.apply_timeout_scale(0.1) th = Thread.new do assert_raise(Net::WriteTimeout) do assert_warning(/Content-Type did not set/) do conn.post('/', "a"*50_000_000) end end end assert th.join(EnvUtil.apply_timeout_scale(10)) } # ... end *OUIFFOWJSPONFOUXIFSFUIFUFTUXBTGBJMJOH UIFTFSWFSFOEFEVQCJOEJOHUP *1W "MJTUFOJOHTPDLFU 4FSWFS *OUIFFOWJSPONFOU XIFSFUIFUFTUXBTGBJMJOH CJOE "OTXFSJOHUIFRVFTUJPO8IZJTUIFUFTUGBJMJOH UFTUOFUIUUQUFTU@IUUQSC "TPG13IUUQTHJUIVCDPNSVCZSVCZQVMM 5IF8JOEPXTTQFDJ fi DQBSUTBSFPNJUUFE
server.addr[1] conn = Net::HTTP.new('localhost', port) conn.write_timeout = EnvUtil.apply_timeout_scale(0.01) conn.open_timeout = EnvUtil.apply_timeout_scale(0.1) th = Thread.new do assert_raise(Net::WriteTimeout) do assert_warning(/Content-Type did not set/) do conn.post('/', "a"*50_000_000) end end end assert th.join(EnvUtil.apply_timeout_scale(10)) } # ... end UFTUOFUIUUQUFTU@IUUQSC "TPG13IUUQTHJUIVCDPNSVCZSVCZQVMM 5IJTNFBOTUIBUXIJMFXBJUJOHGPSUIFNT $POOFDUJPO"UUFNQU%FMBZ UIFNT DPOOFDUUJNFPVUXBTCFJOHUSJHHFSFE DPOOFDUUJNFPVUNT "OTXFSJOHUIFRVFTUJPO8IZJTUIFUFTUGBJMJOH 5IFNTUJNFPVUFYQJSFTEVSJOHUIFNTXBJU 5IF8JOEPXTTQFDJ fi DQBSUTBSFPNJUUFE
*/ if (in_progress_fds(arg->connection_attempt_fds_size)) { for (/* If there are sockets with a confirmed connection state */) { // Check for connection } if (connected_fd >= 0) break; if (!in_progress_fds(arg->connection_attempt_fds_size)) { if (/* No address candidates available and finished name resolution */) { // Raise an error and exit } connection_attempt_delay_expires_at = NULL; user_specified_connect_timeout_at = NULL; } } // ... FYUTPDLFUJQTPDLFUD 4P *SFTFUDPOOFDUJPO@BUUFNQU@EFMBZ@BUXIFOBMM QSFWJPVTDPOOFDUJPOBUUFNQUTIBWFGBJMFE BMMPXJOHUIF OFYUDPOOFDUJPOUPTUBSUXJUIPVUXBJUJOHNT 3FTFUDPOOFDUJPO@BUUFNQU@EFMBZ@FYQJSFT@BU 5IFSFJTOPTVDDFTTGVMMZDPOOFDUFETPDLFU BOEOPTPDLFUDVSSFOUMZBUUFNQUJOHUPDPOOFDU
open(__FILE__) open_fds << file break if file.fileno >= 1010 end TCPServer.open("localhost", 0) do |server| port = server.addr[1] sockets = [] 50.times do |i| socket = TCPSocket.open("localhost", port) p socket sockets << socket end end IUUQTCVHTSVCZMBOHPSHJTTVFT 3FQSPEVDUJPODPEFCZ!KIBXUIPSO
open(__FILE__) open_fds << file break if file.fileno >= 1010 end TCPServer.open("localhost", 0) do |server| port = server.addr[1] sockets = [] 50.times do |i| socket = TCPSocket.open("localhost", port) p socket sockets << socket end end IUUQTCVHTSVCZMBOHPSHJTTVFT 3FQSPEVDUJPODPEFCZ!KIBXUIPSO 0QFOFOPVHI fi MFTJOBEWBODFTPUIBU UIFUPUBMOVNCFSPGGETDPNFTDMPTF UPUIFTZTUFN`T'%@4&54*;&MJNJU
open(__FILE__) open_fds << file break if file.fileno >= 1010 end TCPServer.open("localhost", 0) do |server| port = server.addr[1] sockets = [] 50.times do |i| socket = TCPSocket.open("localhost", port) p socket sockets << socket end end IUUQTCVHTSVCZMBOHPSHJTTVFT 3FQSPEVDUJPODPEFCZ!KIBXUIPSO 4UBSUBTFSWFS
open(__FILE__) open_fds << file break if file.fileno >= 1010 end TCPServer.open("localhost", 0) do |server| port = server.addr[1] sockets = [] 50.times do |i| socket = TCPSocket.open("localhost", port) p socket sockets << socket end end IUUQTCVHTSVCZMBOHPSHJTTVFT 3FQSPEVDUJPODPEFCZ!KIBXUIPSO 4UBSUTFWFSBMOFXDPOOFDUJPOTUP UIFTFSWFSVTJOH5$14PDLFUPQFO 5$14PDLFUOFX
DESCRIPTION WARNING: select() can monitor only fi le descriptors numbers that are less than FD_SETSIZE (1024)—an unreasonably low limit for many modern applications—and this limitation will not change. All modern applications should instead use poll(2) or epoll(7), which do not su ff er this limitation.
fi le descriptors numbers that are less than FD_SETSIZE (1024)—an unreasonably low limit for many modern applications—and this limitation will not change. All modern applications should instead use poll(2) or epoll(7), which do not su ff er this limitation.
the fd_set type: <sys/time.h>. An fd_set is a fi xed size bu ff er. Executing FD_CLR() or FD_SET() with a value of fd that is negative or is equal to or larger than FD_SETSIZE will result in unde fi ned behavior. Moreover, POSIX requires fd to be a valid fi le descriptor. '%@$-3 BOE'%@4&5 BSFNBDSPT UIBUBSFFYFDVUFECFGPSFDBMMJOHTFMFDU
the fd_set type: <sys/time.h>. An fd_set is a fi xed size bu ff er. Executing FD_CLR() or FD_SET() with a value of fd that is negative or is equal to or larger than FD_SETSIZE will result in unde fi ned behavior. Moreover, POSIX requires fd to be a valid fi le descriptor. 5IJTJTBQSPCMFN
DESCRIPTION WARNING: select() can monitor only fi le descriptors numbers that are less than FD_SETSIZE (1024)—an unreasonably low limit for many modern applications—and this limitation will not change. All modern applications should instead use poll(2) or epoll(7), which do not su ff er this limitation.
le descriptors numbers that are less than FD_SETSIZE (1024)—an unreasonably low limit for many modern applications—and this limitation will not change. All modern applications should instead use poll(2) or epoll(7), which do not su ff er this limitation. IUUQTNBOPSHMJOVYNBOQBHFTNBOTFMFDUIUNM FQPMM JTBO"1*TQFDJ fi DUP-JOVYFOWJSPONFOUT 4JODF5$14PDLFUOFXXJUI)&WJTJOUFOEFEUPXPSLPO BMMFOWJSPONFOUTXIFSFQUISFBETBSFBWBJMBCMF DIPPTJOH QPMM JTUIFNPSFSFBTPOBCMFPQUJPOJOUIJTDBTF
descriptors bigger than FD_SETSIZE in * `select(2)` system call. * * ... (The target OS versions) ... * * When `fd_set` is not big enough to hold big file descriptors, it should be * allocated dynamically. Note that this assumes `fd_set` is structured as * bitmap. * * `rb_fd_init` allocates the memory. * `rb_fd_term` frees the memory. * `rb_fd_set` may re-allocate bitmap. * * So `rb_fd_set` doesn't reject file descriptors bigger than `FD_SETSIZE`. */ 8IBUMBSHFTJ[FIUFMMT )FSF`TXIBU`TXSJUUFOJOMBSHFTJ[FI JODMVEFSVCZJOUFSOBMJOUFSOTFMFDUMBSHFTJ[FI
descriptors bigger than FD_SETSIZE in * `select(2)` system call. * * ... (The target OS versions) ... * * When `fd_set` is not big enough to hold big file descriptors, it should be * allocated dynamically. Note that this assumes `fd_set` is structured as * bitmap. * * `rb_fd_init` allocates the memory. * `rb_fd_term` frees the memory. * `rb_fd_set` may re-allocate bitmap. * * So `rb_fd_set` doesn't reject file descriptors bigger than `FD_SETSIZE`. */ 8IBUMBSHFTJ[FIUFMMT JODMVEFSVCZJOUFSOBMJOUFSOTFMFDUMBSHFTJ[FI
descriptors bigger than FD_SETSIZE in * `select(2)` system call. * * ... (The target OS versions) ... * * When `fd_set` is not big enough to hold big file descriptors, it should be * allocated dynamically. Note that this assumes `fd_set` is structured as * bitmap. * * `rb_fd_init` allocates the memory. * `rb_fd_term` frees the memory. * `rb_fd_set` may re-allocate bitmap. * * So `rb_fd_set` doesn't reject file descriptors bigger than `FD_SETSIZE`. */ JODMVEFSVCZJOUFSOBMJOUFSOTFMFDUMBSHFTJ[FI 8IBUMBSHFTJ[FIUFMMT GE@TFUTUPSFTUIF fi MFEFTDSJQUPSTUPCFNPOJUPSFE
bitmap used by select(2). This * allows Ruby to use FD sets larger than that allowed by historic limitations * on modern platforms. */ typedef struct { int maxfd; /**< Maximum allowed number of FDs. */ fd_set *fdset; /**< File descriptors buffer */ } rb_fdset_t; JODMVEFSVCZJOUFSOBMJOUFSOTFMFDUMBSHFTJ[FI
used by select(2). This * allows Ruby to use FD sets larger than that allowed by historic limitations * on modern platforms. */ typedef struct { int maxfd; /**< Maximum allowed number of FDs. */ fd_set *fdset; /**< File descriptors buffer */ } rb_fdset_t; TUSVDUSC@GETFU@U TUSVDUSC@GETFU@UXSBQTUIFGE@TFU UPCFQBTTFEUPTFMFDU 5IFGE@TFUUPCFQBTTFEUPTFMFDU JODMVEFSVCZJOUFSOBMJOUFSOTFMFDUMBSHFTJ[FI
used by select(2). This * allows Ruby to use FD sets larger than that allowed by historic limitations * on modern platforms. */ typedef struct { int maxfd; /**< Maximum allowed number of FDs. */ fd_set *fdset; /**< File descriptors buffer */ } rb_fdset_t; "OEJUIBTBDPNNFOU JODMVEFSVCZJOUFSOBMJOUFSOTFMFDUMBSHFTJ[FI TUSVDUSC@GETFU@U
match("example.com", Resolv::DNS::Resource::IN::A) do |tx| tx.respond!("127.0.0.1") end match("example.com", Resolv::DNS::Resource::IN::AAAA) do |tx| sleep Float::INFINITY tx.respond!("::1") end otherwise do |query| tx.fail!(:NXDomain) end end *QSFQBSFEB%/4TFSWFSUIBUOFWFSSFUVSOTBOZ*1W BEESFTTFT CVUSFTQPOETJNNFEJBUFMZXJUIBO*1W BEESFTT /FWFSSFUVSOTBO*1WBEESFTT 3FUVSOTBO*1WBEESFTTJNNFEJBUFMZ
fast_fallback: false) > " #<TCPSocket:fd 5, AF_INET, 127.0.0.1, 48004> 0.06s user 0.01s system 0% cpu 15.086 total 5IFPVUDPNFPGJOUSPEVDJOH)&W JUUBLFTBCPVUTFDPOETGPSUIFNFUIPEUPDPNQMFUF 3VCZPO6CVOUV
80 Benchmark.bmbm do |x| x.report("fast_fallback: false") do 100.times { TCPSocket.new(hostname, port, fast_fallback: false).close } end x.report("fast_fallback: true") do 100.times { TCPSocket.new(hostname, port, fast_fallback: true).close } end end 0OUIFPUIFSIBOE SFHBSEJOHUIFJNQBDUPOQFSGPSNBODF SVOBCFODINBSLPWFSFYFDVUJPOT