cool.io 1.0.0 → 1.1.0
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 +26 -0
- data/.rspec +3 -0
- data/.travis.yml +4 -0
- data/{CHANGES → CHANGES.md} +43 -79
- data/Gemfile +4 -0
- data/Gemfile.lock +32 -0
- data/{README.markdown → README.md} +0 -4
- data/Rakefile +31 -67
- data/cool.io.gemspec +25 -133
- data/ext/cool.io/extconf.rb +3 -0
- data/ext/cool.io/stat_watcher.c +99 -23
- data/ext/libev/Changes +24 -0
- data/ext/libev/ev.c +78 -32
- data/ext/libev/ev.h +11 -8
- data/ext/libev/ev_epoll.c +39 -7
- data/ext/libev/ev_kqueue.c +5 -5
- data/ext/libev/ev_poll.c +5 -5
- data/ext/libev/ev_port.c +26 -11
- data/ext/libev/ev_select.c +11 -8
- data/ext/libev/ev_vars.h +10 -4
- data/ext/libev/ev_win32.c +6 -6
- data/ext/libev/ev_wrap.h +10 -0
- data/lib/cool.io.rb +3 -3
- data/lib/cool.io/async_watcher.rb +1 -1
- data/lib/cool.io/dns_resolver.rb +14 -12
- data/lib/cool.io/dsl.rb +26 -26
- data/lib/cool.io/eventmachine.rb +18 -18
- data/lib/cool.io/http_client.rb +29 -22
- data/lib/cool.io/io.rb +18 -18
- data/lib/cool.io/iowatcher.rb +1 -1
- data/lib/cool.io/listener.rb +2 -2
- data/lib/cool.io/loop.rb +12 -12
- data/lib/cool.io/meta.rb +4 -4
- data/lib/cool.io/server.rb +3 -3
- data/lib/cool.io/socket.rb +36 -28
- data/lib/cool.io/timer_watcher.rb +1 -1
- data/lib/cool.io/version.rb +5 -0
- data/lib/coolio.rb +1 -1
- data/spec/stat_watcher_spec.rb +77 -0
- metadata +47 -56
- data/VERSION +0 -1
- data/lib/rev.rb +0 -4
- data/spec/possible_tests/schedules_other_threads.rb +0 -48
- data/spec/possible_tests/test_on_resolve_failed.rb +0 -9
- data/spec/possible_tests/test_resolves.rb +0 -27
- data/spec/possible_tests/test_write_during_resolve.rb +0 -27
- data/spec/possible_tests/works_straight.rb +0 -71
data/lib/cool.io/io.rb
CHANGED
@@ -15,7 +15,7 @@ module Coolio
|
|
15
15
|
# Socket class and its associated subclasses.
|
16
16
|
class IO
|
17
17
|
extend Meta
|
18
|
-
|
18
|
+
|
19
19
|
# Maximum number of bytes to consume at once
|
20
20
|
INPUT_SIZE = 16384
|
21
21
|
|
@@ -29,32 +29,32 @@ module Coolio
|
|
29
29
|
#
|
30
30
|
# Watcher methods, delegated to @_read_watcher
|
31
31
|
#
|
32
|
-
|
32
|
+
|
33
33
|
# Attach to the event loop
|
34
34
|
def attach(loop); @_read_watcher.attach loop; schedule_write if !@_write_buffer.empty?; self; end
|
35
|
-
|
35
|
+
|
36
36
|
# Detach from the event loop
|
37
37
|
def detach; @_read_watcher.detach; self; end # TODO should these detect write buffers, as well?
|
38
|
-
|
38
|
+
|
39
39
|
# Enable the watcher
|
40
40
|
def enable; @_read_watcher.enable; self; end
|
41
|
-
|
41
|
+
|
42
42
|
# Disable the watcher
|
43
43
|
def disable; @_read_watcher.disable; self; end
|
44
|
-
|
44
|
+
|
45
45
|
# Is the watcher attached?
|
46
46
|
def attached?; @_read_watcher.attached?; end
|
47
|
-
|
47
|
+
|
48
48
|
# Is the watcher enabled?
|
49
49
|
def enabled?; @_read_watcher.enabled?; end
|
50
|
-
|
50
|
+
|
51
51
|
# Obtain the event loop associated with this object
|
52
52
|
def evloop; @_read_watcher.evloop; end
|
53
|
-
|
53
|
+
|
54
54
|
#
|
55
55
|
# Callbacks for asynchronous events
|
56
56
|
#
|
57
|
-
|
57
|
+
|
58
58
|
# Called whenever the IO object receives data
|
59
59
|
def on_read(data); end
|
60
60
|
event_callback :on_read
|
@@ -114,7 +114,7 @@ module Coolio
|
|
114
114
|
close
|
115
115
|
end
|
116
116
|
end
|
117
|
-
|
117
|
+
|
118
118
|
# Write the contents of the output buffer
|
119
119
|
def on_writable
|
120
120
|
begin
|
@@ -126,23 +126,23 @@ module Coolio
|
|
126
126
|
rescue SystemCallError, IOError, SocketError
|
127
127
|
return close
|
128
128
|
end
|
129
|
-
|
129
|
+
|
130
130
|
if @_write_buffer.empty?
|
131
131
|
disable_write_watcher
|
132
132
|
on_write_complete
|
133
133
|
end
|
134
134
|
end
|
135
135
|
|
136
|
-
# Schedule a write to be performed when the IO object becomes writable
|
136
|
+
# Schedule a write to be performed when the IO object becomes writable
|
137
137
|
def schedule_write
|
138
138
|
return unless @_io # this would mean 'we are still pre DNS here'
|
139
139
|
return unless attached? # this would mean 'currently unattached' -- ie still pre DNS, or just plain not attached, which is ok
|
140
140
|
begin
|
141
|
-
enable_write_watcher
|
141
|
+
enable_write_watcher
|
142
142
|
rescue IOError
|
143
143
|
end
|
144
144
|
end
|
145
|
-
|
145
|
+
|
146
146
|
def enable_write_watcher
|
147
147
|
if @_write_watcher.attached?
|
148
148
|
@_write_watcher.enable unless @_write_watcher.enabled?
|
@@ -150,15 +150,15 @@ module Coolio
|
|
150
150
|
@_write_watcher.attach(evloop)
|
151
151
|
end
|
152
152
|
end
|
153
|
-
|
153
|
+
|
154
154
|
def disable_write_watcher
|
155
155
|
@_write_watcher.disable if @_write_watcher and @_write_watcher.enabled?
|
156
156
|
end
|
157
|
-
|
157
|
+
|
158
158
|
def detach_write_watcher
|
159
159
|
@_write_watcher.detach if @_write_watcher and @_write_watcher.attached?
|
160
160
|
end
|
161
|
-
|
161
|
+
|
162
162
|
# Internal class implementing watchers used by Coolio::IO
|
163
163
|
class Watcher < IOWatcher
|
164
164
|
def initialize(ruby_io, coolio_io, flags)
|
data/lib/cool.io/iowatcher.rb
CHANGED
@@ -5,7 +5,7 @@
|
|
5
5
|
#++
|
6
6
|
|
7
7
|
module Coolio
|
8
|
-
class IOWatcher
|
8
|
+
class IOWatcher
|
9
9
|
# The actual implementation of this class resides in the C extension
|
10
10
|
# Here we metaprogram proper event_callbacks for the callback methods
|
11
11
|
# These can take a block and store it to be called when the event
|
data/lib/cool.io/listener.rb
CHANGED
@@ -55,7 +55,7 @@ module Coolio
|
|
55
55
|
|
56
56
|
class TCPListener < Listener
|
57
57
|
DEFAULT_BACKLOG = 1024
|
58
|
-
|
58
|
+
|
59
59
|
# Create a new Coolio::TCPListener on the specified address and port.
|
60
60
|
# Accepts the following options:
|
61
61
|
#
|
@@ -68,7 +68,7 @@ module Coolio
|
|
68
68
|
def initialize(addr, port = nil, options = {})
|
69
69
|
BasicSocket.do_not_reverse_lookup = true unless options[:reverse_lookup]
|
70
70
|
options[:backlog] ||= DEFAULT_BACKLOG
|
71
|
-
|
71
|
+
|
72
72
|
listen_socket = if ::TCPServer === addr
|
73
73
|
addr
|
74
74
|
else
|
data/lib/cool.io/loop.rb
CHANGED
@@ -35,7 +35,7 @@ module Coolio
|
|
35
35
|
# :skip_environment (boolean)
|
36
36
|
# Ignore the $LIBEV_FLAGS environment variable
|
37
37
|
#
|
38
|
-
# :fork_check (boolean)
|
38
|
+
# :fork_check (boolean)
|
39
39
|
# Enable autodetection of forks
|
40
40
|
#
|
41
41
|
# :backend
|
@@ -49,7 +49,7 @@ module Coolio
|
|
49
49
|
def initialize(options = {})
|
50
50
|
@watchers = {}
|
51
51
|
@active_watchers = 0
|
52
|
-
|
52
|
+
|
53
53
|
flags = 0
|
54
54
|
|
55
55
|
options.each do |option, value|
|
@@ -76,7 +76,7 @@ module Coolio
|
|
76
76
|
|
77
77
|
@loop = ev_loop_new(flags)
|
78
78
|
end
|
79
|
-
|
79
|
+
|
80
80
|
# Attach a watcher to the loop
|
81
81
|
def attach(watcher)
|
82
82
|
watcher.attach self
|
@@ -86,7 +86,7 @@ module Coolio
|
|
86
86
|
# are no watchers associated with the event loop it will return
|
87
87
|
# immediately. Otherwise, run will continue blocking and making
|
88
88
|
# event callbacks to watchers until all watchers associated with
|
89
|
-
# the loop have been disabled or detached. The loop may be
|
89
|
+
# the loop have been disabled or detached. The loop may be
|
90
90
|
# explicitly stopped by calling the stop method on the loop object.
|
91
91
|
def run
|
92
92
|
raise RuntimeError, "no watchers for this loop" if @watchers.empty?
|
@@ -103,28 +103,28 @@ module Coolio
|
|
103
103
|
raise RuntimeError, "loop not running" unless @running
|
104
104
|
@running = false
|
105
105
|
end
|
106
|
-
|
106
|
+
|
107
107
|
# Does the loop have any active watchers?
|
108
108
|
def has_active_watchers?
|
109
109
|
@active_watchers > 0
|
110
110
|
end
|
111
|
-
|
111
|
+
|
112
112
|
# All watchers attached to the current loop
|
113
113
|
def watchers
|
114
114
|
@watchers.keys
|
115
115
|
end
|
116
|
-
|
116
|
+
|
117
117
|
#######
|
118
118
|
private
|
119
119
|
#######
|
120
|
-
|
120
|
+
|
121
121
|
EVFLAG_NOENV = 0x1000000 # do NOT consult environment
|
122
122
|
EVFLAG_FORKCHECK = 0x2000000 # check for a fork in each iteration
|
123
123
|
|
124
|
-
EVBACKEND_SELECT = 0x00000001 # supported about anywhere
|
125
|
-
EVBACKEND_POLL = 0x00000002 # !win
|
126
|
-
EVBACKEND_EPOLL = 0x00000004 # linux
|
127
|
-
EVBACKEND_KQUEUE = 0x00000008 # bsd
|
124
|
+
EVBACKEND_SELECT = 0x00000001 # supported about anywhere
|
125
|
+
EVBACKEND_POLL = 0x00000002 # !win
|
126
|
+
EVBACKEND_EPOLL = 0x00000004 # linux
|
127
|
+
EVBACKEND_KQUEUE = 0x00000008 # bsd
|
128
128
|
EVBACKEND_PORT = 0x00000020 # solaris 10
|
129
129
|
end
|
130
130
|
end
|
data/lib/cool.io/meta.rb
CHANGED
@@ -14,7 +14,7 @@ module Coolio
|
|
14
14
|
%w{attach detach enable disable}.each do |method|
|
15
15
|
module_eval <<-EOD
|
16
16
|
def #{method}(*args)
|
17
|
-
if #{proxy_var}
|
17
|
+
if defined? #{proxy_var} and #{proxy_var}
|
18
18
|
#{proxy_var}.#{method}(*args)
|
19
19
|
return self
|
20
20
|
end
|
@@ -37,9 +37,9 @@ module Coolio
|
|
37
37
|
@#{method}_callback = block
|
38
38
|
return
|
39
39
|
end
|
40
|
-
|
41
|
-
if @#{method}_callback
|
42
|
-
instance_exec(*args, &@#{method}_callback)
|
40
|
+
|
41
|
+
if defined? @#{method}_callback and @#{method}_callback
|
42
|
+
instance_exec(*args, &@#{method}_callback)
|
43
43
|
end
|
44
44
|
end
|
45
45
|
EOD
|
data/lib/cool.io/server.rb
CHANGED
@@ -20,9 +20,9 @@ module Coolio
|
|
20
20
|
expected = arity >= 0 ? arity : -(arity + 1)
|
21
21
|
|
22
22
|
if (arity >= 0 and args.size + 1 != expected) or (arity < 0 and args.size + 1 < expected)
|
23
|
-
raise ArgumentError, "wrong number of arguments for #{klass}#initialize (#{args.size+1} for #{expected})"
|
23
|
+
raise ArgumentError, "wrong number of arguments for #{klass}#initialize (#{args.size+1} for #{expected})"
|
24
24
|
end
|
25
|
-
|
25
|
+
|
26
26
|
@klass, @args, @block = klass, args, block
|
27
27
|
super(listen_socket)
|
28
28
|
end
|
@@ -35,7 +35,7 @@ module Coolio
|
|
35
35
|
#########
|
36
36
|
protected
|
37
37
|
#########
|
38
|
-
|
38
|
+
|
39
39
|
def on_connection(socket)
|
40
40
|
connection = @klass.new(socket, *@args).attach(evloop)
|
41
41
|
connection.__send__(:on_connect)
|
data/lib/cool.io/socket.rb
CHANGED
@@ -8,7 +8,7 @@ require 'socket'
|
|
8
8
|
require 'resolv'
|
9
9
|
|
10
10
|
module Coolio
|
11
|
-
class Socket < IO
|
11
|
+
class Socket < IO
|
12
12
|
def self.connect(socket, *args)
|
13
13
|
|
14
14
|
new(socket, *args).instance_eval do
|
@@ -16,42 +16,50 @@ module Coolio
|
|
16
16
|
self
|
17
17
|
end
|
18
18
|
end
|
19
|
-
|
19
|
+
|
20
|
+
# Just initializes some instance variables to avoid
|
21
|
+
# warnings and calls super().
|
22
|
+
def initialize *args
|
23
|
+
@_failed = nil
|
24
|
+
@_connector = nil
|
25
|
+
super
|
26
|
+
end
|
27
|
+
|
20
28
|
watcher_delegate :@_connector
|
21
29
|
|
22
30
|
def attach(evloop)
|
23
31
|
raise RuntimeError, "connection failed" if @_failed
|
24
|
-
|
32
|
+
|
25
33
|
if @_connector
|
26
34
|
@_connector.attach(evloop)
|
27
35
|
return self
|
28
36
|
end
|
29
|
-
|
37
|
+
|
30
38
|
super
|
31
39
|
end
|
32
40
|
|
33
41
|
# Called upon completion of a socket connection
|
34
42
|
def on_connect; end
|
35
43
|
event_callback :on_connect
|
36
|
-
|
44
|
+
|
37
45
|
# Called if a socket connection failed to complete
|
38
46
|
def on_connect_failed; end
|
39
47
|
event_callback :on_connect_failed
|
40
|
-
|
48
|
+
|
41
49
|
# Called if a hostname failed to resolve when connecting
|
42
50
|
# Defaults to calling on_connect_failed
|
43
51
|
alias_method :on_resolve_failed, :on_connect_failed
|
44
|
-
|
52
|
+
|
45
53
|
#########
|
46
54
|
protected
|
47
55
|
#########
|
48
|
-
|
56
|
+
|
49
57
|
class Connector < IOWatcher
|
50
58
|
def initialize(coolio_socket, ruby_socket)
|
51
59
|
@coolio_socket, @ruby_socket = coolio_socket, ruby_socket
|
52
60
|
super(ruby_socket, :w)
|
53
61
|
end
|
54
|
-
|
62
|
+
|
55
63
|
def on_writable
|
56
64
|
evl = evloop
|
57
65
|
detach
|
@@ -67,7 +75,7 @@ module Coolio
|
|
67
75
|
@coolio_socket.instance_eval { @_failed = true }
|
68
76
|
@coolio_socket.__send__(:on_connect_failed)
|
69
77
|
end
|
70
|
-
end
|
78
|
+
end
|
71
79
|
|
72
80
|
#######
|
73
81
|
private
|
@@ -80,11 +88,11 @@ module Coolio
|
|
80
88
|
end
|
81
89
|
end
|
82
90
|
end
|
83
|
-
|
91
|
+
|
84
92
|
class TCPSocket < Socket
|
85
93
|
attr_reader :remote_host, :remote_addr, :remote_port, :address_family
|
86
94
|
watcher_delegate :@_resolver
|
87
|
-
|
95
|
+
|
88
96
|
# Similar to .new, but used in cases where the resulting object is in a
|
89
97
|
# "half-open" state. This is primarily used for when asynchronous
|
90
98
|
# DNS resolution is taking place. We don't actually have a handle to
|
@@ -95,7 +103,7 @@ module Coolio
|
|
95
103
|
obj.__send__(:preinitialize, *args, &block)
|
96
104
|
obj
|
97
105
|
end
|
98
|
-
|
106
|
+
|
99
107
|
# Perform a non-blocking connect to the given host and port
|
100
108
|
# see examples/echo_client.rb
|
101
109
|
# addr is a string, can be an IP address or a hostname.
|
@@ -107,7 +115,7 @@ module Coolio
|
|
107
115
|
elsif(Resolv::IPv6.create(addr) rescue nil)
|
108
116
|
family = ::Socket::AF_INET6
|
109
117
|
end
|
110
|
-
|
118
|
+
|
111
119
|
if family
|
112
120
|
return super(TCPConnectSocket.new(family, addr, port), *args) # this creates a 'real' write buffer so we're ok there with regards to already having a write buffer from the get go
|
113
121
|
end
|
@@ -118,37 +126,37 @@ module Coolio
|
|
118
126
|
|
119
127
|
precreate(addr, port, *args)
|
120
128
|
end
|
121
|
-
|
129
|
+
|
122
130
|
# Called by precreate during asyncronous DNS resolution
|
123
131
|
def preinitialize(addr, port, *args)
|
124
132
|
@_write_buffer = ::IO::Buffer.new # allow for writing BEFORE DNS has resolved
|
125
133
|
@remote_host, @remote_addr, @remote_port = addr, addr, port
|
126
134
|
@_resolver = TCPConnectResolver.new(self, addr, port, *args)
|
127
135
|
end
|
128
|
-
|
136
|
+
|
129
137
|
private :preinitialize
|
130
|
-
|
138
|
+
|
131
139
|
def initialize(socket)
|
132
140
|
unless socket.is_a?(::TCPSocket) or socket.is_a?(TCPConnectSocket)
|
133
141
|
raise TypeError, "socket must be a TCPSocket"
|
134
142
|
end
|
135
|
-
|
143
|
+
|
136
144
|
super
|
137
|
-
|
145
|
+
|
138
146
|
@address_family, @remote_port, @remote_host, @remote_addr = socket.peeraddr
|
139
147
|
end
|
140
|
-
|
148
|
+
|
141
149
|
def peeraddr
|
142
150
|
[@address_family, @remote_port, @remote_host, @remote_addr]
|
143
151
|
end
|
144
|
-
|
152
|
+
|
145
153
|
#########
|
146
154
|
protected
|
147
155
|
#########
|
148
156
|
|
149
157
|
class TCPConnectSocket < ::Socket
|
150
158
|
def initialize(family, addr, port, host = addr)
|
151
|
-
@host,
|
159
|
+
@host, @addr, @port = host, addr, port
|
152
160
|
@address_family = nil
|
153
161
|
|
154
162
|
super(family, ::Socket::SOCK_STREAM, 0)
|
@@ -195,26 +203,26 @@ module Coolio
|
|
195
203
|
|
196
204
|
def on_failure
|
197
205
|
@sock.__send__(:on_resolve_failed)
|
198
|
-
@sock.instance_eval do
|
199
|
-
@_resolver = nil
|
206
|
+
@sock.instance_eval do
|
207
|
+
@_resolver = nil
|
200
208
|
@_failed = true
|
201
209
|
end
|
202
210
|
return
|
203
211
|
end
|
204
212
|
end
|
205
213
|
end
|
206
|
-
|
214
|
+
|
207
215
|
class UNIXSocket < Socket
|
208
216
|
attr_reader :path, :address_family
|
209
|
-
|
217
|
+
|
210
218
|
# Connect to the given UNIX domain socket
|
211
219
|
def self.connect(path, *args)
|
212
220
|
new(::UNIXSocket.new(path), *args)
|
213
221
|
end
|
214
|
-
|
222
|
+
|
215
223
|
def initialize(socket)
|
216
224
|
raise ArgumentError, "socket must be a UNIXSocket" unless socket.is_a? ::UNIXSocket
|
217
|
-
|
225
|
+
|
218
226
|
super
|
219
227
|
@address_family, @path = socket.peeraddr
|
220
228
|
end
|