rb-kqueue-burke 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/.gitignore ADDED
@@ -0,0 +1,3 @@
1
+ /pkg
2
+ /.yardoc
3
+ /doc
data/.yardopts ADDED
@@ -0,0 +1,5 @@
1
+ --readme README.md
2
+ --markup markdown
3
+ --markup-provider maruku
4
+ --hide-void-return
5
+ --no-private
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2010 Nathan Weizenbaum
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,44 @@
1
+ # rb-kqueue
2
+
3
+ This is a simple wrapper over the [kqueue](http://en.wikipedia.org/wiki/Kqueue)
4
+ BSD event notification interface (supported on FreeBSD, NetBSD, OpenBSD, and Darwin).
5
+ It uses the [FFI](http://wiki.github.com/ffi/ffi) gem to avoid having to compile a C extension.
6
+
7
+ [API documentation is available on rdoc.info](http://rdoc.info/projects/nex3/rb-kqueue).
8
+
9
+ ## WARNING
10
+
11
+ This code is incomplete, and didn't work last I had a chance to test it.
12
+ I don't have time to continue working on it at the moment,
13
+ so I'm posting it online for posterity and in case anyone wants to take a crack at it.
14
+
15
+ If anyone wants commit rights, just email me at nex342@gmail.com.
16
+
17
+ ## Usage
18
+
19
+ The API is similar to the kqueue C API, but with a more Rubyish feel.
20
+ First, create a queue:
21
+
22
+ queue = KQueue::Queue.new
23
+
24
+ Then, tell it to watch the events you're interested in:
25
+
26
+ queue.watch_file("path/to/foo.txt", :write) {puts "foo.txt was modified!"}
27
+ queue.watch_process(Process.pid, :fork, :exec) do |event|
28
+ puts "This process has #{event.flags.map {|f| f.to_s + "ed"}.join(" and ")}"
29
+ end
30
+
31
+ KQueue can monitor for all sorts of events.
32
+ For a full list, see the `watch_*` methods on {Queue}.
33
+
34
+ Finally, run the queue:
35
+
36
+ queue.run
37
+
38
+ This will loop infinitely, calling the appropriate callbacks when the events are fired.
39
+ If you don't want infinite looping,
40
+ you can also block until there are available events,
41
+ process them all at once,
42
+ and then continue on your merry way:
43
+
44
+ queue.process
data/Rakefile ADDED
@@ -0,0 +1,51 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ begin
5
+ require 'jeweler'
6
+ Jeweler::Tasks.new do |gem|
7
+ gem.name = "rb-kqueue"
8
+ gem.summary = "A Ruby wrapper for BSD's kqueue, using FFI"
9
+ gem.description = gem.summary
10
+ gem.email = "nex342@gmail.com"
11
+ gem.homepage = "http://github.com/nex3/rb-kqueue"
12
+ gem.authors = ["Nathan Weizenbaum"]
13
+ gem.add_dependency "ffi", ">= 0.5.0"
14
+ gem.add_development_dependency "yard", ">= 0.4.0"
15
+ end
16
+ Jeweler::GemcutterTasks.new
17
+ rescue LoadError
18
+ puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
19
+ end
20
+
21
+ module Jeweler::VersionHelper::PlaintextExtension
22
+ def write_with_kqueue
23
+ write_without_kqueue
24
+ filename = File.join(File.dirname(__FILE__), "lib/rb-kqueue.rb")
25
+ text = File.read(filename)
26
+ File.open(filename, 'w') do |f|
27
+ f.write text.gsub(/^( VERSION = ).*/, '\1' + [major, minor, patch].inspect)
28
+ end
29
+ end
30
+ alias_method :write_without_kqueue, :write
31
+ alias_method :write, :write_with_kqueue
32
+ end
33
+
34
+ class Jeweler::Commands::Version::Base
35
+ def commit_version_with_kqueue
36
+ return unless self.repo
37
+ self.repo.add(File.join(File.dirname(__FILE__), "lib/rb-kqueue.rb"))
38
+ commit_version_without_kqueue
39
+ end
40
+ alias_method :commit_version_without_kqueue, :commit_version
41
+ alias_method :commit_version, :commit_version_with_kqueue
42
+ end
43
+
44
+ begin
45
+ require 'yard'
46
+ YARD::Rake::YardocTask.new
47
+ rescue LoadError
48
+ task :yardoc do
49
+ abort "YARD is not available. In order to run yardoc, you must: sudo gem install yard"
50
+ end
51
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.0
data/lib/rb-kqueue.rb ADDED
@@ -0,0 +1,40 @@
1
+ require 'rb-kqueue/native'
2
+ require 'rb-kqueue/native/flags'
3
+ require 'rb-kqueue/watcher'
4
+ require 'rb-kqueue/watcher/file'
5
+ require 'rb-kqueue/watcher/read_write'
6
+ require 'rb-kqueue/watcher/process'
7
+ require 'rb-kqueue/event'
8
+ require 'rb-kqueue/queue'
9
+
10
+ # The root module of the library, which is laid out as so:
11
+ #
12
+ # * {Queue} -- The main class, where events are registered
13
+ # * {Watcher} -- A watcher for a single sort of event
14
+ # * {Event} -- A notification that an event has occurred
15
+ module KQueue
16
+ VERSION = [0, 1, 0]
17
+
18
+ # Raise an exception for a native kqueue error.
19
+ #
20
+ # @param errno [Fixnum] The errno identifying the sort of error.
21
+ # This is usually C's `errno` variable,
22
+ # but is sometimes set in a kevent struct
23
+ # @raise [SystemCallError]
24
+ # @private
25
+ def self.handle_error(errno = FFI.errno)
26
+ raise SystemCallError.new(
27
+ "KQueue failed" +
28
+ case errno
29
+ when Errno::EFAULT::Errno; ": There was an error reading or writing the kevent structure."
30
+ when Errno::EBADF::Errno; ": The specified descriptor is invalid."
31
+ when Errno::EINTR::Errno; ": A signal was delivered before the timeout expired and before any events were placed on the kqueue for return."
32
+ when Errno::EINVAL::Errno; ": The specified time limit or filter is invalid."
33
+ when Errno::ENOENT::Errno; ": The event could not be found to be modified or deleted."
34
+ when Errno::ENOMEM::Errno; ": No memory was available to register the event."
35
+ when Errno::ESRCH::Errno; ": The specified process to attach to does not exist."
36
+ else; ""
37
+ end,
38
+ errno)
39
+ end
40
+ end
@@ -0,0 +1,83 @@
1
+ module KQueue
2
+ # An event produced by kqueue.
3
+ # Each {Watcher} can fire many events,
4
+ # which are passed to that Watcher's callback.
5
+ class Event
6
+ # Some integer data, the interpretation of which
7
+ # is specific to each individual {Watcher}.
8
+ # For specifics, see the individual Watcher subclasses.
9
+ #
10
+ # `data` is not meaningful for all events.
11
+ # For example, file-change notifications do not set `data`.
12
+ #
13
+ # @return [Fixnum]
14
+ attr_reader :data
15
+
16
+ # The name of the kqueue filter that created this event,
17
+ # e.g. `:vnode` or `:read`.
18
+ #
19
+ # @private
20
+ # @return [Symbol]
21
+ attr_reader :filter
22
+
23
+ # The {Watcher} that produced this event.
24
+ #
25
+ # @return [Watcher]
26
+ def watcher
27
+ @watcher ||= @queue.watchers[[filter, @native[:ident]]]
28
+ end
29
+
30
+ # An array of flags, the interpretation of which
31
+ # is specific to each individual {Watcher}.
32
+ #
33
+ # If the Watcher watches for different sorts of events,
34
+ # this is usually the specific events that actually occurred.
35
+ # For example, for file-change notifications this could be `[:delete]`.
36
+ #
37
+ # `flags` is not meaningful for all events.
38
+ # For example, readability notifications do not set `flags`.
39
+ #
40
+ # @return [Array<Symbol>]
41
+ def flags
42
+ @fflags ||= Native::Flags.from_mask("NOTE_#{filter.to_s.upcase}", @native[:fflags])
43
+ end
44
+
45
+ # Returns whether the end-of-file flag has been set for this event.
46
+ # The interpretation of this is specific to each individual {Watcher}.
47
+ #
48
+ # `eof?` is not meaningful for all events.
49
+ # For example, file-change notifications don't set `eof?`.
50
+ #
51
+ # @return [Boolean]
52
+ def eof?
53
+ @flags.include?(:eof)
54
+ end
55
+
56
+ # Creates a new event from a native event structure.
57
+ #
58
+ # @private
59
+ # @param native [Native::Event] The native event structure
60
+ # from which to construct this event
61
+ # @param queue [Queue] The queue that produced this event
62
+ # @raise [SystemCallError] If this event signals an error
63
+ def initialize(native, queue)
64
+ @native = native
65
+ @queue = queue
66
+ @data = @native[:data]
67
+ @filter = KQueue::Native::Flags.from_flag("EVFILT", @native[:filter])
68
+ @flags = Native::Flags.from_mask("EV", @native[:flags])
69
+
70
+ KQueue.handle_error @native[:data] if @flags.include?(:error)
71
+ end
72
+
73
+ # Runs the callback for this event.
74
+ # This callback is associated with the {Watcher}
75
+ # that produced the event.
76
+ #
77
+ # @private
78
+ # @return [void]
79
+ def callback!
80
+ watcher.callback! self
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,41 @@
1
+ require 'ffi'
2
+
3
+ module KQueue
4
+ # This module contains the low-level foreign-function interface code
5
+ # for dealing with the kqueue C APIs.
6
+ # It's an implementation detail, and not meant for users to deal with.
7
+ #
8
+ # @private
9
+ module Native
10
+ extend FFI::Library
11
+ ffi_lib FFI::Library::LIBC
12
+
13
+ # The C struct describing a kqueue event.
14
+ #
15
+ # @private
16
+ class KEvent < FFI::Struct
17
+ layout(
18
+ :ident, :uintptr_t,
19
+ :filter, :int16,
20
+ :flags, :uint16,
21
+ :fflags, :uint32,
22
+ :data, :intptr_t,
23
+ :udata, :pointer)
24
+ end
25
+
26
+ # The C struct describing a timeout.
27
+ #
28
+ # @private
29
+ class TimeSpec < FFI::Struct
30
+ layout(
31
+ :tv_sec, :time_t,
32
+ :tv_nsec, :long)
33
+ end
34
+
35
+ attach_function :kqueue, [], :int
36
+ attach_function :kevent, [:int, :pointer, :int, :pointer, :int, :pointer], :int
37
+
38
+ attach_function :open, [:string, :int], :int
39
+ attach_function :close, [:int], :int
40
+ end
41
+ end
@@ -0,0 +1,114 @@
1
+ module KQueue
2
+ module Native
3
+ # A module containing all the C-level integer flags
4
+ # that are used with kqueue.
5
+ #
6
+ # @private
7
+ module Flags
8
+ # Filters
9
+ EVFILT_READ = -1
10
+ EVFILT_WRITE = -2
11
+ EVFILT_AIO = -3 # Attached to aio requests
12
+ EVFILT_VNODE = -4 # Attached to vnodes
13
+ EVFILT_PROC = -5 # Attached to struct proc
14
+ EVFILT_SIGNAL = -6 # Attached to struct proc
15
+ EVFILT_TIMER = -7 # Timers
16
+ EVFILT_MACHPORT = -8 # Mach portsets
17
+ EVFILT_FS = -9 # Filesystem events
18
+ EVFILT_USER = -10 # User events
19
+ EVFILT_SESSION = -11 # Audit session events
20
+
21
+
22
+ # Actions
23
+ EV_ADD = 0x0001 # Add event to kq (implies enable)
24
+ EV_DELETE = 0x0002 # Delete event from kq
25
+ EV_ENABLE = 0x0004 # Enable event
26
+ EV_DISABLE = 0x0008 # Disable event (not reported)
27
+ EV_RECEIPT = 0x0040 # Force EV_ERROR on success, data == 0
28
+
29
+ # Flags
30
+ EV_ONESHOT = 0x0010 # Only report one occurrence
31
+ EV_CLEAR = 0x0020 # Clear event state after reporting
32
+ EV_DISPATCH = 0x0080 # Disable event after reporting
33
+
34
+ # Returned values
35
+ EV_EOF = 0x8000 # EOF detected
36
+ EV_ERROR = 0x4000 # Error, data contains errno
37
+
38
+
39
+ # For `EVFILT_{READ,WRITE}`
40
+ NOTE_READ_LOWAT = NOTE_WRITE_LOWAT = 0x00000001 # Low water mark
41
+
42
+ # For `EVFILT_VNODE`
43
+ NOTE_VNODE_DELETE = 0x00000001 # Vnode was removed
44
+ NOTE_VNODE_WRITE = 0x00000002 # Data contents changed
45
+ NOTE_VNODE_EXTEND = 0x00000004 # Size increased
46
+ NOTE_VNODE_ATTRIB = 0x00000008 # Attributes changed
47
+ NOTE_VNODE_LINK = 0x00000010 # Link count changed
48
+ NOTE_VNODE_RENAME = 0x00000020 # Vnode was renamed
49
+ NOTE_VNODE_REVOKE = 0x00000040 # Vnode access was revoked
50
+
51
+ # For `EVFILT_PROC`
52
+ NOTE_PROC_EXIT = 0x80000000 # Process exited
53
+ NOTE_PROC_FORK = 0x40000000 # Process forked
54
+ NOTE_PROC_EXEC = 0x20000000 # Process exec'd
55
+ NOTE_PROC_REAP = 0x10000000 # Process reaped
56
+ NOTE_PROC_SIGNAL = 0x08000000 # Received signal
57
+ NOTE_PROC_TRACK = 0x00000001 # follow across forks
58
+ NOTE_PROC_TRACKERR = 0x00000002 # could not track child
59
+ NOTE_PROC_CHILD = 0x00000004 # am a child process
60
+
61
+ # For `EVFILT_TIMER`
62
+ NOTE_TIMER_SECONDS = 0x00000001 # data is seconds
63
+ NOTE_TIMER_USECONDS = 0x00000002 # data is microseconds
64
+ NOTE_TIMER_NSECONDS = 0x00000004 # data is nanoseconds
65
+ NOTE_TIMER_ABSOLUTE = 0x00000008 # absolute timeout
66
+
67
+
68
+ # Converts a list of flags to the bitmask that the C API expects.
69
+ #
70
+ # @param prefix [String] The prefix for the C names of the flags
71
+ # @param flags [Array<Symbol>]
72
+ # @return [Fixnum]
73
+ def self.to_mask(prefix, flags)
74
+ flags.map {|flag| const_get("#{prefix}_#{flag.to_s.upcase}")}.
75
+ inject(0) {|mask, flag| mask | flag}
76
+ end
77
+
78
+ # Converts a bitmask from the C API into a list of flags.
79
+ #
80
+ # @param prefix [String] The prefix for the C names of the flags
81
+ # @param mask [Fixnum]
82
+ # @return [Array<Symbol>]
83
+ def self.from_mask(prefix, mask)
84
+ re = /^#{Regexp.quote prefix}_/
85
+ constants.select do |c|
86
+ next false unless c =~ re
87
+ const_get(c) & mask != 0
88
+ end.map {|c| c.to_s.sub("#{prefix}_", "").downcase.to_sym}
89
+ end
90
+
91
+ # Converts a flag to the integer that the C API expects.
92
+ #
93
+ # @param prefix [String] The prefix for the C names of the flags
94
+ # @param flag [Symbol]
95
+ # @return [Fixnum]
96
+ def self.to_flag(prefix, flag)
97
+ const_get("#{prefix}_#{flag.to_s.upcase}")
98
+ end
99
+
100
+ # Converts an integer from the C API into a flag.
101
+ #
102
+ # @param prefix [String] The prefix for the C names of the flags
103
+ # @param flag [Fixnum]
104
+ # @return [Symbol]
105
+ def self.from_flag(prefix, flag)
106
+ re = /^#{Regexp.quote prefix}_/
107
+ constants.each do |c|
108
+ next unless c =~ re
109
+ return c.to_s.sub("#{prefix}_", "").downcase.to_sym if const_get(c) == flag
110
+ end
111
+ end
112
+ end
113
+ end
114
+ end
@@ -0,0 +1,365 @@
1
+ module KQueue
2
+ # Queue wraps a single instance of kqueue.
3
+ # It's possible to have more than one instance,
4
+ # but usually unnecessary.
5
+ #
6
+ # New event watchers are added to a queue
7
+ # via various `watch_*` methods.
8
+ # For example, \{#watch\_stream\_for\_read} watches for a stream
9
+ # to become readable, and \{#watch\_file} watches for a file to change.
10
+ #
11
+ # Once watchers are added, \{#run} or \{#process} can be used to fire events.
12
+ # Note that if any event-causing conditions happen
13
+ # between adding a watcher and running one of these methods,
14
+ # these events are also fired once the methods are called.
15
+ #
16
+ # @example
17
+ # # Create the queue
18
+ # queue = KQueue::Queue.new
19
+ #
20
+ # # Run this callback whenever the file path/to/foo.txt is read
21
+ # queue.watch_file("path/to/foo.txt", :write) do
22
+ # puts "Foo.txt was modified!"
23
+ # end
24
+ #
25
+ # # Run this callback whenever this process forks or execs
26
+ # queue.watch_process(Process.pid, :fork, :exec) do |event|
27
+ # # The #flags field of the event object contains the actions that happened
28
+ # puts "This process has #{event.flags.map {|f| f.to_s + "ed"}.join(" and ")}"
29
+ # end
30
+ #
31
+ # # Nothing happens until you run the queue!
32
+ # queue.run
33
+ class Queue
34
+ # The file descriptor of the kqueue.
35
+ #
36
+ # @private
37
+ # @return [Fixnum]
38
+ attr_reader :fd
39
+
40
+ # A hash from filter names and idents to {Watcher}s.
41
+ # The kqueue API guarantees that a (filter, ident) pair
42
+ # uniquely identifies a watcher.
43
+ #
44
+ # This hash allows events to retrieve their watchers.
45
+ #
46
+ # @private
47
+ # @return [{(Symbol, Fixnum) => Watcher}]
48
+ attr_reader :watchers
49
+
50
+ # Creates a new, empty queue.
51
+ def initialize
52
+ @fd = Native.kqueue
53
+ @watchers = {}
54
+ end
55
+
56
+ # Watches a stream and produces an event when there's data available to read.
57
+ #
58
+ # This can watch files, pipes, fifos, and BPF devices.
59
+ # For files, an event is fired whenever the file pointer
60
+ # is not at the end of the file,
61
+ # and the {Event#data} field is set to the offset
62
+ # from the current position to the end of the file.
63
+ # {Event#data} may be negative.
64
+ #
65
+ # For pipes and fifos, an event is fired whenever there's data to read.
66
+ # The {Event#data} field is set to the number of bytes available.
67
+ # When the last writer disconnects, {Event#eof?} will be set.
68
+ #
69
+ # For BPF devices (not supported under Darwin/OS X),
70
+ # an event is fired when the BPF buffer is full,
71
+ # the BPF timeout has expired,
72
+ # or when the BPF has "immediate mode" enabled
73
+ # and there is data to read.
74
+ # The {Event#data} field is set to the number of bytes available.
75
+ #
76
+ # Note that this isn't compatible with JRuby
77
+ # unless a native-code file descriptor is passed in.
78
+ # This means the file descriptor must be returned by an FFI-wrapped C function.
79
+ #
80
+ # @param fd [IO, Fixnum] A Ruby IO stream, or the file descriptor
81
+ # for a native IO stream.
82
+ # @yield [event] A block that will be run when the specified stream
83
+ # has data to read.
84
+ # @yieldparam event [Event] The Event object containing information
85
+ # about the event that occurred.
86
+ # @return [Watcher] The Watcher for this event.
87
+ # @raise [SystemCallError] If something goes wrong when registering the Watcher.
88
+ def watch_stream_for_read(fd, &callback)
89
+ Watcher::ReadWrite.new(self, fd, :read, callback)
90
+ end
91
+
92
+ # Watches a socket and produces an event when there's data available to read.
93
+ #
94
+ # Sockets which have previously had `Socket#listen` called fire events
95
+ # when there is an incoming connection pending.
96
+ # In this case, {Event#data} contains the size of the listen backlog.
97
+ #
98
+ # Other sockets return when there is data to be read,
99
+ # subject to the SO_RCVLOWAT value of the socket buffer.
100
+ # This may be overridden via the `low_water` parameter,
101
+ # which sets a new low-water mark.
102
+ # In this case, {Event#data} contains the number of bytes
103
+ # of protocol data available to read.
104
+ #
105
+ # If the read direction of the socket has shut down,
106
+ # then {Event#eof?} is set.
107
+ # It's possible for {Event#eof?} to be set while there's still
108
+ # data pending in the socket buffer.
109
+ #
110
+ # Note that this isn't compatible with JRuby
111
+ # unless a native-code file descriptor is passed in.
112
+ # This means the file descriptor must be returned by an FFI-wrapped C function.
113
+ #
114
+ # @param fd [Socket, Fixnum] A Ruby Socket, or the file descriptor
115
+ # for a native Socket.
116
+ # @param low_water [Fixnum] The low-water mark for new data.
117
+ # @yield [event] A block that will be run when the specified socket
118
+ # has data to read.
119
+ # @yieldparam event [Event] The Event object containing information
120
+ # about the event that occurred.
121
+ # @return [Watcher] The Watcher for this event.
122
+ # @raise [SystemCallError] If something goes wrong when registering the Watcher.
123
+ def watch_socket_for_read(fd, low_water = nil, &callback)
124
+ Watcher::SocketReadWrite.new(self, fd, :read, low_water, callback)
125
+ end
126
+
127
+ # Watches a stream and produces an event
128
+ # when it's possible to write to the stream.
129
+ #
130
+ # This can watch pipes and fifos.
131
+ # The {Event#data} field is set to the amount of space
132
+ # remaining in the write buffer.
133
+ # When the reader disconnects, {Event#eof?} will be set.
134
+ #
135
+ # Note that this isn't compatible with JRuby
136
+ # unless a native-code file descriptor is passed in.
137
+ # This means the file descriptor must be returned by an FFI-wrapped C function.
138
+ #
139
+ # @param fd [IO, Fixnum] A Ruby IO stream, or the file descriptor
140
+ # for a native IO stream.
141
+ # @yield [event] A block that will be run when the specified stream
142
+ # has data to read.
143
+ # @yieldparam event [Event] The Event object containing information
144
+ # about the event that occurred.
145
+ # @return [Watcher] The Watcher for this event.
146
+ # @raise [SystemCallError] If something goes wrong when registering the Watcher.
147
+ def watch_stream_for_write(fd, &callback)
148
+ Watcher::ReadWrite.new(self, fd, :write, callback)
149
+ end
150
+
151
+ # Watches a socket and produces an event when it's possible to write.
152
+ # The {Event#data} field is set to the amount of space
153
+ # remaining in the write buffer.
154
+ #
155
+ # When an event is fired is subject to the
156
+ # subject to the SO_RCVLOWAT value of the socket buffer.
157
+ # This may be overridden via the `low_water` parameter,
158
+ # which sets a new low-water mark.
159
+ #
160
+ # If the write direction of the socket has shut down,
161
+ # then {Event#eof?} is set.
162
+ # It's possible for {Event#eof?} to be set while there's still
163
+ # data pending in the socket buffer.
164
+ #
165
+ # Note that this isn't compatible with JRuby
166
+ # unless a native-code file descriptor is passed in.
167
+ # This means the file descriptor must be returned by an FFI-wrapped C function.
168
+ #
169
+ # @param fd [Socket, Fixnum] A Ruby Socket, or the file descriptor
170
+ # for a native Socket.
171
+ # @param low_water [Fixnum] The low-water mark for new data.
172
+ # @yield [event] A block that will be run when it's possible
173
+ # to write to the specified socket.
174
+ # @yieldparam event [Event] The Event object containing information
175
+ # about the event that occurred.
176
+ # @return [Watcher] The Watcher for this event.
177
+ # @raise [SystemCallError] If something goes wrong when registering the Watcher.
178
+ def watch_socket_for_write(fd, low_water = nil, &callback)
179
+ Watcher::SocketReadWrite.new(self, fd, :write, low_water, callback)
180
+ end
181
+
182
+ # Watches a file or directory for changes.
183
+ # The `flags` parameter specifies which changes
184
+ # will fire events.
185
+ #
186
+ # The {Event#flags} field contains the changes that caused the event to be fired.
187
+ # {Event#data} and {Event#eof?} are unused.
188
+ #
189
+ # Note that this only watches a single file.
190
+ # If the file is a direcotry,
191
+ # it will only report changes to the directory itself,
192
+ # not to any files within the directory.
193
+ #
194
+ # ## Flags
195
+ #
196
+ # `:delete`
197
+ # : The file was deleted.
198
+ #
199
+ # `:write`
200
+ # : The file was modified.
201
+ #
202
+ # `:extend`
203
+ # : The size of the file increased.
204
+ #
205
+ # `:attrib`
206
+ # : Attributes of the file, such as timestamp or permissions, changed.
207
+ #
208
+ # `:link`
209
+ # : The link count of the file changed.
210
+ #
211
+ # `:rename`
212
+ # : The file was renamed.
213
+ #
214
+ # `:revoke`
215
+ # : Access to the file was revoked,
216
+ # either via the `revoke(2)` system call
217
+ # or because the underlying filesystem was unmounted.
218
+ #
219
+ # @param path [String] The path to the file or directory.
220
+ # @param flags [Array<Symbol>] Which events to watch for.
221
+ # @yield [event] A block that will be run when the file changes.
222
+ # @yieldparam event [Event] The Event object containing information
223
+ # about the event that occurred.
224
+ # @return [Watcher] The Watcher for this event.
225
+ # @raise [SystemCallError] If something goes wrong when registering the Watcher.
226
+ def watch_file(path, *flags, &callback)
227
+ Watcher::File.new(self, path, flags, callback)
228
+ end
229
+
230
+ # Watches a process for changes.
231
+ # The `flags` parameter specifies which changes
232
+ # will fire events.
233
+ #
234
+ # The {Event#flags} field contains the changes that caused the event to be fired.
235
+ # {Event#data} and {Event#eof?} are unused.
236
+ #
237
+ # ## Flags
238
+ #
239
+ # `:exit`
240
+ # : The process has exited.
241
+ #
242
+ # `:fork`
243
+ # : The process has created a child process via `fork(2)` or similar.
244
+ #
245
+ # `:exec`
246
+ # : The process has executed a new process via `exec(2)` or similar.
247
+ #
248
+ # `:signal`
249
+ # : The process was sent a signal.
250
+ # This is only supported under Darwin/OS X.
251
+ #
252
+ # `:reap`
253
+ # : The process was reaped by the parent via `wait(2)` or similar.
254
+ # This is only supported under Darwin/OS X.
255
+ #
256
+ # `:track`
257
+ # : Follow the process across `fork(2)` calls.
258
+ # {Event#flags} for the parent process will contain `:fork`,
259
+ # while {Event#flags} for the child process will contain `:child`.
260
+ # If the system was unable to attach an event to the child process,
261
+ # {Event#flags} will contain `:trackerr`.
262
+ # This is not supported under Darwin/OS X.
263
+ #
264
+ # @param pid [Fixnum] The id of the process.
265
+ # @param flags [Array<Symbol>] Which events to watch for.
266
+ # @yield [event] A block that will be run when the process changes.
267
+ # @yieldparam event [Event] The Event object containing information
268
+ # about the event that occurred.
269
+ # @return [Watcher] The Watcher for this event.
270
+ # @raise [SystemCallError] If something goes wrong when registering the Watcher.
271
+ def watch_process(pid, *flags, &callback)
272
+ Watcher::Process.new(self, path, flags, callback)
273
+ end
274
+
275
+ # Watches for signals to this process.
276
+ # This coexists with other signal facilities, and has lower precedence.
277
+ # Only signals sent to the process, not to a particular thread, will fire events.
278
+ # Event notification happens before normal signal delivery processing.
279
+ #
280
+ # The {Event#data} field contains the number of times the signal has been generated
281
+ # since the last time the event was fired.
282
+ #
283
+ # @param signal [String, Fixnum] The name of number of the signal.
284
+ # @yield [event] A block that will be run when the signal is received.
285
+ # @yieldparam event [Event] The Event object containing information
286
+ # about the event that occurred.
287
+ # @return [Watcher] The Watcher for this event.
288
+ # @raise [SystemCallError] If something goes wrong when registering the Watcher.
289
+ def watch_for_signal(signal, &callback)
290
+ Watcher::Signal.new(self, signal, callback)
291
+ end
292
+
293
+ # Sets up a watcher that fires an event
294
+ # once every specified interval.
295
+ #
296
+ # The {Event#data} field contains the number of times the interval has passed
297
+ # since the last time the event was fired.
298
+ #
299
+ # @param time [Number] The interval, in seconds.
300
+ # @yield [event] A block that will be run when the interval passes.
301
+ # @yieldparam event [Event] The Event object containing information
302
+ # about the event that occurred.
303
+ # @return [Watcher] The Watcher for this event.
304
+ # @raise [SystemCallError] If something goes wrong when registering the Watcher.
305
+ def watch_timer(time, &callback)
306
+ Watcher::Timer.new(self, time, callback)
307
+ end
308
+
309
+ # Starts the queue watching for events.
310
+ # Blocks until \{#stop} is called.
311
+ #
312
+ # @see #process
313
+ # @return [void]
314
+ def run
315
+ @stop = false
316
+ process until @stop
317
+ end
318
+
319
+ # Stop watching for events.
320
+ # That is, if we're in a \{#run} loop,
321
+ # exit out as soon as we finish handling
322
+ # the current batch of events.
323
+ #
324
+ # @return [void]
325
+ def stop
326
+ @stop = true
327
+ end
328
+
329
+ # Blocks until there are one or more events
330
+ # that this queue has watchers registered for.
331
+ # Once there are events, the appropriate callbacks are called
332
+ # and this function returns.
333
+ #
334
+ # @see #run
335
+ # @return [void]
336
+ def process
337
+ read_events.each {|event| event.callback!}
338
+ end
339
+
340
+ def poll
341
+ read_events(false).each {|event| event.callback!}
342
+ end
343
+
344
+ NULL_TIMEOUT = Native::TimeSpec.new.tap { |ts|
345
+ ts[:tv_sec] = 0
346
+ ts[:tv_nsec] = 0
347
+ }
348
+
349
+ # Blocks until there are one or more filesystem events
350
+ # that this notifier has watchers registered for.
351
+ # Once there are events, returns their {Event} objects.
352
+ #
353
+ # @private
354
+ def read_events(blocking = true)
355
+ size = 1024
356
+ eventlist = FFI::MemoryPointer.new(Native::KEvent, size)
357
+
358
+ timeout = blocking ? nil : NULL_TIMEOUT
359
+ res = Native.kevent(@fd, nil, 0, eventlist, size, timeout)
360
+
361
+ KQueue.handle_error if res < 0
362
+ (0...res).map {|i| KQueue::Event.new(Native::KEvent.new(eventlist[i]), self)}
363
+ end
364
+ end
365
+ end
@@ -0,0 +1,124 @@
1
+ module KQueue
2
+ # Watchers monitor for a single sort of event,
3
+ # specified by the specific subclass and its parameters.
4
+ # A watcher is usually created via one of the `watch_*` methods
5
+ # on {Queue}.
6
+ #
7
+ # One {Queue} may have many {Watcher}s.
8
+ # The Queue actually takes care of the checking for events,
9
+ # via \{Queue#run #run} or \{Queue#process #process}.
10
+ #
11
+ # Watcher objects themselves have several useful capabilities.
12
+ # Each subclass keeps track of its own specific information.
13
+ # In addition, all Watchers can be \{#delete! deleted from the queue},
14
+ # \{#add! added back in}, \{#disable! disabled}, and \{#enable! enabled}.
15
+ class Watcher
16
+ # The {Queue} that created this Watcher.
17
+ #
18
+ # @return [Queue]
19
+ attr_reader :queue
20
+
21
+ # Creates a new Watcher.
22
+ #
23
+ # @private
24
+ # @param queue [Queue] The queue for which this watcher will be used.
25
+ # @param ident [Fixnum] The underlying kqueue identifier for this watcher.
26
+ # @param filter [Symbol] The name of the underlying kqueue filter for this watcher.
27
+ # @param fflags [Array<Symbol>] Filter-specific flags.
28
+ # @param data [Fixnum] Filter-specific data.
29
+ # @param callback [Proc{Event -> void}] The callback that will be called
30
+ # on any events fired by this watcher.
31
+ # @raise [SystemCallError] If something goes wrong when registering this Watcher.
32
+ def initialize(queue, ident, filter, fflags, data, callback)
33
+ @queue = queue
34
+ @ident = ident
35
+ @filter = filter
36
+ @flags = []
37
+ @fflags = fflags
38
+ @data = data
39
+ @callback = callback
40
+ add!
41
+ end
42
+
43
+ # Adds this Watcher to \{#queue its Queue}.
44
+ # Note that this is done automatically when the Watcher is created.
45
+ #
46
+ # @raise [SystemCallError] If something goes wrong when adding this Watcher.
47
+ # @return [void]
48
+ def add!
49
+ kevent! :add, :clear # TODO: Don't always enable :clear
50
+ @queue.watchers[[@filter, @ident]] = self
51
+ end
52
+
53
+ # Removes this Watcher from \{#queue its Queue}.
54
+ # This means that events won't be fired
55
+ # or even checked for.
56
+ #
57
+ # @raise [SystemCallError] If something goes wrong when deleting this Watcher.
58
+ # @return [void]
59
+ def delete!
60
+ kevent! :delete
61
+ @queue.watchers.delete([@filter, @ident])
62
+ end
63
+
64
+ # Enables this Watcher.
65
+ # Note that this is done automatically when the Watcher is created,
66
+ # as well as whenever \{#add!} is called.
67
+ #
68
+ # @raise [SystemCallError] If something goes wrong when enabling this Watcher.
69
+ # @return [void]
70
+ def enable!
71
+ kevent! :enable
72
+ end
73
+
74
+ # Disables this Watcher.
75
+ # This means that events won't be fired,
76
+ # but they'll still be checked for.
77
+ #
78
+ # @raise [SystemCallError] If something goes wrong when enabling this Watcher.
79
+ # @return [void]
80
+ def disable!
81
+ kevent! :disable
82
+ end
83
+
84
+ # Calls this Watcher's callback with the given {Event}.
85
+ #
86
+ # @private
87
+ # @param event [Event]
88
+ # @return [void]
89
+ def callback!(event)
90
+ @callback.call event
91
+ end
92
+
93
+ private
94
+
95
+ # Returns a C struct corresponding to this watcher.
96
+ #
97
+ # @param flags [Array<Symbol>] Flags for the C struct's `flags` field,
98
+ # in addition to the `@flags` var.
99
+ # @return [Native::KEvent]
100
+ def native(flags)
101
+ native = Native::KEvent.new
102
+ native[:ident] = @ident
103
+ native[:filter] = Native::Flags.to_flag("EVFILT", @filter)
104
+ native[:flags] = Native::Flags.to_mask("EV", @flags | flags)
105
+ native[:fflags] = Native::Flags.to_mask("NOTE_#{@filter.to_s.upcase}", @fflags)
106
+ native[:data] = @data if @data
107
+ native
108
+ end
109
+
110
+ # Runs the `kevent` C call with this Watcher's kevent struct as input.
111
+ # This effectively means telling kqueue to perform some action
112
+ # with this Watcher as an argument.
113
+ #
114
+ # @param flags [Array<Symbol>] Flags specifying the action to perform
115
+ # as well as any additional flags.
116
+ # @return [void]
117
+ # @raise [SystemCallError] If something goes wrong when performing the C call.
118
+ def kevent!(*flags)
119
+ if Native.kevent(@queue.fd, native(flags).pointer, 1, nil, 0, nil) < 0
120
+ KQueue.handle_error
121
+ end
122
+ end
123
+ end
124
+ end
@@ -0,0 +1,56 @@
1
+ module KQueue
2
+ class Watcher
3
+ # The {Watcher} subclass for events fired when a file changes.
4
+ # File events are watched via {Queue#watch_file}.
5
+ class File < Watcher
6
+ # The path to the file being watched.
7
+ #
8
+ # @return [String]
9
+ attr_reader :path
10
+
11
+ # Creates a new file Watcher.
12
+ #
13
+ # @private
14
+ def initialize(queue, path, flags, callback)
15
+ @path = path
16
+ @fd = Native.open(path, 0) # 0 means "read only"
17
+
18
+ if @fd < 0
19
+ raise SystemCallError.new(
20
+ "Failed to open file #{path}" +
21
+ case FFI.errno
22
+ when Errno::EACCES::Errno; ": Permission denied."
23
+ when Errno::EAGAIN::Errno; ": Slave side of a locked pseudo-terminal device."
24
+ when Errno::EFAULT::Errno; ": Outside the process's allocated address space."
25
+ when Errno::EINTR::Errno; ": Interrupted."
26
+ when Errno::ELOOP::Errno; ": Too many symbolic links (possible loop)."
27
+ when Errno::EMFILE::Errno; ": Too many open files."
28
+ when Errno::ENAMETOOLONG::Errno; ": Name too long."
29
+ when Errno::ENFILE::Errno; ": System file table is full."
30
+ when Errno::ENOENT::Errno; ": File doesn't exist."
31
+ when Errno::ENOTDIR::Errno; ": A component of the path prefix is not a directory."
32
+ when Errno::ENXIO::Errno; ": The device associated with this file doesn't exist."
33
+ when Errno::EOPNOTSUPP::Errno; ": File type not supported."
34
+ when Errno::EOVERFLOW::Errno; ": File too big."
35
+ else; ""
36
+ end,
37
+ FFI.errno)
38
+ end
39
+
40
+ ObjectSpace.define_finalizer(self, lambda do
41
+ next unless Native.close(@fd) < 0
42
+ raise SystemCallError.new(
43
+ "Failed to close file #{path}" +
44
+ case FFI.errno
45
+ when Errno::EBADF::Errno; ": Invalid file descriptor."
46
+ when Errno::EINTR::Errno; ": Closing interrupted."
47
+ when Errno::EIO::Errno; ": IO error."
48
+ else; ""
49
+ end,
50
+ FFI.errno)
51
+ end)
52
+ super(queue, @fd, :vnode, flags, nil, callback)
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,20 @@
1
+ module KQueue
2
+ class Watcher
3
+ # The {Watcher} subclass for process events.
4
+ # Process events are watched via {Queue#watch_process}.
5
+ class Process < Watcher
6
+ # The process id of the process being watched.
7
+ #
8
+ # @return [Fixnum]
9
+ attr_reader :pid
10
+
11
+ # Creates a new process Watcher.
12
+ #
13
+ # @private
14
+ def initialize(queue, pid, flags, callback)
15
+ @pid = pid
16
+ super(queue, pid, :proc, flags, nil, callback)
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,44 @@
1
+ module KQueue
2
+ class Watcher
3
+ # The {Watcher} subclass for events
4
+ # fired when a stream can be read from or written to
5
+ # (which of these is determined by \{#type}).
6
+ # Read events are watched via {Queue#watch_stream_for_read},
7
+ # and write events are watched via {Queue#watch_stream_for_write}.
8
+ #
9
+ # Note that read and write events for sockets
10
+ # use the {SocketReadWrite} class.
11
+ class ReadWrite < Watcher
12
+ # The Ruby IO object from which the file descriptor was extracted.
13
+ # This is only set if an IO object was used to construct this watcher.
14
+ # Otherwise, it's `nil`.
15
+ #
16
+ # @return [IO, nil]
17
+ attr_reader :io
18
+
19
+ # The file descriptor for the stream being watched.
20
+ #
21
+ # @return [Fixnum]
22
+ attr_reader :fd
23
+
24
+ # The type of watcher, `:read` or `:write`.
25
+ #
26
+ # @return [Symbol]
27
+ attr_reader :type
28
+
29
+ # Creates a new read/write Watcher.
30
+ #
31
+ # @private
32
+ def initialize(queue, fd, type, callback)
33
+ if fd.is_a?(IO)
34
+ @io = fd
35
+ fd = fd.fileno
36
+ end
37
+
38
+ @fd = fd
39
+ @type = type
40
+ super(queue, @fd, type, [], nil, callback)
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,32 @@
1
+ module KQueue
2
+ class Watcher
3
+ # The {Watcher} subclass for events fired when a signal is received.
4
+ # Signal events are watched via {Queue#watch_for_signal}.
5
+ class Signal < Watcher
6
+ # The name of the signal, e.g. "KILL" for SIGKILL.
7
+ #
8
+ # @return [String]
9
+ attr_reader :name
10
+
11
+ # The number of the signal, e.g. 9 for SIGKILL.
12
+ #
13
+ # @return [Fixnum]
14
+ attr_reader :number
15
+
16
+ # Creates a new signal Watcher.
17
+ #
18
+ # @private
19
+ def initialize(queue, signal, callback)
20
+ if signal.is_a?(String)
21
+ @name = signal
22
+ @number = Signal.list[signal]
23
+ else
24
+ @name = Signal.list.find {|_, n| n == signal}.first
25
+ @number = signal
26
+ end
27
+
28
+ super(queue, @number, :signal, [], nil, callback)
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,43 @@
1
+ module KQueue
2
+ class Watcher
3
+ # The {Watcher} subclass for events
4
+ # fired when a socket can be read from or written to
5
+ # (which of these is determined by \{ReadWrite#type}).
6
+ # Read events are watched via {Queue#watch_socket_for_read},
7
+ # and write events are watched via {Queue#watch_socket_for_write}.
8
+ #
9
+ # Note that read and write events for non-socket streams
10
+ # use the {ReadWrite} class.
11
+ class SocketReadWrite < ReadWrite
12
+ # The custom low-water mark for the amount of data necessary
13
+ # to trigger an event,
14
+ # or `nil` if none has been set.
15
+ #
16
+ # @return [Fixnum, nil]
17
+ attr_reader :low_water
18
+
19
+ # Creates a new socket Watcher.
20
+ #
21
+ # @private
22
+ def initialize(queue, fd, type, low_water, callback)
23
+ if fd.is_a?(IO)
24
+ @io = fd
25
+ fd = fd.fileno
26
+ end
27
+
28
+ @fd = fd
29
+ @type = type
30
+
31
+ if low_water
32
+ fflags = [:lowat]
33
+ data = low_water
34
+ else
35
+ fflags = []
36
+ data = nil
37
+ end
38
+
39
+ super(queue, @fd, type, fflags, data, callback)
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,30 @@
1
+ module KQueue
2
+ class Watcher
3
+ # The {Watcher} subclass for events fired based on a timer.
4
+ # Timer events are watched via {Queue#watch_timer}.
5
+ class Timer < Watcher
6
+ # The interval on which the timer fires an event, in seconds.
7
+ #
8
+ # @return [Numeric]
9
+ attr_reader :time
10
+
11
+ # Creates a new timer Watcher.
12
+ #
13
+ # @private
14
+ def initialize(time, callback)
15
+ time, unit =
16
+ if time < 10**-3
17
+ [(time * 10**9).round, :nseconds]
18
+ elsif time < 1
19
+ [(time * 10**6).round, :useconds]
20
+ elsif time < 10**3 && !time.is_a?(Fixnum)
21
+ [(time * 10**3).round, nil] # milliseconds
22
+ else
23
+ [time.round, :seconds]
24
+ end
25
+
26
+ super(queue, time, :timer, Array(unit), nil, callback)
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,61 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{rb-kqueue-burke}
8
+ s.version = "0.1.0"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Nathan Weizenbaum"]
12
+ s.date = %q{2010-02-08}
13
+ s.description = %q{A Ruby wrapper for BSD's kqueue, using FFI}
14
+ s.email = %q{nex342@gmail.com}
15
+ s.extra_rdoc_files = [
16
+ "README.md"
17
+ ]
18
+ s.files = [
19
+ ".gitignore",
20
+ ".yardopts",
21
+ "MIT-LICENSE",
22
+ "README.md",
23
+ "Rakefile",
24
+ "VERSION",
25
+ "lib/rb-kqueue.rb",
26
+ "lib/rb-kqueue/event.rb",
27
+ "lib/rb-kqueue/native.rb",
28
+ "lib/rb-kqueue/native/flags.rb",
29
+ "lib/rb-kqueue/queue.rb",
30
+ "lib/rb-kqueue/watcher.rb",
31
+ "lib/rb-kqueue/watcher/file.rb",
32
+ "lib/rb-kqueue/watcher/process.rb",
33
+ "lib/rb-kqueue/watcher/read_write.rb",
34
+ "lib/rb-kqueue/watcher/signal.rb",
35
+ "lib/rb-kqueue/watcher/socket_read_write.rb",
36
+ "lib/rb-kqueue/watcher/timer.rb",
37
+ "rb-kqueue-burke.gemspec"
38
+ ]
39
+ s.homepage = %q{http://github.com/burke/rb-kqueue}
40
+ s.rdoc_options = ["--charset=UTF-8"]
41
+ s.require_paths = ["lib"]
42
+ s.rubygems_version = %q{1.3.5}
43
+ s.summary = %q{A Ruby wrapper for BSD's kqueue, using FFI}
44
+
45
+ if s.respond_to? :specification_version then
46
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
47
+ s.specification_version = 3
48
+
49
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
50
+ s.add_runtime_dependency(%q<ffi>, [">= 0.5.0"])
51
+ s.add_development_dependency(%q<yard>, [">= 0.4.0"])
52
+ else
53
+ s.add_dependency(%q<ffi>, [">= 0.5.0"])
54
+ s.add_dependency(%q<yard>, [">= 0.4.0"])
55
+ end
56
+ else
57
+ s.add_dependency(%q<ffi>, [">= 0.5.0"])
58
+ s.add_dependency(%q<yard>, [">= 0.4.0"])
59
+ end
60
+ end
61
+
metadata ADDED
@@ -0,0 +1,87 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rb-kqueue-burke
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Nathan Weizenbaum
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2010-02-08 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: ffi
16
+ requirement: &70244761446700 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: 0.5.0
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: *70244761446700
25
+ - !ruby/object:Gem::Dependency
26
+ name: yard
27
+ requirement: &70244761445980 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ! '>='
31
+ - !ruby/object:Gem::Version
32
+ version: 0.4.0
33
+ type: :development
34
+ prerelease: false
35
+ version_requirements: *70244761445980
36
+ description: A Ruby wrapper for BSD's kqueue, using FFI
37
+ email: nex342@gmail.com
38
+ executables: []
39
+ extensions: []
40
+ extra_rdoc_files:
41
+ - README.md
42
+ files:
43
+ - .gitignore
44
+ - .yardopts
45
+ - MIT-LICENSE
46
+ - README.md
47
+ - Rakefile
48
+ - VERSION
49
+ - lib/rb-kqueue.rb
50
+ - lib/rb-kqueue/event.rb
51
+ - lib/rb-kqueue/native.rb
52
+ - lib/rb-kqueue/native/flags.rb
53
+ - lib/rb-kqueue/queue.rb
54
+ - lib/rb-kqueue/watcher.rb
55
+ - lib/rb-kqueue/watcher/file.rb
56
+ - lib/rb-kqueue/watcher/process.rb
57
+ - lib/rb-kqueue/watcher/read_write.rb
58
+ - lib/rb-kqueue/watcher/signal.rb
59
+ - lib/rb-kqueue/watcher/socket_read_write.rb
60
+ - lib/rb-kqueue/watcher/timer.rb
61
+ - rb-kqueue-burke.gemspec
62
+ homepage: http://github.com/burke/rb-kqueue
63
+ licenses: []
64
+ post_install_message:
65
+ rdoc_options:
66
+ - --charset=UTF-8
67
+ require_paths:
68
+ - lib
69
+ required_ruby_version: !ruby/object:Gem::Requirement
70
+ none: false
71
+ requirements:
72
+ - - ! '>='
73
+ - !ruby/object:Gem::Version
74
+ version: '0'
75
+ required_rubygems_version: !ruby/object:Gem::Requirement
76
+ none: false
77
+ requirements:
78
+ - - ! '>='
79
+ - !ruby/object:Gem::Version
80
+ version: '0'
81
+ requirements: []
82
+ rubyforge_project:
83
+ rubygems_version: 1.8.11
84
+ signing_key:
85
+ specification_version: 3
86
+ summary: A Ruby wrapper for BSD's kqueue, using FFI
87
+ test_files: []