Class: Yahns::Queue

Inherits:
SleepyPenguin::Epoll::IO
  • Object
show all
Includes:
SleepyPenguin
Defined in:
lib/yahns/queue_epoll.rb

Overview

Copyright © 2013, Eric Wong <[email protected]> and all contributors License: GPLv3 or later (www.gnu.org/licenses/gpl-3.0.txt)

This is the dangerous, low-level epoll interface for sleepy_penguin It is safe as long as you’re aware of all potential concurrency issues given multithreading, GC, and epoll itself.

Constant Summary collapse

QEV_QUIT =

public

Epoll::OUT
QEV_RD =

Level Trigger for QueueQuitter

Epoll::IN | Epoll::ONESHOT
QEV_WR =
Epoll::OUT | Epoll::ONESHOT

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Instance Attribute Details

#fdmapObject

Yahns::Fdmap



10
11
12
# File 'lib/yahns/queue_epoll.rb', line 10

def fdmap
  @fdmap
end

Class Method Details

.newObject



17
18
19
# File 'lib/yahns/queue_epoll.rb', line 17

def self.new
  super(SleepyPenguin::Epoll::CLOEXEC)
end

Instance Method Details

#queue_add(io, flags) ⇒ Object

for HTTP and HTTPS servers, we rely on the io writing to us, first flags: QEV_RD/QEV_WR (usually QEV_RD)



23
24
25
26
27
28
# File 'lib/yahns/queue_epoll.rb', line 23

def queue_add(io, flags)
  # order is very important here, this thread cannot do anything with
  # io once we've issued epoll_ctl() because another thread may use it
  @fdmap.add(io)
  epoll_ctl(Epoll::CTL_ADD, io, flags)
end

#queue_del(io) ⇒ Object

use only before hijacking, once hijacked, io may be unusable to us It is not safe to call this unless it is an unarmed EPOLLONESHOT object.



38
39
40
41
42
43
44
# File 'lib/yahns/queue_epoll.rb', line 38

def queue_del(io)
  # order does not really matter here, however Epoll::CTL_DEL
  # will free up ~200 bytes of unswappable kernel memory,
  # so we call it first
  epoll_ctl(Epoll::CTL_DEL, io, 0)
  @fdmap.forget(io)
end

#thr_initObject



30
31
32
33
# File 'lib/yahns/queue_epoll.rb', line 30

def thr_init
  Thread.current[:yahns_rbuf] = ""
  Thread.current[:yahns_fdmap] = @fdmap
end

#worker_thread(logger, max_events) ⇒ Object

returns an array of infinitely running threads



47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
# File 'lib/yahns/queue_epoll.rb', line 47

def worker_thread(logger, max_events)
  Thread.new do
    thr_init
    begin
      epoll_wait(max_events) do |_, io| # don't care for flags for now

        # Note: we absolutely must not do anything with io after
        # we've called epoll_ctl on it, io is exclusive to this
        # thread only until epoll_ctl is called on it.
        case rv = io.yahns_step
        when :wait_readable
          epoll_ctl(Epoll::CTL_MOD, io, QEV_RD)
        when :wait_writable
          epoll_ctl(Epoll::CTL_MOD, io, QEV_WR)
        when :ignore # only used by rack.hijack
          # we cannot call Epoll::CTL_DEL after hijacking, the hijacker
          # may have already closed it  Likewise, io.fileno is not
          # expected to work, so we had to erase it from fdmap before hijack
        when nil, :close
          # this must be the ONLY place where we call IO#close on
          # things that got inside the queue
          @fdmap.sync_close(io)
        else
          raise "BUG: #{io.inspect}#yahns_step returned: #{rv.inspect}"
        end
      end
    rescue => e
      break if closed? # can still happen due to shutdown_timeout
      Yahns::Log.exception(logger, 'queue loop', e)
    end while true
  end
end