# frozen_string_literal: true# Released under the MIT License.# Copyright, 2011-2017, by Tony Arcieri.# Copyright, 2012, by Logan Bowers.# Copyright, 2013, by Sadayuki Furuhashi.# Copyright, 2013, by Stephen von Takach.# Copyright, 2013, by Tim Carey-Smith.# Copyright, 2013, by Ravil Bayramgalin.# Copyright, 2014, by Sergey Avseyev.# Copyright, 2014, by John Thornton.# Copyright, 2015, by Vladimir Kochnev.# Copyright, 2015, by Upekshe Jayasekera.# Copyright, 2019-2020, by Gregory Longtin.# Copyright, 2020-2021, by Joao Fernandes.# Copyright, 2023, by Samuel Williams.require"set"moduleNIO# Selectors monitor IO objects for events of interestclassSelector# Return supported backends as symbols## See `#backend` method definition for all possible backendsdefself.backends[:ruby]end# Create a new NIO::Selectordefinitialize(backend=:ruby)raiseArgumentError,"unsupported backend: #{backend}"unless[:ruby,nil].include?(backend)@selectables={}@lock=Mutex.new# Other threads can wake up a selector@wakeup,@waker=IO.pipe@closed=falseend# Return a symbol representing the backend I/O multiplexing mechanism used.# Supported backends are:# * :ruby - pure Ruby (i.e IO.select)# * :java - Java NIO on JRuby# * :epoll - libev w\ Linux epoll# * :poll - libev w\ POSIX poll# * :kqueue - libev w\ BSD kqueue# * :select - libev w\ SysV select# * :port - libev w\ I/O completion ports# * :linuxaio - libev w\ Linux AIO io_submit (experimental)# * :io_uring - libev w\ Linux io_uring (experimental)# * :unknown - libev w\ unknown backenddefbackend:rubyend# Register interest in an IO object with the selector for the given types# of events. Valid event types for interest are:# * :r - is the IO readable?# * :w - is the IO writeable?# * :rw - is the IO either readable or writeable?defregister(io,interest)unlessdefined?(::OpenSSL)&&io.is_a?(::OpenSSL::SSL::SSLSocket)io=IO.try_convert(io)end@lock.synchronizedoraiseIOError,"selector is closed"ifclosed?monitor=@selectables[io]raiseArgumentError,"already registered as #{monitor.interests.inspect}"ifmonitormonitor=Monitor.new(io,interest,self)@selectables[monitor.io]=monitormonitorendend# Deregister the given IO object from the selectordefderegister(io)@lock.synchronizedomonitor=@selectables.deleteIO.try_convert(io)monitor.close(false)ifmonitor&&!monitor.closed?monitorendend# Is the given IO object registered with the selector?defregistered?(io)@lock.synchronize{@selectables.key?io}end# Select which monitors are readydefselect(timeout=nil)selected_monitors=Set.new@lock.synchronizedoreaders=[@wakeup]writers=[]@selectables.eachdo|io,monitor|readers<<ioifmonitor.interests==:r||monitor.interests==:rwwriters<<ioifmonitor.interests==:w||monitor.interests==:rwmonitor.readiness=nilendready_readers,ready_writers=Kernel.select(readers,writers,[],timeout)returnunlessready_readers# timeoutready_readers.eachdo|io|ifio==@wakeup# Clear all wakeup signals we've received by reading them# Wakeups should have level triggered behavior@wakeup.read(@wakeup.stat.size)elsemonitor=@selectables[io]monitor.readiness=:rselected_monitors<<monitorendendready_writers.eachdo|io|monitor=@selectables[io]monitor.readiness=monitor.readiness==:r?:rw::wselected_monitors<<monitorendendifblock_given?selected_monitors.each{|m|yieldm}selected_monitors.sizeelseselected_monitors.to_aendend# Wake up a thread that's in the middle of selecting on this selector, if# any such thread exists.## Invoking this method more than once between two successive select calls# has the same effect as invoking it just once. In other words, it provides# level-triggered behavior.defwakeup# Send the selector a signal in the form of writing data to a pipebegin@waker.write_nonblock"\0"rescueIO::WaitWritable# This indicates the wakeup pipe is full, which means the other thread# has already received many wakeup calls, but not processed them yet.# The other thread will completely drain this pipe when it wakes up,# so it's ok to ignore this exception if it occurs: we know the other# thread has already been signaled to wake upendnilend# Close this selector and free its resourcesdefclose@lock.synchronizedoreturnif@closedbegin@wakeup.closerescueIOErrorendbegin@waker.closerescueIOErrorend@closed=trueendend# Is this selector closed?defclosed?@closedenddefempty?@selectables.empty?endendend