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
17 @io = SleepyPenguin::Kqueue::IO.new
20 @copies = { @io => self }
23 # Kqueue objects may be watched by IO.select and similar methods
31 def __kq_reinit # :nodoc:
32 @io = SleepyPenguin::Kqueue::IO.new
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 }
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.
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 }
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.
67 # We may still raise and drop events due to user error
68 changelist = changelist.map do |item|
70 item[5] = item[5].object_id
76 @io.kevent(changelist, *args) do |ident,filter,flags,
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,
85 @io.kevent(changelist, *args)
89 def initialize_copy(src) # :nodoc:
104 # Closes an existing Kqueue object and returns memory back to the kernel.
105 # Raises IOError if object is already closed.
114 # kq.closed? -> true or false
116 # Returns whether or not an Kqueue object is closed.