Class: Yahns::Fdmap
- Inherits:
-
Object
- Object
- Yahns::Fdmap
- Defined in:
- lib/yahns/fdmap.rb
Overview
only initialize this after forking, this is highly volatile and won’t be able to share data across processes at all. This is really a singleton
Instance Method Summary collapse
-
#__expire(timeout) ⇒ Object
expire a bunch of idle clients and register the current one We should not be calling this too frequently, it is expensive This is called while @fdmap_mtx is held.
-
#add(io) ⇒ Object
called immediately after accept().
-
#desperate_expire(timeout) ⇒ Object
this is only called in Errno::EMFILE/Errno::ENFILE situations and graceful shutdown.
-
#forget(io) ⇒ Object
only called on hijack.
-
#initialize(logger, client_expire_threshold) ⇒ Fdmap
constructor
:nodoc:.
-
#size ⇒ Object
used for graceful shutdown.
-
#sync_close(io) ⇒ Object
Yes, we call IO#close inside the lock(!).
Constructor Details
#initialize(logger, client_expire_threshold) ⇒ Fdmap
:nodoc:
10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
# File 'lib/yahns/fdmap.rb', line 10 def initialize(logger, client_expire_threshold) @logger = logger if Float === client_expire_threshold client_expire_threshold *= Process.getrlimit(:NOFILE)[0] elsif client_expire_threshold < 0 client_expire_threshold = Process.getrlimit(:NOFILE)[0] + client_expire_threshold end @client_expire_threshold = client_expire_threshold.to_i # This is an array because any sane OS will frequently reuse FDs # to keep this tightly-packed and favor lower FD numbers # (consider select(2) performance (not that we use select)) # An (unpacked) Hash (in MRI) uses 5 more words per entry than an Array, # and we should expect this array to have around 60K elements @fdmap_ary = [] @fdmap_mtx = Mutex.new @last_expire = 0.0 @count = 0 end |
Instance Method Details
#__expire(timeout) ⇒ Object
expire a bunch of idle clients and register the current one We should not be calling this too frequently, it is expensive This is called while @fdmap_mtx is held
81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 |
# File 'lib/yahns/fdmap.rb', line 81 def __expire(timeout) nr = 0 now = Time.now.to_f (now - @last_expire) >= 1.0 or return # don't expire too frequently # @fdmap_ary may be huge, so always expire a bunch at once to # avoid getting to this method too frequently @fdmap_ary.each do |c| c.respond_to?(:yahns_expire) or next ctimeout = c.class.client_timeout tout = (timeout && timeout < ctimeout) ? timeout : ctimeout nr += c.yahns_expire(tout) end @last_expire = Time.now.to_f msg = timeout ? "timeout=#{timeout}" : "client_timeout" @logger.info("dropping #{nr} of #@count clients for #{msg}") end |
#add(io) ⇒ Object
called immediately after accept()
52 53 54 55 56 57 58 59 60 |
# File 'lib/yahns/fdmap.rb', line 52 def add(io) fd = io.fileno @fdmap_mtx.synchronize do if (@count += 1) > @client_expire_threshold __expire(nil) end @fdmap_ary[fd] = io end end |
#desperate_expire(timeout) ⇒ Object
this is only called in Errno::EMFILE/Errno::ENFILE situations and graceful shutdown
64 65 66 |
# File 'lib/yahns/fdmap.rb', line 64 def desperate_expire(timeout) @fdmap_mtx.synchronize { __expire(timeout) } end |
#forget(io) ⇒ Object
only called on hijack
69 70 71 72 73 74 75 76 |
# File 'lib/yahns/fdmap.rb', line 69 def forget(io) fd = io.fileno @fdmap_mtx.synchronize do # prevent rack.hijacked IOs from being expired by us @fdmap_ary[fd] = nil @count -= 1 end end |
#size ⇒ Object
used for graceful shutdown
101 102 103 |
# File 'lib/yahns/fdmap.rb', line 101 def size @fdmap_mtx.synchronize { @count } end |
#sync_close(io) ⇒ Object
Yes, we call IO#close inside the lock(!)
We don’t want to race with __expire. Theoretically, a Ruby implementation w/o GVL may end up issuing shutdown(2) on the same fd as one which got accept-ed (a brand new IO object) so we must prevent IO#close in worker threads from racing with any threads which may run __expire
We must never, ever call this while it is capable of being on the epoll ready list and returnable by epoll_wait. So we can only call this on objects which were epoll_ctl-ed with EPOLLONESHOT (and now retrieved).
44 45 46 47 48 49 |
# File 'lib/yahns/fdmap.rb', line 44 def sync_close(io) @fdmap_mtx.synchronize do @count -= 1 io.close end end |