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 +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
|