kqueue: get rid of unused variable warnings
[sleepy_penguin.git] / lib / sleepy_penguin / kqueue.rb
blobd1a1577c259fb1abba28a5069ee5b5f48198f041
1 require 'thread'
2 require_relative 'kevent'
4 # The high-level Kqueue interface.  This provides fork-safety; as
5 # underlying kqueue descriptors are closed by the OS upon fork.
6 # This also provides memory protection from bugs due to not storing an
7 # external reference to an object, but still requires the user to store
8 # their own object references.
9 # Events registered to a Kqueue object cannot be shared across fork
10 # due to the underlying implementation of kqueue in *BSDs.
11 class SleepyPenguin::Kqueue
13   # Initialize a new Kqueue object, this allocates an underlying Kqueue::IO
14   # object and may fail if the system is out of file descriptors or
15   # kernel memory
16   def initialize
17     @io = SleepyPenguin::Kqueue::IO.new
18     @mtx = Mutex.new
19     @pid = $$
20     @copies = { @io => self }
21   end
23   # Kqueue objects may be watched by IO.select and similar methods
24   def to_io
25     @mtx.synchronize do
26       __kq_check
27       @io
28     end
29   end
31   def __kq_reinit # :nodoc:
32     @io = SleepyPenguin::Kqueue::IO.new
33   end
35   def __kq_check # :nodoc:
36     return if @pid == $$ || @io.closed?
38     # kqueue has (strange) close-on-fork behavior
39     objects = @copies.values
40     @copies.each_key { |kqio| kqio.autoclose = false }
41     @copies.clear
42     __kq_reinit
43     objects.each do |obj|
44       io_dup = @io.dup
45       @copies[io_dup] = obj
46     end
47     @pid = $$
48   end
50   # A high-level wrapper around Kqueue::IO#kevent
51   # Users are responsible for ensuring +udata+ objects remain visible to the
52   # Ruby GC, otherwise ObjectSpace._id2ref may return invalid objects.
53   # Unlike the low-level Kqueue::IO#kevent, the block given yields only
54   # a single Kevent struct, not a 6-element array.
55   #
56   # As of sleepy_penguin 3.5.0+, it is possible to nest #kevent
57   # calls within the same thread.
58   def kevent(changelist = nil, *args)
59     @mtx.synchronize { __kq_check }
60     if changelist
61       changelist = [ changelist ] if Struct === changelist
63       # store the object_id instead of the raw VALUE itself in kqueue and
64       # use _id2ref to safely recover the object without the possibility of
65       # invalid memory acccess.
66       #
67       # We may still raise and drop events due to user error
68       changelist = changelist.map do |item|
69         item = item.dup
70         item[5] = item[5].object_id
71         item
72       end
73     end
75     if block_given?
76       @io.kevent(changelist, *args) do |ident,filter,flags,
77                                                fflags,data,udata|
78         # This may raise and cause events to be lost,
79         # that's the users' fault/problem
80         udata = ObjectSpace._id2ref(udata)
81         yield SleepyPenguin::Kevent.new(ident, filter, flags,
82                                         fflags, data, udata)
83       end
84     else
85       @io.kevent(changelist, *args)
86     end
87   end
89   def initialize_copy(src) # :nodoc:
90     @mtx.synchronize do
91       __kq_check
92       rv = super
93       unless @io.closed?
94         @io = @io.dup
95         @copies[@io] = self
96       end
97       rv
98     end
99   end
101   # call-seq:
102   #     kq.close -> nil
103   #
104   # Closes an existing Kqueue object and returns memory back to the kernel.
105   # Raises IOError if object is already closed.
106   def close
107     @mtx.synchronize do
108       @copies.delete(@io)
109       @io.close
110     end
111   end
113   # call-seq:
114   #     kq.closed? -> true or false
115   #
116   # Returns whether or not an Kqueue object is closed.
117   def closed?
118     @mtx.synchronize do
119       @io.closed?
120     end
121   end