rb-kqueue 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
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