rb-kqueue 0.0.1 → 0.0.2

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/README.md CHANGED
@@ -3,3 +3,34 @@
3
3
  This is a simple wrapper over the [kqueue](http://en.wikipedia.org/wiki/Kqueue)
4
4
  BSD event notification interface (supported on FreeBSD, NetBSD, OpenBSD, and Darwin).
5
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
+ ## Usage
10
+
11
+ The API is similar to the kqueue C API, but with a more Rubyish feel.
12
+ First, create a queue:
13
+
14
+ queue = KQueue::Queue.new
15
+
16
+ Then, tell it to watch the events you're interested in:
17
+
18
+ queue.watch_file("path/to/foo.txt", :write) {puts "foo.txt was modified!"}
19
+ queue.watch_process(Process.pid, :fork, :exec) do |event|
20
+ puts "This process has #{event.flags.map {|f| f.to_s + "ed"}.join(" and ")}"
21
+ end
22
+
23
+ KQueue can monitor for all sorts of events.
24
+ For a full list, see the `watch_*` methods on {Queue}.
25
+
26
+ Finally, run the queue:
27
+
28
+ queue.run
29
+
30
+ This will loop infinitely, calling the appropriate callbacks when the events are fired.
31
+ If you don't want infinite looping,
32
+ you can also block until there are available events,
33
+ process them all at once,
34
+ and then continue on your merry way:
35
+
36
+ queue.process
data/Rakefile CHANGED
@@ -19,26 +19,26 @@ rescue LoadError
19
19
  end
20
20
 
21
21
  module Jeweler::VersionHelper::PlaintextExtension
22
- def write_with_inotify
23
- write_without_inotify
22
+ def write_with_kqueue
23
+ write_without_kqueue
24
24
  filename = File.join(File.dirname(__FILE__), "lib/rb-kqueue.rb")
25
25
  text = File.read(filename)
26
26
  File.open(filename, 'w') do |f|
27
27
  f.write text.gsub(/^( VERSION = ).*/, '\1' + [major, minor, patch].inspect)
28
28
  end
29
29
  end
30
- alias_method :write_without_inotify, :write
31
- alias_method :write, :write_with_inotify
30
+ alias_method :write_without_kqueue, :write
31
+ alias_method :write, :write_with_kqueue
32
32
  end
33
33
 
34
34
  class Jeweler::Commands::Version::Base
35
- def commit_version_with_inotify
35
+ def commit_version_with_kqueue
36
36
  return unless self.repo
37
37
  self.repo.add(File.join(File.dirname(__FILE__), "lib/rb-kqueue.rb"))
38
- commit_version_without_inotify
38
+ commit_version_without_kqueue
39
39
  end
40
- alias_method :commit_version_without_inotify, :commit_version
41
- alias_method :commit_version, :commit_version_with_inotify
40
+ alias_method :commit_version_without_kqueue, :commit_version
41
+ alias_method :commit_version, :commit_version_with_kqueue
42
42
  end
43
43
 
44
44
  begin
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.0.1
1
+ 0.0.2
data/lib/rb-kqueue.rb CHANGED
@@ -1,15 +1,27 @@
1
1
  require 'rb-kqueue/native'
2
2
  require 'rb-kqueue/native/flags'
3
3
  require 'rb-kqueue/watcher'
4
- require 'rb-kqueue/watcher/vnode'
4
+ require 'rb-kqueue/watcher/file'
5
5
  require 'rb-kqueue/watcher/read_write'
6
6
  require 'rb-kqueue/watcher/process'
7
7
  require 'rb-kqueue/event'
8
8
  require 'rb-kqueue/queue'
9
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
10
15
  module KQueue
11
- VERSION = [0, 0, 1]
16
+ VERSION = [0, 0, 2]
12
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
13
25
  def self.handle_error(errno = FFI.errno)
14
26
  raise SystemCallError.new(
15
27
  "KQueue failed" +
@@ -1,20 +1,65 @@
1
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.
2
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]
3
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]
4
21
  attr_reader :filter
5
22
 
23
+ # The {Watcher} that produced this event.
24
+ #
25
+ # @return [Watcher]
6
26
  def watcher
7
27
  @watcher ||= @queue.watchers[[filter, @native[:ident]]]
8
28
  end
9
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>]
10
41
  def flags
11
42
  @fflags ||= Native::Flags.from_mask("NOTE", @native[:fflags])
12
43
  end
13
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]
14
52
  def eof?
15
53
  @flags.include?(:eof)
16
54
  end
17
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
18
63
  def initialize(native, queue)
19
64
  @native = native
20
65
  @queue = queue
@@ -25,6 +70,12 @@ module KQueue
25
70
  KQueue.handle_error @native[:data] if @flags.inclue?(:error)
26
71
  end
27
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]
28
79
  def callback!
29
80
  watcher.callback! self
30
81
  end
@@ -1,9 +1,17 @@
1
1
  require 'ffi'
2
2
 
3
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
4
9
  module Native
5
10
  extend FFI::Library
6
11
 
12
+ # The C struct describing a kqueue event.
13
+ #
14
+ # @private
7
15
  class KEvent < FFI::Struct
8
16
  layout(
9
17
  :ident, :uintptr_t,
@@ -14,6 +22,9 @@ module KQueue
14
22
  :udata, :pointer)
15
23
  end
16
24
 
25
+ # The C struct describing a timeout.
26
+ #
27
+ # @private
17
28
  class TimeSpec < FFI::Struct
18
29
  layout(
19
30
  :tv_sec, :time_t,
@@ -1,5 +1,9 @@
1
1
  module KQueue
2
2
  module Native
3
+ # A module containing all the C-level integer flags
4
+ # that are used with kqueue.
5
+ #
6
+ # @private
3
7
  module Flags
4
8
  # Filters
5
9
  EVFILT_READ = -1
@@ -32,10 +36,10 @@ module KQueue
32
36
  EV_ERROR = 0x4000 # Error, data contains errno
33
37
 
34
38
 
35
- # For EVFILT_{READ,WRITE}
39
+ # For `EVFILT_{READ,WRITE}`
36
40
  NOTE_LOWAT = 0x00000001 # Low water mark
37
41
 
38
- # For EVFILT_VNODE
42
+ # For `EVFILT_VNODE`
39
43
  NOTE_DELETE = 0x00000001 # Vnode was removed
40
44
  NOTE_WRITE = 0x00000002 # Data contents changed
41
45
  NOTE_EXTEND = 0x00000004 # Size increased
@@ -43,7 +47,13 @@ module KQueue
43
47
  NOTE_LINK = 0x00000010 # Link count changed
44
48
  NOTE_RENAME = 0x00000020 # Vnode was renamed
45
49
  NOTE_REVOKE = 0x00000040 # Vnode access was revoked
46
- NOTE_NONE = 0x00000080 # No specific vnode event: to test for EVFILT_READ activation
50
+
51
+ # For `EVFILT_PROC`
52
+ NOTE_EXIT = 0x80000000 # Process exited
53
+ NOTE_FORK = 0x40000000 # Process forked
54
+ NOTE_EXEC = 0x20000000 # Process exec'd
55
+ NOTE_REAP = 0x10000000 # Process reaped
56
+ NOTE_SIGNAL = 0x08000000 # Received signal
47
57
 
48
58
 
49
59
  # Converts a list of flags to the bitmask that the C API expects.
@@ -69,10 +79,20 @@ module KQueue
69
79
  end.map {|c| c.sub("#{prefix}_", "").downcase.to_sym}
70
80
  end
71
81
 
82
+ # Converts a flag to the integer that the C API expects.
83
+ #
84
+ # @param prefix [String] The prefix for the C names of the flags
85
+ # @param flag [Symbol]
86
+ # @return [Fixnum]
72
87
  def self.to_flag(prefix, flag)
73
88
  const_get("#{prefix}_#{flag.to_s.upcase}")
74
89
  end
75
90
 
91
+ # Converts an integer from the C API into a flag.
92
+ #
93
+ # @param prefix [String] The prefix for the C names of the flags
94
+ # @param flag [Fixnum]
95
+ # @return [Symbol]
76
96
  def self.from_flag(prefix, flag)
77
97
  re = /^#{Regexp.quote prefix}_/
78
98
  constants.each do |c|
@@ -1,54 +1,284 @@
1
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
2
33
  class Queue
34
+ # The file descriptor of the kqueue.
35
+ #
36
+ # @private
37
+ # @return [Fixnum]
3
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}]
4
48
  attr_reader :watchers
5
49
 
50
+ # Creates a new, empty queue.
6
51
  def initialize
7
52
  @fd = Native.kqueue
8
53
  @watchers = {}
9
54
  end
10
55
 
11
- def watch_for_read(fd, &callback)
56
+ # Watches a stream and produces an event when there's data available to read.
57
+ #
58
+ # This can watch files, pipes, and fifos.
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
+ # @param fd [IO, Fixnum] A Ruby IO stream, or the file descriptor
70
+ # for a native IO stream.
71
+ # @yield [event] A block that will be run when the specified stream
72
+ # has data to read.
73
+ # @yieldparam event [Event] The Event object containing information
74
+ # about the event that occurred.
75
+ # @return [Watcher] The Watcher for this event.
76
+ # @raise [SystemCallError] If something goes wrong when registering the Watcher.
77
+ def watch_stream_for_read(fd, &callback)
12
78
  fd = fd.fileno if fd.respond_to?(:fileno)
13
79
  Watcher::ReadWrite.new(self, fd, :read, callback)
14
80
  end
15
81
 
16
- def watch_for_socket_read(fd, low_water = nil, &callback)
82
+ # Watches a socket and produces an event when there's data available to read.
83
+ #
84
+ # Sockets which have previously had `Socket#listen` called fire events
85
+ # when there is an incoming connection pending.
86
+ # In this case, {Event#data} contains the size of the listen backlog.
87
+ #
88
+ # Other sockets return when there is data to be read,
89
+ # subject to the SO_RCVLOWAT value of the socket buffer.
90
+ # This may be overridden via the `low_water` parameter,
91
+ # which sets a new low-water mark.
92
+ # In this case, {Event#data} contains the number of bytes
93
+ # of protocol data available to read.
94
+ #
95
+ # If the read direction of the socket has shut down,
96
+ # then {Event#eof?} is set.
97
+ # It's possible for {Event#eof?} to be set while there's still
98
+ # data pending in the socket buffer.
99
+ #
100
+ # @param fd [Socket, Fixnum] A Ruby Socket, or the file descriptor
101
+ # for a native Socket.
102
+ # @param low_water [Fixnum] The low-water mark for new data.
103
+ # @yield [event] A block that will be run when the specified socket
104
+ # has data to read.
105
+ # @yieldparam event [Event] The Event object containing information
106
+ # about the event that occurred.
107
+ # @return [Watcher] The Watcher for this event.
108
+ # @raise [SystemCallError] If something goes wrong when registering the Watcher.
109
+ def watch_socket_for_read(fd, low_water = nil, &callback)
17
110
  fd = fd.fileno if fd.respond_to?(:fileno)
18
111
  Watcher::SocketReadWrite.new(self, fd, :read, low_water, callback)
19
112
  end
20
113
 
21
- def watch_for_write(fd, &callback)
114
+ # Watches a stream and produces an event
115
+ # when it's possible to write to the stream.
116
+ #
117
+ # This can watch pipes and fifos.
118
+ # The {Event#data} field is set to the amount of space
119
+ # remaining in the write buffer.
120
+ # When the reader disconnects, {Event#eof?} will be set.
121
+ #
122
+ # @param fd [IO, Fixnum] A Ruby IO stream, or the file descriptor
123
+ # for a native IO stream.
124
+ # @yield [event] A block that will be run when the specified stream
125
+ # has data to read.
126
+ # @yieldparam event [Event] The Event object containing information
127
+ # about the event that occurred.
128
+ # @return [Watcher] The Watcher for this event.
129
+ # @raise [SystemCallError] If something goes wrong when registering the Watcher.
130
+ def watch_stream_for_write(fd, &callback)
22
131
  fd = fd.fileno if fd.respond_to?(:fileno)
23
132
  Watcher::ReadWrite.new(self, fd, :write, callback)
24
133
  end
25
134
 
26
- def watch_for_socket_write(fd, low_water = nil, &callback)
135
+ # Watches a socket and produces an event when it's possible to write.
136
+ # The {Event#data} field is set to the amount of space
137
+ # remaining in the write buffer.
138
+ #
139
+ # When an event is fired is subject to the
140
+ # subject to the SO_RCVLOWAT value of the socket buffer.
141
+ # This may be overridden via the `low_water` parameter,
142
+ # which sets a new low-water mark.
143
+ #
144
+ # If the write direction of the socket has shut down,
145
+ # then {Event#eof?} is set.
146
+ # It's possible for {Event#eof?} to be set while there's still
147
+ # data pending in the socket buffer.
148
+ #
149
+ # @param fd [Socket, Fixnum] A Ruby Socket, or the file descriptor
150
+ # for a native Socket.
151
+ # @param low_water [Fixnum] The low-water mark for new data.
152
+ # @yield [event] A block that will be run when it's possible
153
+ # to write to the specified socket.
154
+ # @yieldparam event [Event] The Event object containing information
155
+ # about the event that occurred.
156
+ # @return [Watcher] The Watcher for this event.
157
+ # @raise [SystemCallError] If something goes wrong when registering the Watcher.
158
+ def watch_socket_for_write(fd, low_water = nil, &callback)
27
159
  fd = fd.fileno if fd.respond_to?(:fileno)
28
160
  Watcher::SocketReadWrite.new(self, fd, :write, low_water, callback)
29
161
  end
30
162
 
31
- def watch_for_file_change(path, *flags, &callback)
32
- Watcher::VNode.new(self, path, flags, callback)
163
+ # Watches a file or directory for changes.
164
+ # The `flags` parameter specifies which changes
165
+ # will fire events.
166
+ #
167
+ # The {Event#flags} field contains the changes that caused the event to be fired.
168
+ # {Event#data} and {Event#eof?} are unused.
169
+ #
170
+ # Note that this only watches a single file.
171
+ # If the file is a direcotry,
172
+ # it will only report changes to the directory itself,
173
+ # not to any files within the directory.
174
+ #
175
+ # ## Flags
176
+ #
177
+ # `:delete`
178
+ # : The file was deleted.
179
+ #
180
+ # `:write`
181
+ # : The file was modified.
182
+ #
183
+ # `:extend`
184
+ # : The size of the file increased.
185
+ #
186
+ # `:attrib`
187
+ # : Attributes of the file, such as timestamp or permissions, changed.
188
+ #
189
+ # `:link`
190
+ # : The link count of the file changed.
191
+ #
192
+ # `:rename`
193
+ # : The file was renamed.
194
+ #
195
+ # `:revoke`
196
+ # : Access to the file was revoked,
197
+ # either via the `revoke(2)` system call
198
+ # or because the underlying filesystem was unmounted.
199
+ #
200
+ # @param path [String] The path to the file or directory.
201
+ # @param flags [Array<Symbol>] Which events to watch for.
202
+ # @yield [event] A block that will be run when the file changes.
203
+ # @yieldparam event [Event] The Event object containing information
204
+ # about the event that occurred.
205
+ # @return [Watcher] The Watcher for this event.
206
+ # @raise [SystemCallError] If something goes wrong when registering the Watcher.
207
+ def watch_file(path, *flags, &callback)
208
+ Watcher::File.new(self, path, flags, callback)
33
209
  end
34
210
 
35
- def watch_for_process_change(pid, *flags, &callback)
211
+ # Watches a process for changes.
212
+ # The `flags` parameter specifies which changes
213
+ # will fire events.
214
+ #
215
+ # The {Event#flags} field contains the changes that caused the event to be fired.
216
+ # {Event#data} and {Event#eof?} are unused.
217
+ #
218
+ # ## Flags
219
+ #
220
+ # `:exit`
221
+ # : The process has exited.
222
+ #
223
+ # `:fork`
224
+ # : The process has created a child process via `fork(2)` or similar.
225
+ #
226
+ # `:exec`
227
+ # : The process has executed a new process via `exec(2)` or similar.
228
+ #
229
+ # `:signal`
230
+ # : The process was sent a signal.
231
+ #
232
+ # `:reap`
233
+ # : The process was reaped by the parent via `wait(2)` or similar.
234
+ #
235
+ # @param pid [Fixnum] The id of the process.
236
+ # @param flags [Array<Symbol>] Which events to watch for.
237
+ # @yield [event] A block that will be run when the process changes.
238
+ # @yieldparam event [Event] The Event object containing information
239
+ # about the event that occurred.
240
+ # @return [Watcher] The Watcher for this event.
241
+ # @raise [SystemCallError] If something goes wrong when registering the Watcher.
242
+ def watch_process(pid, *flags, &callback)
36
243
  Watcher::Process.new(self, path, flags, callback)
37
244
  end
38
245
 
246
+ # Starts the queue watching for events.
247
+ # Blocks until \{#stop} is called.
248
+ #
249
+ # @see #process
250
+ # @return [void]
39
251
  def run
40
252
  @stop = false
41
253
  process until @stop
42
254
  end
43
255
 
256
+ # Stop watching for events.
257
+ # That is, if we're in a \{#run} loop,
258
+ # exit out as soon as we finish handling
259
+ # the current batch of events.
260
+ #
261
+ # @return [void]
44
262
  def stop
45
263
  @stop = true
46
264
  end
47
265
 
266
+ # Blocks until there are one or more events
267
+ # that this queue has watchers registered for.
268
+ # Once there are events, the appropriate callbacks are called
269
+ # and this function returns.
270
+ #
271
+ # @see #run
272
+ # @return [void]
48
273
  def process
49
274
  read_events.each {|event| event.callback!}
50
275
  end
51
276
 
277
+ # Blocks until there are one or more filesystem events
278
+ # that this notifier has watchers registered for.
279
+ # Once there are events, returns their {Event} objects.
280
+ #
281
+ # @private
52
282
  def read_events
53
283
  size = 1024
54
284
  eventlist = FFI::MemoryPointer.new(Native::KEvent, size)
@@ -1,7 +1,34 @@
1
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}.
2
15
  class Watcher
16
+ # The {Queue} that created this Watcher.
17
+ #
18
+ # @return [Queue]
3
19
  attr_reader :queue
4
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.
5
32
  def initialize(queue, ident, filter, fflags, data, callback)
6
33
  @queue = queue
7
34
  @ident = ident
@@ -13,30 +40,63 @@ module KQueue
13
40
  add!
14
41
  end
15
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]
16
48
  def add!
17
- kqueue! :add, :clear # TODO: Don't always enable :clear
49
+ kevent! :add, :clear # TODO: Don't always enable :clear
18
50
  @queue.watchers[[@filter, @ident]] = self
19
51
  end
20
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]
21
59
  def delete!
22
- kqueue! :delete
60
+ kevent! :delete
23
61
  @queue.watchers.delete([@filter, @ident])
24
62
  end
25
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]
26
70
  def enable!
27
- kqueue! :enable
71
+ kevent! :enable
28
72
  end
29
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]
30
80
  def disable!
31
- kqueue! :disable
81
+ kevent! :disable
32
82
  end
33
83
 
84
+ # Calls this Watcher's callback with the given {Event}.
85
+ #
86
+ # @private
87
+ # @param event [Event]
88
+ # @return [void]
34
89
  def callback!(event)
35
90
  @callback.call event
36
91
  end
37
92
 
38
93
  private
39
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]
40
100
  def native(flags)
41
101
  native = Native::KEvent.new
42
102
  native[:ident] = @ident
@@ -47,7 +107,15 @@ module KQueue
47
107
  native
48
108
  end
49
109
 
50
- def kqueue!(*flags)
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)
51
119
  if Native.kevent(@queue.fd, native(flags).pointer, 1, nil, 0, nil) < 0
52
120
  KQueue.handle_error
53
121
  end
@@ -0,0 +1,21 @@
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
+ @file = File.open(path) # TODO: not JRuby-compatible
17
+ super(queue, @file.fileno, :vnode, flags, nil, callback)
18
+ end
19
+ end
20
+ end
21
+ end
@@ -1,8 +1,16 @@
1
1
  module KQueue
2
2
  class Watcher
3
+ # The {Watcher} subclass for process events.
4
+ # Process events are watched via {Queue#watch_process}.
3
5
  class Process < Watcher
6
+ # The process id of the process being watched.
7
+ #
8
+ # @return [Fixnum]
4
9
  attr_reader :pid
5
10
 
11
+ # Creates a new process Watcher.
12
+ #
13
+ # @private
6
14
  def initialize(queue, pid, flags, callback)
7
15
  @pid = pid
8
16
  super(queue, pid, :proc, flags, nil, callback)
@@ -1,9 +1,27 @@
1
1
  module KQueue
2
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.
3
11
  class ReadWrite < Watcher
12
+ # The file descriptor for the stream being watched.
13
+ #
14
+ # @return [Fixnum]
4
15
  attr_reader :fd
16
+
17
+ # The type of watcher, `:read` or `:write`.
18
+ #
19
+ # @return [Symbol]
5
20
  attr_reader :type
6
21
 
22
+ # Creates a new read/write Watcher.
23
+ #
24
+ # @private
7
25
  def initialize(queue, fd, type, callback)
8
26
  @fd = fd
9
27
  @type = type
@@ -1,8 +1,24 @@
1
1
  module KQueue
2
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.
3
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]
4
17
  attr_reader :low_water
5
18
 
19
+ # Creates a new socket Watcher.
20
+ #
21
+ # @private
6
22
  def initialize(queue, fd, type, low_water, callback)
7
23
  @fd = fd
8
24
  @type = type
data/rb-kqueue.gemspec ADDED
@@ -0,0 +1,59 @@
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}
8
+ s.version = "0.0.2"
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/socket_read_write.rb",
35
+ "rb-kqueue.gemspec"
36
+ ]
37
+ s.homepage = %q{http://github.com/nex3/rb-kqueue}
38
+ s.rdoc_options = ["--charset=UTF-8"]
39
+ s.require_paths = ["lib"]
40
+ s.rubygems_version = %q{1.3.5}
41
+ s.summary = %q{A Ruby wrapper for BSD's kqueue, using FFI}
42
+
43
+ if s.respond_to? :specification_version then
44
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
45
+ s.specification_version = 3
46
+
47
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
48
+ s.add_runtime_dependency(%q<ffi>, [">= 0.5.0"])
49
+ s.add_development_dependency(%q<yard>, [">= 0.4.0"])
50
+ else
51
+ s.add_dependency(%q<ffi>, [">= 0.5.0"])
52
+ s.add_dependency(%q<yard>, [">= 0.4.0"])
53
+ end
54
+ else
55
+ s.add_dependency(%q<ffi>, [">= 0.5.0"])
56
+ s.add_dependency(%q<yard>, [">= 0.4.0"])
57
+ end
58
+ end
59
+
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rb-kqueue
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nathan Weizenbaum
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2010-02-07 00:00:00 -08:00
12
+ date: 2010-02-08 00:00:00 -08:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
@@ -41,6 +41,8 @@ extensions: []
41
41
  extra_rdoc_files:
42
42
  - README.md
43
43
  files:
44
+ - .gitignore
45
+ - .yardopts
44
46
  - MIT-LICENSE
45
47
  - README.md
46
48
  - Rakefile
@@ -51,10 +53,11 @@ files:
51
53
  - lib/rb-kqueue/native/flags.rb
52
54
  - lib/rb-kqueue/queue.rb
53
55
  - lib/rb-kqueue/watcher.rb
56
+ - lib/rb-kqueue/watcher/file.rb
54
57
  - lib/rb-kqueue/watcher/process.rb
55
58
  - lib/rb-kqueue/watcher/read_write.rb
56
59
  - lib/rb-kqueue/watcher/socket_read_write.rb
57
- - lib/rb-kqueue/watcher/vnode.rb
60
+ - rb-kqueue.gemspec
58
61
  has_rdoc: true
59
62
  homepage: http://github.com/nex3/rb-kqueue
60
63
  licenses: []
@@ -1,13 +0,0 @@
1
- module KQueue
2
- class Watcher
3
- class VNode < Watcher
4
- attr_reader :path
5
-
6
- def initialize(queue, path, flags, callback)
7
- @path = path
8
- @file = File.open(path) # TODO: not JRuby-compatible
9
- super(queue, @file.fileno, :vnode, flags, nil, callback)
10
- end
11
- end
12
- end
13
- end