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 +3 -0
- data/.yardopts +5 -0
- data/README.md +31 -0
- data/Rakefile +8 -8
- data/VERSION +1 -1
- data/lib/rb-kqueue.rb +14 -2
- data/lib/rb-kqueue/event.rb +51 -0
- data/lib/rb-kqueue/native.rb +11 -0
- data/lib/rb-kqueue/native/flags.rb +23 -3
- data/lib/rb-kqueue/queue.rb +237 -7
- data/lib/rb-kqueue/watcher.rb +73 -5
- data/lib/rb-kqueue/watcher/file.rb +21 -0
- data/lib/rb-kqueue/watcher/process.rb +8 -0
- data/lib/rb-kqueue/watcher/read_write.rb +18 -0
- data/lib/rb-kqueue/watcher/socket_read_write.rb +16 -0
- data/rb-kqueue.gemspec +59 -0
- metadata +6 -3
- data/lib/rb-kqueue/watcher/vnode.rb +0 -13
data/.gitignore
ADDED
data/.yardopts
ADDED
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
|
23
|
-
|
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 :
|
31
|
-
alias_method :write, :
|
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
|
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
|
-
|
38
|
+
commit_version_without_kqueue
|
39
39
|
end
|
40
|
-
alias_method :
|
41
|
-
alias_method :commit_version, :
|
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
|
+
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/
|
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,
|
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" +
|
data/lib/rb-kqueue/event.rb
CHANGED
@@ -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
|
data/lib/rb-kqueue/native.rb
CHANGED
@@ -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
|
-
|
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|
|
data/lib/rb-kqueue/queue.rb
CHANGED
@@ -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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
32
|
-
|
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
|
-
|
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)
|
data/lib/rb-kqueue/watcher.rb
CHANGED
@@ -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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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.
|
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-
|
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
|
-
-
|
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
|