Class: SleepyPenguin::Kqueue

Inherits:
Object
  • Object
show all
Defined in:
lib/sleepy_penguin/kqueue.rb,
ext/sleepy_penguin/kqueue.c

Overview

The high-level Kqueue interface. This provides fork-safety; as underlying kqueue descriptors are closed by the OS upon fork. This also provides memory protection from bugs due to not storing an external reference to an object, but still requires the user to store their own object references. Events registered to a Kqueue object cannot be shared across fork due to the underlying implementation of kqueue in *BSDs.

Defined Under Namespace

Classes: IO

Instance Method Summary collapse

Constructor Details

#initializeKqueue

Initialize a new Kqueue object, this allocates an underlying Kqueue::IO object and may fail if the system is out of file descriptors or kernel memory



16
17
18
19
20
21
# File 'lib/sleepy_penguin/kqueue.rb', line 16

def initialize
  @io = SleepyPenguin::Kqueue::IO.new
  @mtx = Mutex.new
  @pid = $$
  @copies = { @io => self }
end

Instance Method Details

#__kq_checkObject

:nodoc:



35
36
37
38
39
40
41
42
43
44
45
46
47
48
# File 'lib/sleepy_penguin/kqueue.rb', line 35

def __kq_check # :nodoc:
  return if @pid == $$ || @io.closed?

  # kqueue has (strange) close-on-fork behavior
  objects = @copies.values
  @copies.each_key { |kqio| kqio.autoclose = false }
  @copies.clear
  __kq_reinit
  objects.each do |obj|
    io_dup = @io.dup
    @copies[io_dup] = obj
  end
  @pid = $$
end

#__kq_reinitObject

:nodoc:



31
32
33
# File 'lib/sleepy_penguin/kqueue.rb', line 31

def __kq_reinit # :nodoc:
  @io = SleepyPenguin::Kqueue::IO.new
end

#closeObject

call-seq:

kq.close -> nil

Closes an existing Kqueue object and returns memory back to the kernel. Raises IOError if object is already closed.



106
107
108
109
110
111
# File 'lib/sleepy_penguin/kqueue.rb', line 106

def close
  @mtx.synchronize do
    @copies.delete(@io)
    @io.close
  end
end

#closed?Boolean

call-seq:

kq.closed? -> true or false

Returns whether or not an Kqueue object is closed.

Returns:

  • (Boolean)


117
118
119
120
121
# File 'lib/sleepy_penguin/kqueue.rb', line 117

def closed?
  @mtx.synchronize do
    @io.closed?
  end
end

#initialize_copy(src) ⇒ Object

:nodoc:



89
90
91
92
93
94
95
96
97
98
99
# File 'lib/sleepy_penguin/kqueue.rb', line 89

def initialize_copy(src) # :nodoc:
  @mtx.synchronize do
    __kq_check
    rv = super
    unless @io.closed?
      @io = @io.dup
      @copies[@io] = self
    end
    rv
  end
end

#kevent(changelist = nil, *args) ⇒ Object

A high-level wrapper around Kqueue::IO#kevent Users are responsible for ensuring udata objects remain visible to the Ruby GC, otherwise ObjectSpace._id2ref may return invalid objects. Unlike the low-level Kqueue::IO#kevent, the block given yields only a single Kevent struct, not a 6-element array.

As of sleepy_penguin 3.5.0+, it is possible to nest #kevent calls within the same thread.



58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
# File 'lib/sleepy_penguin/kqueue.rb', line 58

def kevent(changelist = nil, *args)
  @mtx.synchronize { __kq_check }
  if changelist
    changelist = [ changelist ] if Struct === changelist

    # store the object_id instead of the raw VALUE itself in kqueue and
    # use _id2ref to safely recover the object without the possibility of
    # invalid memory acccess.
    #
    # We may still raise and drop events due to user error
    changelist = changelist.map do |item|
      item = item.dup
      item[5] = item[5].object_id
      item
    end
  end

  if block_given?
    @io.kevent(changelist, *args) do |ident,filter,flags,
                                             fflags,data,udata|
      # This may raise and cause events to be lost,
      # that's the users' fault/problem
      udata = ObjectSpace._id2ref(udata)
      yield SleepyPenguin::Kevent.new(ident, filter, flags,
                                      fflags, data, udata)
    end
  else
    @io.kevent(changelist, *args)
  end
end

#to_ioObject

Kqueue objects may be watched by IO.select and similar methods



24
25
26
27
28
29
# File 'lib/sleepy_penguin/kqueue.rb', line 24

def to_io
  @mtx.synchronize do
    __kq_check
    @io
  end
end