rb-kqueue 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2010 Nathan Weizenbaum
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,5 @@
1
+ # rb-kqueue
2
+
3
+ This is a simple wrapper over the [kqueue](http://en.wikipedia.org/wiki/Kqueue)
4
+ BSD event notification interface (supported on FreeBSD, NetBSD, OpenBSD, and Darwin).
5
+ It uses the [FFI](http://wiki.github.com/ffi/ffi) gem to avoid having to compile a C extension.
data/Rakefile ADDED
@@ -0,0 +1,51 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ begin
5
+ require 'jeweler'
6
+ Jeweler::Tasks.new do |gem|
7
+ gem.name = "rb-kqueue"
8
+ gem.summary = "A Ruby wrapper for BSD's kqueue, using FFI"
9
+ gem.description = gem.summary
10
+ gem.email = "nex342@gmail.com"
11
+ gem.homepage = "http://github.com/nex3/rb-kqueue"
12
+ gem.authors = ["Nathan Weizenbaum"]
13
+ gem.add_dependency "ffi", ">= 0.5.0"
14
+ gem.add_development_dependency "yard", ">= 0.4.0"
15
+ end
16
+ Jeweler::GemcutterTasks.new
17
+ rescue LoadError
18
+ puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
19
+ end
20
+
21
+ module Jeweler::VersionHelper::PlaintextExtension
22
+ def write_with_inotify
23
+ write_without_inotify
24
+ filename = File.join(File.dirname(__FILE__), "lib/rb-kqueue.rb")
25
+ text = File.read(filename)
26
+ File.open(filename, 'w') do |f|
27
+ f.write text.gsub(/^( VERSION = ).*/, '\1' + [major, minor, patch].inspect)
28
+ end
29
+ end
30
+ alias_method :write_without_inotify, :write
31
+ alias_method :write, :write_with_inotify
32
+ end
33
+
34
+ class Jeweler::Commands::Version::Base
35
+ def commit_version_with_inotify
36
+ return unless self.repo
37
+ self.repo.add(File.join(File.dirname(__FILE__), "lib/rb-kqueue.rb"))
38
+ commit_version_without_inotify
39
+ end
40
+ alias_method :commit_version_without_inotify, :commit_version
41
+ alias_method :commit_version, :commit_version_with_inotify
42
+ end
43
+
44
+ begin
45
+ require 'yard'
46
+ YARD::Rake::YardocTask.new
47
+ rescue LoadError
48
+ task :yardoc do
49
+ abort "YARD is not available. In order to run yardoc, you must: sudo gem install yard"
50
+ end
51
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.0.1
data/lib/rb-kqueue.rb ADDED
@@ -0,0 +1,28 @@
1
+ require 'rb-kqueue/native'
2
+ require 'rb-kqueue/native/flags'
3
+ require 'rb-kqueue/watcher'
4
+ require 'rb-kqueue/watcher/vnode'
5
+ require 'rb-kqueue/watcher/read_write'
6
+ require 'rb-kqueue/watcher/process'
7
+ require 'rb-kqueue/event'
8
+ require 'rb-kqueue/queue'
9
+
10
+ module KQueue
11
+ VERSION = [0, 0, 1]
12
+
13
+ def self.handle_error(errno = FFI.errno)
14
+ raise SystemCallError.new(
15
+ "KQueue failed" +
16
+ case errno
17
+ when Errno::EFAULT::Errno; ": There was an error reading or writing the kevent structure."
18
+ when Errno::EBADF::Errno; ": The specified descriptor is invalid."
19
+ when Errno::EINTR::Errno; ": A signal was delivered before the timeout expired and before any events were placed on the kqueue for return."
20
+ when Errno::EINVAL::Errno; ": The specified time limit or filter is invalid."
21
+ when Errno::ENOENT::Errno; ": The event could not be found to be modified or deleted."
22
+ when Errno::ENOMEM::Errno; ": No memory was available to register the event."
23
+ when Errno::ESRCH::Errno; ": The specified process to attach to does not exist."
24
+ else; ""
25
+ end,
26
+ errno)
27
+ end
28
+ end
@@ -0,0 +1,32 @@
1
+ module KQueue
2
+ class Event
3
+ attr_reader :data
4
+ attr_reader :filter
5
+
6
+ def watcher
7
+ @watcher ||= @queue.watchers[[filter, @native[:ident]]]
8
+ end
9
+
10
+ def flags
11
+ @fflags ||= Native::Flags.from_mask("NOTE", @native[:fflags])
12
+ end
13
+
14
+ def eof?
15
+ @flags.include?(:eof)
16
+ end
17
+
18
+ def initialize(native, queue)
19
+ @native = native
20
+ @queue = queue
21
+ @data = @native[:data]
22
+ @filter = KQueue::Native::Flags.from_flag("EVFILT", @native[:filter])
23
+ @flags = Native::Flags.from_mask("EV", @native[:flags])
24
+
25
+ KQueue.handle_error @native[:data] if @flags.inclue?(:error)
26
+ end
27
+
28
+ def callback!
29
+ watcher.callback! self
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,26 @@
1
+ require 'ffi'
2
+
3
+ module KQueue
4
+ module Native
5
+ extend FFI::Library
6
+
7
+ class KEvent < FFI::Struct
8
+ layout(
9
+ :ident, :uintptr_t,
10
+ :filter, :int16,
11
+ :flags, :uint16,
12
+ :fflags, :uint32,
13
+ :data, :intptr_t,
14
+ :udata, :pointer)
15
+ end
16
+
17
+ class TimeSpec < FFI::Struct
18
+ layout(
19
+ :tv_sec, :time_t,
20
+ :tv_nsec, :long)
21
+ end
22
+
23
+ attach_function :kqueue, [], :int
24
+ attach_function :kevent, [:int, :pointer, :int, :pointer, :int, :pointer], :int
25
+ end
26
+ end
@@ -0,0 +1,85 @@
1
+ module KQueue
2
+ module Native
3
+ module Flags
4
+ # Filters
5
+ EVFILT_READ = -1
6
+ EVFILT_WRITE = -2
7
+ EVFILT_AIO = -3 # Attached to aio requests
8
+ EVFILT_VNODE = -4 # Attached to vnodes
9
+ EVFILT_PROC = -5 # Attached to struct proc
10
+ EVFILT_SIGNAL = -6 # Attached to struct proc
11
+ EVFILT_TIMER = -7 # Timers
12
+ EVFILT_MACHPORT = -8 # Mach portsets
13
+ EVFILT_FS = -9 # Filesystem events
14
+ EVFILT_USER = -10 # User events
15
+ EVFILT_SESSION = -11 # Audit session events
16
+
17
+
18
+ # Actions
19
+ EV_ADD = 0x0001 # Add event to kq (implies enable)
20
+ EV_DELETE = 0x0002 # Delete event from kq
21
+ EV_ENABLE = 0x0004 # Enable event
22
+ EV_DISABLE = 0x0008 # Disable event (not reported)
23
+ EV_RECEIPT = 0x0040 # Force EV_ERROR on success, data == 0
24
+
25
+ # Flags
26
+ EV_ONESHOT = 0x0010 # Only report one occurrence
27
+ EV_CLEAR = 0x0020 # Clear event state after reporting
28
+ EV_DISPATCH = 0x0080 # Disable event after reporting
29
+
30
+ # Returned values
31
+ EV_EOF = 0x8000 # EOF detected
32
+ EV_ERROR = 0x4000 # Error, data contains errno
33
+
34
+
35
+ # For EVFILT_{READ,WRITE}
36
+ NOTE_LOWAT = 0x00000001 # Low water mark
37
+
38
+ # For EVFILT_VNODE
39
+ NOTE_DELETE = 0x00000001 # Vnode was removed
40
+ NOTE_WRITE = 0x00000002 # Data contents changed
41
+ NOTE_EXTEND = 0x00000004 # Size increased
42
+ NOTE_ATTRIB = 0x00000008 # Attributes changed
43
+ NOTE_LINK = 0x00000010 # Link count changed
44
+ NOTE_RENAME = 0x00000020 # Vnode was renamed
45
+ NOTE_REVOKE = 0x00000040 # Vnode access was revoked
46
+ NOTE_NONE = 0x00000080 # No specific vnode event: to test for EVFILT_READ activation
47
+
48
+
49
+ # Converts a list of flags to the bitmask that the C API expects.
50
+ #
51
+ # @param prefix [String] The prefix for the C names of the flags
52
+ # @param flags [Array<Symbol>]
53
+ # @return [Fixnum]
54
+ def self.to_mask(prefix, flags)
55
+ flags.map {|flag| const_get("#{prefix}_#{flag.to_s.upcase}")}.
56
+ inject(0) {|mask, flag| mask | flag}
57
+ end
58
+
59
+ # Converts a bitmask from the C API into a list of flags.
60
+ #
61
+ # @param prefix [String] The prefix for the C names of the flags
62
+ # @param mask [Fixnum]
63
+ # @return [Array<Symbol>]
64
+ def self.from_mask(prefix, mask)
65
+ re = /^#{Regexp.quote prefix}_/
66
+ constants.select do |c|
67
+ next false unless c =~ re
68
+ const_get(c) & mask != 0
69
+ end.map {|c| c.sub("#{prefix}_", "").downcase.to_sym}
70
+ end
71
+
72
+ def self.to_flag(prefix, flag)
73
+ const_get("#{prefix}_#{flag.to_s.upcase}")
74
+ end
75
+
76
+ def self.from_flag(prefix, flag)
77
+ re = /^#{Regexp.quote prefix}_/
78
+ constants.each do |c|
79
+ next unless c =~ re
80
+ return c.sub("#{prefix}_", "").downcase.to_sym if const_get(c) == flag
81
+ end
82
+ end
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,61 @@
1
+ module KQueue
2
+ class Queue
3
+ attr_reader :fd
4
+ attr_reader :watchers
5
+
6
+ def initialize
7
+ @fd = Native.kqueue
8
+ @watchers = {}
9
+ end
10
+
11
+ def watch_for_read(fd, &callback)
12
+ fd = fd.fileno if fd.respond_to?(:fileno)
13
+ Watcher::ReadWrite.new(self, fd, :read, callback)
14
+ end
15
+
16
+ def watch_for_socket_read(fd, low_water = nil, &callback)
17
+ fd = fd.fileno if fd.respond_to?(:fileno)
18
+ Watcher::SocketReadWrite.new(self, fd, :read, low_water, callback)
19
+ end
20
+
21
+ def watch_for_write(fd, &callback)
22
+ fd = fd.fileno if fd.respond_to?(:fileno)
23
+ Watcher::ReadWrite.new(self, fd, :write, callback)
24
+ end
25
+
26
+ def watch_for_socket_write(fd, low_water = nil, &callback)
27
+ fd = fd.fileno if fd.respond_to?(:fileno)
28
+ Watcher::SocketReadWrite.new(self, fd, :write, low_water, callback)
29
+ end
30
+
31
+ def watch_for_file_change(path, *flags, &callback)
32
+ Watcher::VNode.new(self, path, flags, callback)
33
+ end
34
+
35
+ def watch_for_process_change(pid, *flags, &callback)
36
+ Watcher::Process.new(self, path, flags, callback)
37
+ end
38
+
39
+ def run
40
+ @stop = false
41
+ process until @stop
42
+ end
43
+
44
+ def stop
45
+ @stop = true
46
+ end
47
+
48
+ def process
49
+ read_events.each {|event| event.callback!}
50
+ end
51
+
52
+ def read_events
53
+ size = 1024
54
+ eventlist = FFI::MemoryPointer.new(Native::KEvent, size)
55
+ res = Native.kevent(@fd, nil, 0, eventlist, size, nil)
56
+
57
+ KQueue.handle_error if res < 0
58
+ (0...res).map {|i| KQueue::Event.new(Native::KEvent.new(eventlist[i]), self)}
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,56 @@
1
+ module KQueue
2
+ class Watcher
3
+ attr_reader :queue
4
+
5
+ def initialize(queue, ident, filter, fflags, data, callback)
6
+ @queue = queue
7
+ @ident = ident
8
+ @filter = filter
9
+ @flags = []
10
+ @fflags = fflags
11
+ @data = data
12
+ @callback = callback
13
+ add!
14
+ end
15
+
16
+ def add!
17
+ kqueue! :add, :clear # TODO: Don't always enable :clear
18
+ @queue.watchers[[@filter, @ident]] = self
19
+ end
20
+
21
+ def delete!
22
+ kqueue! :delete
23
+ @queue.watchers.delete([@filter, @ident])
24
+ end
25
+
26
+ def enable!
27
+ kqueue! :enable
28
+ end
29
+
30
+ def disable!
31
+ kqueue! :disable
32
+ end
33
+
34
+ def callback!(event)
35
+ @callback.call event
36
+ end
37
+
38
+ private
39
+
40
+ def native(flags)
41
+ native = Native::KEvent.new
42
+ native[:ident] = @ident
43
+ native[:filter] = Native::Flags.to_flag("EVFILT", @filter)
44
+ native[:flags] = Native::Flags.to_mask("EV", @flags | flags)
45
+ native[:fflags] = Native::Flags.to_mask("NOTE", @fflags)
46
+ native[:data] = @data if @data
47
+ native
48
+ end
49
+
50
+ def kqueue!(*flags)
51
+ if Native.kevent(@queue.fd, native(flags).pointer, 1, nil, 0, nil) < 0
52
+ KQueue.handle_error
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,12 @@
1
+ module KQueue
2
+ class Watcher
3
+ class Process < Watcher
4
+ attr_reader :pid
5
+
6
+ def initialize(queue, pid, flags, callback)
7
+ @pid = pid
8
+ super(queue, pid, :proc, flags, nil, callback)
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,14 @@
1
+ module KQueue
2
+ class Watcher
3
+ class ReadWrite < Watcher
4
+ attr_reader :fd
5
+ attr_reader :type
6
+
7
+ def initialize(queue, fd, type, callback)
8
+ @fd = fd
9
+ @type = type
10
+ super(queue, @fd, type, [], nil, callback)
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,22 @@
1
+ module KQueue
2
+ class Watcher
3
+ class SocketReadWrite < ReadWrite
4
+ attr_reader :low_water
5
+
6
+ def initialize(queue, fd, type, low_water, callback)
7
+ @fd = fd
8
+ @type = type
9
+
10
+ if low_water
11
+ fflags = [:lowat]
12
+ data = low_water
13
+ else
14
+ fflags = []
15
+ data = nil
16
+ end
17
+
18
+ super(queue, @fd, type, fflags, data, callback)
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,13 @@
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
metadata ADDED
@@ -0,0 +1,87 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rb-kqueue
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Nathan Weizenbaum
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2010-02-07 00:00:00 -08:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: ffi
17
+ type: :runtime
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: 0.5.0
24
+ version:
25
+ - !ruby/object:Gem::Dependency
26
+ name: yard
27
+ type: :development
28
+ version_requirement:
29
+ version_requirements: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: 0.4.0
34
+ version:
35
+ description: A Ruby wrapper for BSD's kqueue, using FFI
36
+ email: nex342@gmail.com
37
+ executables: []
38
+
39
+ extensions: []
40
+
41
+ extra_rdoc_files:
42
+ - README.md
43
+ files:
44
+ - MIT-LICENSE
45
+ - README.md
46
+ - Rakefile
47
+ - VERSION
48
+ - lib/rb-kqueue.rb
49
+ - lib/rb-kqueue/event.rb
50
+ - lib/rb-kqueue/native.rb
51
+ - lib/rb-kqueue/native/flags.rb
52
+ - lib/rb-kqueue/queue.rb
53
+ - lib/rb-kqueue/watcher.rb
54
+ - lib/rb-kqueue/watcher/process.rb
55
+ - lib/rb-kqueue/watcher/read_write.rb
56
+ - lib/rb-kqueue/watcher/socket_read_write.rb
57
+ - lib/rb-kqueue/watcher/vnode.rb
58
+ has_rdoc: true
59
+ homepage: http://github.com/nex3/rb-kqueue
60
+ licenses: []
61
+
62
+ post_install_message:
63
+ rdoc_options:
64
+ - --charset=UTF-8
65
+ require_paths:
66
+ - lib
67
+ required_ruby_version: !ruby/object:Gem::Requirement
68
+ requirements:
69
+ - - ">="
70
+ - !ruby/object:Gem::Version
71
+ version: "0"
72
+ version:
73
+ required_rubygems_version: !ruby/object:Gem::Requirement
74
+ requirements:
75
+ - - ">="
76
+ - !ruby/object:Gem::Version
77
+ version: "0"
78
+ version:
79
+ requirements: []
80
+
81
+ rubyforge_project:
82
+ rubygems_version: 1.3.5
83
+ signing_key:
84
+ specification_version: 3
85
+ summary: A Ruby wrapper for BSD's kqueue, using FFI
86
+ test_files: []
87
+