rev 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,101 @@
1
+ #--
2
+ # Copyright (C)2007 Tony Arcieri
3
+ # You can redistribute this under the terms of the Ruby license
4
+ # See file LICENSE for details
5
+ #++
6
+
7
+ require File.dirname(__FILE__) + '/../rev'
8
+
9
+ module Rev
10
+ class Loop
11
+ attr_reader :watchers
12
+
13
+ # Create a new Rev::Loop
14
+ #
15
+ # Options:
16
+ #
17
+ # :skip_environment (boolean)
18
+ # Ignore the $LIBEV_FLAGS environment variable
19
+ #
20
+ # :fork_check (boolean)
21
+ # Enable autodetection of forks
22
+ #
23
+ # :backend
24
+ # Choose the default backend, one (or many in an array) of:
25
+ # :select (most platforms)
26
+ # :poll (most platforms except Windows)
27
+ # :epoll (Linux)
28
+ # :kqueue (BSD/Mac OS X)
29
+ # :port (Solaris 10)
30
+ #
31
+ def initialize(options = {})
32
+ @watchers = []
33
+ @active_watchers = 0
34
+
35
+ flags = 0
36
+
37
+ options.each do |option, value|
38
+ case option
39
+ when :skip_environment
40
+ flags |= EVFLAG_NOEV if value
41
+ when :fork_check
42
+ flags |= EVFLAG_FORKCHECK if value
43
+ when :backend
44
+ value = [value] unless value.is_a? Array
45
+ value.each do |backend|
46
+ case backend
47
+ when :select then flags |= EVBACKEND_SELECT
48
+ when :poll then flags |= EVBACKEND_POLL
49
+ when :epoll then flags |= EVBACKEND_EPOLL
50
+ when :kqueue then flags |= EVBACKEND_KQUEUE
51
+ when :port then flags |= EVBACKEND_PORT
52
+ else raise ArgumentError, "no such backend: #{backend}"
53
+ end
54
+ end
55
+ else raise ArgumentError, "no such option: #{option}"
56
+ end
57
+ end
58
+
59
+ @loop = ev_loop_new(flags)
60
+ end
61
+
62
+ # Attach a watcher to the loop
63
+ def attach(watcher)
64
+ watcher.attach self
65
+ end
66
+
67
+ # Run the event loop and dispatch events back to Ruby. If there
68
+ # are no watchers associated with the event loop it will return
69
+ # immediately. Otherwise, run will continue blocking and making
70
+ # event callbacks to watchers until all watchers associated with
71
+ # the loop have been disabled or detached. The loop may be
72
+ # explicitly stopped by calling the stop method on the loop object.
73
+ def run
74
+ raise RuntimeError, "no watchers for this loop" if @watchers.empty?
75
+
76
+ @running = true
77
+ while @running and not @active_watchers.zero?
78
+ run_once
79
+ end
80
+ end
81
+
82
+ # Stop the event loop if it's running
83
+ def stop
84
+ raise RuntimeError, "loop not running" unless @running
85
+ @running = false
86
+ end
87
+
88
+ #######
89
+ private
90
+ #######
91
+
92
+ EVFLAG_NOENV = 0x1000000 # do NOT consult environment
93
+ EVFLAG_FORKCHECK = 0x2000000 # check for a fork in each iteration
94
+
95
+ EVBACKEND_SELECT = 0x00000001 # supported about anywhere
96
+ EVBACKEND_POLL = 0x00000002 # !win
97
+ EVBACKEND_EPOLL = 0x00000004 # linux
98
+ EVBACKEND_KQUEUE = 0x00000008 # bsd
99
+ EVBACKEND_PORT = 0x00000020 # solaris 10
100
+ end
101
+ end
@@ -0,0 +1,53 @@
1
+ #--
2
+ # Copyright (C)2007 Tony Arcieri
3
+ # You can redistribute this under the terms of the Ruby license
4
+ # See file LICENSE for details
5
+ #++
6
+
7
+ require File.dirname(__FILE__) + '/../rev'
8
+
9
+ module Rev
10
+ class Server < Listener
11
+ def initialize(listen_socket, klass = Socket, *args, &block)
12
+ # Ensure the provided class responds to attach
13
+ unless (klass.instance_method(:attach) rescue nil)
14
+ raise ArgumentError, "provided class must respond to 'attach'"
15
+ end
16
+
17
+ # Verify the arity of the provided arguments
18
+ arity = klass.instance_method(:initialize).arity
19
+ expected = arity >= 0 ? arity : -(arity + 1)
20
+
21
+ if (arity >= 0 and args.size + 1 != expected) or (arity < 0 and args.size + 1 < expected)
22
+ raise ArgumentError, "wrong number of arguments for #{klass}#initialize (#{args.size+1} for #{expected})"
23
+ end
24
+
25
+ @klass, @args, @block = klass, args, block
26
+ super(listen_socket)
27
+ end
28
+
29
+ #########
30
+ protected
31
+ #########
32
+
33
+ def on_connection(socket)
34
+ connection = @klass.new(socket, *@args).attach(evloop)
35
+ @block.(connection) if @block
36
+ connection.on_connect
37
+ end
38
+ end
39
+
40
+ class TCPServer < Server
41
+ def initialize(host, port, klass = TCPSocket, *args, &block)
42
+ listen_socket = ::TCPServer.new(host, port)
43
+ listen_socket.instance_eval { listen(1024) } # Change listen backlog to 1024
44
+ super(listen_socket, klass, *args, &block)
45
+ end
46
+ end
47
+
48
+ class UNIXServer < Server
49
+ def initialize(path, klass = UNIXSocket, *args, &block)
50
+ super(::UNIXServer.new(*args), klass, *args, &block)
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,186 @@
1
+ #--
2
+ # Copyright (C)2007 Tony Arcieri
3
+ # You can redistribute this under the terms of the Ruby license
4
+ # See file LICENSE for details
5
+ #++
6
+
7
+ require 'socket'
8
+ require 'resolv'
9
+ require File.dirname(__FILE__) + '/../rev'
10
+
11
+ module Rev
12
+ class Socket < BufferedIO
13
+ def self.connect(socket, *args)
14
+ new(socket, *args).instance_eval {
15
+ @connector = Connector.new(self, socket)
16
+ self
17
+ }
18
+ end
19
+
20
+ watcher_delegate :@connector
21
+
22
+ def attach(evloop)
23
+ raise RuntimeError, "connection failed" if @failed
24
+
25
+ if @connector
26
+ @connector.attach(evloop)
27
+ return self
28
+ end
29
+
30
+ super
31
+ end
32
+
33
+ # Called upon completion of a socket connection
34
+ def on_connect; end
35
+ event_callback :on_connect
36
+
37
+ # Called if a socket connection failed to complete
38
+ def on_connect_failed; end
39
+ event_callback :on_connect_failed
40
+
41
+ #########
42
+ protected
43
+ #########
44
+
45
+ class Connector < IOWatcher
46
+ def initialize(rev_socket, ruby_socket)
47
+ @rev_socket, @ruby_socket = rev_socket, ruby_socket
48
+ super(ruby_socket, :w)
49
+ end
50
+
51
+ def on_writable
52
+ evl = evloop
53
+ detach
54
+
55
+ if connect_successful?
56
+ @rev_socket.instance_eval { @connector = nil }
57
+ @rev_socket.attach(evl)
58
+ @rev_socket.on_connect
59
+ else
60
+ @rev_socket.instance_eval { @failed = true }
61
+ @rev_socket.on_connect_failed
62
+ end
63
+ end
64
+
65
+ #######
66
+ private
67
+ #######
68
+
69
+ def connect_successful?
70
+ @ruby_socket.getsockopt(::Socket::SOL_SOCKET, ::Socket::SO_ERROR).unpack('i').first == 0
71
+ end
72
+ end
73
+ end
74
+
75
+ class TCPSocket < Socket
76
+ attr_reader :remote_host, :remote_addr, :remote_port, :address_family
77
+ watcher_delegate :@resolver
78
+
79
+ # Perform a non-blocking connect to the given host and port
80
+ def self.connect(addr, port, *args)
81
+ family = nil
82
+
83
+ if (Resolv::IPv4.create(addr) rescue nil)
84
+ family = ::Socket::AF_INET
85
+ elsif(Resolv::IPv6.create(addr) rescue nil)
86
+ family = ::Socket::AF_INET6
87
+ end
88
+
89
+ if family
90
+ return super(TCPConnectSocket.new(family, addr, port), *args)
91
+ end
92
+
93
+ if host = Rev::DNSResolver.hosts(addr)
94
+ return connect(host, port, *args)
95
+ end
96
+
97
+ return allocate.instance_eval {
98
+ @remote_host, @remote_addr, @remote_port = addr, addr, port
99
+ @resolver = TCPConnectResolver.new(self, addr, port, *args)
100
+ }
101
+ end
102
+
103
+ def initialize(socket)
104
+ unless socket.is_a?(::TCPSocket) or socket.is_a?(TCPConnectSocket)
105
+ raise TypeError, "socket must be a TCPSocket"
106
+ end
107
+
108
+ super
109
+
110
+ @address_family, @remote_port, @remote_host, @remote_addr = socket.peeraddr
111
+ end
112
+
113
+ #########
114
+ protected
115
+ #########
116
+
117
+ class TCPConnectSocket < ::Socket
118
+ def initialize(family, addr, port, host = addr)
119
+ @host, @addr, @port = host, addr, port
120
+ @address_family = nil
121
+
122
+ @socket = super(family, ::Socket::SOCK_STREAM, 0)
123
+ begin
124
+ @socket.connect_nonblock(::Socket.sockaddr_in(port, addr))
125
+ rescue Errno::EINPROGRESS
126
+ end
127
+ end
128
+
129
+ def peeraddr
130
+ [
131
+ @address_family == ::Socket::AF_INET ? 'AF_INET' : 'AF_INET6',
132
+ @port,
133
+ @host,
134
+ @addr
135
+ ]
136
+ end
137
+ end
138
+
139
+ class TCPConnectResolver < Rev::DNSResolver
140
+ def initialize(socket, host, port, *args)
141
+ @sock, @host, @port, @args = socket, host, port, args
142
+ super(host)
143
+ end
144
+
145
+ def on_success(addr)
146
+ host, port, args = @host, @port, @args
147
+
148
+ @sock.instance_eval {
149
+ # DNSResolver only supports IPv4 so we can safely assume an IPv4 address
150
+ socket = TCPConnectSocket.new(::Socket::AF_INET, addr, port, host)
151
+ initialize(socket, *args)
152
+ @connector = Connector.new(self, socket)
153
+ @resolver = nil
154
+ }
155
+ @sock.attach(evloop)
156
+ end
157
+
158
+ def on_failure
159
+ @sock.on_connect_failed
160
+ @sock.instance_eval {
161
+ @resolver = nil
162
+ @failed = true
163
+ }
164
+ return
165
+ end
166
+
167
+ alias_method :on_timeout, :on_failure
168
+ end
169
+ end
170
+
171
+ class UNIXSocket < Socket
172
+ attr_reader :path, :address_family
173
+
174
+ # Connect to the given UNIX domain socket
175
+ def self.connect(path, *args)
176
+ new(::UNIXSocket.new(path), *args)
177
+ end
178
+
179
+ def initialize(socket)
180
+ raise ArgumentError, "socket must be a UNIXSocket" unless socket.is_a? ::UNIXSocket
181
+
182
+ super
183
+ @address_family, @path = socket.peeraddr
184
+ end
185
+ end
186
+ end
@@ -0,0 +1,18 @@
1
+ #--
2
+ # Copyright (C)2007 Tony Arcieri
3
+ # You can redistribute this under the terms of the Ruby license
4
+ # See file LICENSE for details
5
+ #++
6
+
7
+ require File.dirname(__FILE__) + '/../rev'
8
+
9
+ module Rev
10
+ class IOWatcher
11
+ # The actual implementation of this class resides in the C extension
12
+ # Here we metaprogram proper event_callbacks for the callback methods
13
+ # These can take a block and store it to be called when the event
14
+ # is actually fired.
15
+
16
+ event_callback :timer_watcher
17
+ end
18
+ end
@@ -0,0 +1,49 @@
1
+ #--
2
+ # Copyright (C)2007 Tony Arcieri
3
+ # You can redistribute this under the terms of the Ruby license
4
+ # See file LICENSE for details
5
+ #++
6
+
7
+ require File.dirname(__FILE__) + '/../rev'
8
+
9
+ module Rev
10
+ class Watcher
11
+ # Use an alternate watcher with the attach/detach/enable/disable methods
12
+ # if it is presently assigned. This is useful if you are waiting for
13
+ # an event to occur before the current watcher can be used in earnest,
14
+ # such as making an outgoing TCP connection.
15
+ def self.watcher_delegate(proxy_var)
16
+ %w{attach detach enable disable}.each do |method|
17
+ module_eval <<-EOD
18
+ def #{method}(*args)
19
+ if #{proxy_var}
20
+ #{proxy_var}.#{method}(*args)
21
+ return self
22
+ end
23
+
24
+ super
25
+ end
26
+ EOD
27
+ end
28
+ end
29
+
30
+ # Define callbacks whose behavior can be changed on-the-fly per instance.
31
+ # This is done by giving a block to the callback method, which is captured
32
+ # as a proc and stored for later. If the method is called without a block,
33
+ # the stored block is executed if present, otherwise it's a noop.
34
+ def self.event_callback(*methods)
35
+ methods.each do |method|
36
+ module_eval <<-EOD
37
+ def #{method}(*args, &block)
38
+ if block
39
+ @#{method}_callback = block
40
+ return
41
+ end
42
+
43
+ @#{method}_callback.(*([self] + args)) if @#{method}_callback
44
+ end
45
+ EOD
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,26 @@
1
+ #
2
+ # Oops, were you expecting some real RSpec?
3
+ #
4
+ # I'm sorry to disappoint. Really, this is pretty damn weak.
5
+ #
6
+ # I swear that after Ruby 1.9 comes out proper, this will become an actual spec
7
+ #
8
+
9
+ require 'socket'
10
+ require File.dirname(__FILE__) + '/../lib/rev'
11
+
12
+ class MyWatcher < Rev::IOWatcher
13
+ def on_readable
14
+ puts "Yippie, I'm readable!"
15
+ detach
16
+ end
17
+ end
18
+
19
+ listen_socket = TCPServer.new 'localhost', 4321
20
+ watcher = MyWatcher.new(listen_socket)
21
+ event_loop = Rev::Loop.new
22
+ watcher.attach(event_loop)
23
+
24
+ puts "Running the event loop in blocking mode on localhost port 4321"
25
+ puts "Telnet there or something and you should get a read event"
26
+ event_loop.run
metadata ADDED
@@ -0,0 +1,93 @@
1
+ --- !ruby/object:Gem::Specification
2
+ rubygems_version: 0.9.4
3
+ specification_version: 1
4
+ name: rev
5
+ version: !ruby/object:Gem::Version
6
+ version: 0.1.0
7
+ date: 2007-12-25 00:00:00 -07:00
8
+ summary: Ruby 1.9 binding to the libev high performance event library
9
+ require_paths:
10
+ - lib
11
+ email:
12
+ homepage:
13
+ rubyforge_project:
14
+ description:
15
+ autorequire:
16
+ default_executable:
17
+ bindir: bin
18
+ has_rdoc: true
19
+ required_ruby_version: !ruby/object:Gem::Version::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: 1.9.0
24
+ version:
25
+ platform: ruby
26
+ signing_key:
27
+ cert_chain:
28
+ post_install_message:
29
+ authors:
30
+ - Tony Arcieri
31
+ files:
32
+ - LICENSE
33
+ - README
34
+ - lib/rev
35
+ - lib/rev/buffered_io.rb
36
+ - lib/rev/dns_resolver.rb
37
+ - lib/rev/io_watcher.rb
38
+ - lib/rev/listener.rb
39
+ - lib/rev/loop.rb
40
+ - lib/rev/server.rb
41
+ - lib/rev/socket.rb
42
+ - lib/rev/timer_watcher.rb
43
+ - lib/rev/watcher.rb
44
+ - lib/rev.rb
45
+ - ext/libev/ev.h
46
+ - ext/libev/ev_vars.h
47
+ - ext/libev/ev_wrap.h
48
+ - ext/rev/rev.h
49
+ - ext/rev/rev_watcher.h
50
+ - ext/libev/ev.c
51
+ - ext/libev/ev_epoll.c
52
+ - ext/libev/ev_kqueue.c
53
+ - ext/libev/ev_poll.c
54
+ - ext/libev/ev_port.c
55
+ - ext/libev/ev_select.c
56
+ - ext/libev/ev_win32.c
57
+ - ext/rev/rev_ext.c
58
+ - ext/rev/rev_io_watcher.c
59
+ - ext/rev/rev_loop.c
60
+ - ext/rev/rev_timer_watcher.c
61
+ - ext/rev/rev_watcher.c
62
+ - ext/rev/extconf.rb
63
+ test_files:
64
+ - spec/rev_spec.rb
65
+ rdoc_options:
66
+ - --title
67
+ - Rev
68
+ - --main
69
+ - README
70
+ - --line-numbers
71
+ extra_rdoc_files:
72
+ - README
73
+ - LICENSE
74
+ - ext/libev/ev.c
75
+ - ext/libev/ev_epoll.c
76
+ - ext/libev/ev_kqueue.c
77
+ - ext/libev/ev_poll.c
78
+ - ext/libev/ev_port.c
79
+ - ext/libev/ev_select.c
80
+ - ext/libev/ev_win32.c
81
+ - ext/rev/rev_ext.c
82
+ - ext/rev/rev_io_watcher.c
83
+ - ext/rev/rev_loop.c
84
+ - ext/rev/rev_timer_watcher.c
85
+ - ext/rev/rev_watcher.c
86
+ executables: []
87
+
88
+ extensions:
89
+ - ext/rev/extconf.rb
90
+ requirements: []
91
+
92
+ dependencies: []
93
+