libuv 0.11.3 → 0.11.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +14 -6
- data/.gitignore +17 -17
- data/.gitmodules +3 -3
- data/.rspec +1 -1
- data/.travis.yml +16 -16
- data/Gemfile +4 -4
- data/LICENSE +23 -23
- data/README.md +82 -82
- data/Rakefile +31 -31
- data/lib/libuv.rb +54 -54
- data/lib/libuv/async.rb +47 -47
- data/lib/libuv/check.rb +55 -55
- data/lib/libuv/dns.rb +85 -85
- data/lib/libuv/error.rb +73 -70
- data/lib/libuv/ext/ext.rb +258 -258
- data/lib/libuv/ext/platform/darwin_x64.rb +23 -23
- data/lib/libuv/ext/platform/linux.rb +7 -7
- data/lib/libuv/ext/platform/unix.rb +29 -29
- data/lib/libuv/ext/platform/windows.rb +40 -40
- data/lib/libuv/ext/tasks.rb +29 -29
- data/lib/libuv/ext/tasks/mac.rb +23 -23
- data/lib/libuv/ext/tasks/unix.rb +23 -23
- data/lib/libuv/ext/tasks/win.rb +11 -11
- data/lib/libuv/ext/types.rb +238 -238
- data/lib/libuv/file.rb +191 -191
- data/lib/libuv/filesystem.rb +232 -232
- data/lib/libuv/fs_event.rb +31 -31
- data/lib/libuv/handle.rb +85 -85
- data/lib/libuv/idle.rb +56 -56
- data/lib/libuv/loop.rb +387 -385
- data/lib/libuv/mixins/assertions.rb +23 -23
- data/lib/libuv/mixins/fs_checks.rb +55 -55
- data/lib/libuv/mixins/listener.rb +34 -34
- data/lib/libuv/mixins/net.rb +40 -40
- data/lib/libuv/mixins/resource.rb +27 -27
- data/lib/libuv/mixins/stream.rb +154 -154
- data/lib/libuv/pipe.rb +197 -197
- data/lib/libuv/prepare.rb +56 -56
- data/lib/libuv/signal.rb +51 -51
- data/lib/libuv/tcp.rb +317 -315
- data/lib/libuv/timer.rb +91 -91
- data/lib/libuv/tty.rb +37 -37
- data/lib/libuv/udp.rb +224 -224
- data/lib/libuv/version.rb +3 -3
- data/lib/libuv/work.rb +75 -75
- data/libuv.gemspec +56 -56
- data/spec/async_spec.rb +60 -60
- data/spec/cpu_spec.rb +10 -10
- data/spec/defer_spec.rb +980 -980
- data/spec/dns_spec.rb +90 -90
- data/spec/filesystem_spec.rb +124 -124
- data/spec/idle_spec.rb +56 -56
- data/spec/pipe_spec.rb +160 -160
- data/spec/tcp_spec.rb +267 -267
- metadata +24 -30
data/lib/libuv/fs_event.rb
CHANGED
@@ -1,31 +1,31 @@
|
|
1
|
-
module Libuv
|
2
|
-
class FSEvent < Handle
|
3
|
-
|
4
|
-
|
5
|
-
EVENTS = {1 => :rename, 2 => :change}.freeze
|
6
|
-
|
7
|
-
|
8
|
-
def initialize(loop, path)
|
9
|
-
@loop = loop
|
10
|
-
|
11
|
-
fs_event_ptr = ::Libuv::Ext.create_handle(:uv_fs_event)
|
12
|
-
error = check_result ::Libuv::Ext.fs_event_init(loop.handle, fs_event_ptr, path, callback(:on_fs_event), 0)
|
13
|
-
|
14
|
-
super(fs_event_ptr, error)
|
15
|
-
end
|
16
|
-
|
17
|
-
|
18
|
-
private
|
19
|
-
|
20
|
-
|
21
|
-
def on_fs_event(handle, filename, events, status)
|
22
|
-
e = check_result(status)
|
23
|
-
|
24
|
-
if e
|
25
|
-
reject(e)
|
26
|
-
else
|
27
|
-
defer.notify(filename, EVENTS[events]) # notify of a change
|
28
|
-
end
|
29
|
-
end
|
30
|
-
end
|
31
|
-
end
|
1
|
+
module Libuv
|
2
|
+
class FSEvent < Handle
|
3
|
+
|
4
|
+
|
5
|
+
EVENTS = {1 => :rename, 2 => :change}.freeze
|
6
|
+
|
7
|
+
|
8
|
+
def initialize(loop, path)
|
9
|
+
@loop = loop
|
10
|
+
|
11
|
+
fs_event_ptr = ::Libuv::Ext.create_handle(:uv_fs_event)
|
12
|
+
error = check_result ::Libuv::Ext.fs_event_init(loop.handle, fs_event_ptr, path, callback(:on_fs_event), 0)
|
13
|
+
|
14
|
+
super(fs_event_ptr, error)
|
15
|
+
end
|
16
|
+
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
|
21
|
+
def on_fs_event(handle, filename, events, status)
|
22
|
+
e = check_result(status)
|
23
|
+
|
24
|
+
if e
|
25
|
+
reject(e)
|
26
|
+
else
|
27
|
+
defer.notify(filename, EVENTS[events]) # notify of a change
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
data/lib/libuv/handle.rb
CHANGED
@@ -1,86 +1,86 @@
|
|
1
|
-
module Libuv
|
2
|
-
class Handle < Q::DeferredPromise
|
3
|
-
include Assertions, Resource, Listener
|
4
|
-
|
5
|
-
|
6
|
-
attr_accessor :storage # A place for general storage
|
7
|
-
attr_reader :closed
|
8
|
-
attr_reader :loop
|
9
|
-
|
10
|
-
|
11
|
-
def initialize(pointer, error)
|
12
|
-
@pointer = pointer
|
13
|
-
|
14
|
-
# Initialise the promise
|
15
|
-
super(loop, loop.defer)
|
16
|
-
|
17
|
-
# clean up on init error (always raise here)
|
18
|
-
if error
|
19
|
-
::Libuv::Ext.free(pointer)
|
20
|
-
defer.reject(error)
|
21
|
-
@closed = true
|
22
|
-
raise error
|
23
|
-
end
|
24
|
-
end
|
25
|
-
|
26
|
-
# Public: Increment internal ref counter for the handle on the loop. Useful for
|
27
|
-
# extending the loop with custom watchers that need to make loop not stop
|
28
|
-
#
|
29
|
-
# Returns self
|
30
|
-
def ref
|
31
|
-
return if @closed
|
32
|
-
::Libuv::Ext.ref(handle)
|
33
|
-
end
|
34
|
-
|
35
|
-
# Public: Decrement internal ref counter for the handle on the loop, useful to stop
|
36
|
-
# loop even when there are outstanding open handles
|
37
|
-
#
|
38
|
-
# Returns self
|
39
|
-
def unref
|
40
|
-
return if @closed
|
41
|
-
::Libuv::Ext.unref(handle)
|
42
|
-
end
|
43
|
-
|
44
|
-
def close
|
45
|
-
return if @closed
|
46
|
-
@closed = true
|
47
|
-
Libuv::Ext.close(handle, callback(:on_close))
|
48
|
-
end
|
49
|
-
|
50
|
-
def active?
|
51
|
-
::Libuv::Ext.is_active(handle) > 0
|
52
|
-
end
|
53
|
-
|
54
|
-
def closing?
|
55
|
-
::Libuv::Ext.is_closing(handle) > 0
|
56
|
-
end
|
57
|
-
|
58
|
-
|
59
|
-
protected
|
60
|
-
|
61
|
-
|
62
|
-
def handle; @pointer; end
|
63
|
-
def defer; @defer; end
|
64
|
-
|
65
|
-
|
66
|
-
private
|
67
|
-
|
68
|
-
|
69
|
-
# Clean up and throw an error
|
70
|
-
def reject(reason)
|
71
|
-
@close_error = reason
|
72
|
-
close
|
73
|
-
end
|
74
|
-
|
75
|
-
def on_close(pointer)
|
76
|
-
::Libuv::Ext.free(pointer)
|
77
|
-
clear_callbacks
|
78
|
-
|
79
|
-
if @close_error
|
80
|
-
defer.reject(@close_error)
|
81
|
-
else
|
82
|
-
defer.resolve(nil)
|
83
|
-
end
|
84
|
-
end
|
85
|
-
end
|
1
|
+
module Libuv
|
2
|
+
class Handle < Q::DeferredPromise
|
3
|
+
include Assertions, Resource, Listener
|
4
|
+
|
5
|
+
|
6
|
+
attr_accessor :storage # A place for general storage
|
7
|
+
attr_reader :closed
|
8
|
+
attr_reader :loop
|
9
|
+
|
10
|
+
|
11
|
+
def initialize(pointer, error)
|
12
|
+
@pointer = pointer
|
13
|
+
|
14
|
+
# Initialise the promise
|
15
|
+
super(loop, loop.defer)
|
16
|
+
|
17
|
+
# clean up on init error (always raise here)
|
18
|
+
if error
|
19
|
+
::Libuv::Ext.free(pointer)
|
20
|
+
defer.reject(error)
|
21
|
+
@closed = true
|
22
|
+
raise error
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
# Public: Increment internal ref counter for the handle on the loop. Useful for
|
27
|
+
# extending the loop with custom watchers that need to make loop not stop
|
28
|
+
#
|
29
|
+
# Returns self
|
30
|
+
def ref
|
31
|
+
return if @closed
|
32
|
+
::Libuv::Ext.ref(handle)
|
33
|
+
end
|
34
|
+
|
35
|
+
# Public: Decrement internal ref counter for the handle on the loop, useful to stop
|
36
|
+
# loop even when there are outstanding open handles
|
37
|
+
#
|
38
|
+
# Returns self
|
39
|
+
def unref
|
40
|
+
return if @closed
|
41
|
+
::Libuv::Ext.unref(handle)
|
42
|
+
end
|
43
|
+
|
44
|
+
def close
|
45
|
+
return if @closed
|
46
|
+
@closed = true
|
47
|
+
Libuv::Ext.close(handle, callback(:on_close))
|
48
|
+
end
|
49
|
+
|
50
|
+
def active?
|
51
|
+
::Libuv::Ext.is_active(handle) > 0
|
52
|
+
end
|
53
|
+
|
54
|
+
def closing?
|
55
|
+
::Libuv::Ext.is_closing(handle) > 0
|
56
|
+
end
|
57
|
+
|
58
|
+
|
59
|
+
protected
|
60
|
+
|
61
|
+
|
62
|
+
def handle; @pointer; end
|
63
|
+
def defer; @defer; end
|
64
|
+
|
65
|
+
|
66
|
+
private
|
67
|
+
|
68
|
+
|
69
|
+
# Clean up and throw an error
|
70
|
+
def reject(reason)
|
71
|
+
@close_error = reason
|
72
|
+
close
|
73
|
+
end
|
74
|
+
|
75
|
+
def on_close(pointer)
|
76
|
+
::Libuv::Ext.free(pointer)
|
77
|
+
clear_callbacks
|
78
|
+
|
79
|
+
if @close_error
|
80
|
+
defer.reject(@close_error)
|
81
|
+
else
|
82
|
+
defer.resolve(nil)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
86
|
end
|
data/lib/libuv/idle.rb
CHANGED
@@ -1,56 +1,56 @@
|
|
1
|
-
module Libuv
|
2
|
-
class Idle < Handle
|
3
|
-
|
4
|
-
|
5
|
-
# @param loop [::Libuv::Loop] loop this idle handler will be associated
|
6
|
-
# @param callback [Proc] callback to be called when the loop is idle
|
7
|
-
def initialize(loop, callback = nil, &blk)
|
8
|
-
@loop = loop
|
9
|
-
@callback = callback || blk
|
10
|
-
|
11
|
-
idle_ptr = ::Libuv::Ext.create_handle(:uv_idle)
|
12
|
-
error = check_result(::Libuv::Ext.idle_init(loop.handle, idle_ptr))
|
13
|
-
|
14
|
-
super(idle_ptr, error)
|
15
|
-
end
|
16
|
-
|
17
|
-
# Enables the idle handler.
|
18
|
-
def start
|
19
|
-
return if @closed
|
20
|
-
error = check_result ::Libuv::Ext.idle_start(handle, callback(:on_idle))
|
21
|
-
reject(error) if error
|
22
|
-
end
|
23
|
-
|
24
|
-
# Disables the idle handler.
|
25
|
-
def stop
|
26
|
-
return if @closed
|
27
|
-
error = check_result ::Libuv::Ext.idle_stop(handle)
|
28
|
-
reject(error) if error
|
29
|
-
end
|
30
|
-
|
31
|
-
# Used to update the callback that will be triggered on idle
|
32
|
-
#
|
33
|
-
# @param callback [Proc] the callback to be called on idle trigger
|
34
|
-
def progress(callback = nil, &blk)
|
35
|
-
@callback = callback || blk
|
36
|
-
end
|
37
|
-
|
38
|
-
|
39
|
-
private
|
40
|
-
|
41
|
-
|
42
|
-
def on_idle(handle, status)
|
43
|
-
e = check_result(status)
|
44
|
-
|
45
|
-
if e
|
46
|
-
reject(e)
|
47
|
-
else
|
48
|
-
begin
|
49
|
-
@callback.call
|
50
|
-
rescue Exception => e
|
51
|
-
@loop.log :error, :idle_cb, e
|
52
|
-
end
|
53
|
-
end
|
54
|
-
end
|
55
|
-
end
|
56
|
-
end
|
1
|
+
module Libuv
|
2
|
+
class Idle < Handle
|
3
|
+
|
4
|
+
|
5
|
+
# @param loop [::Libuv::Loop] loop this idle handler will be associated
|
6
|
+
# @param callback [Proc] callback to be called when the loop is idle
|
7
|
+
def initialize(loop, callback = nil, &blk)
|
8
|
+
@loop = loop
|
9
|
+
@callback = callback || blk
|
10
|
+
|
11
|
+
idle_ptr = ::Libuv::Ext.create_handle(:uv_idle)
|
12
|
+
error = check_result(::Libuv::Ext.idle_init(loop.handle, idle_ptr))
|
13
|
+
|
14
|
+
super(idle_ptr, error)
|
15
|
+
end
|
16
|
+
|
17
|
+
# Enables the idle handler.
|
18
|
+
def start
|
19
|
+
return if @closed
|
20
|
+
error = check_result ::Libuv::Ext.idle_start(handle, callback(:on_idle))
|
21
|
+
reject(error) if error
|
22
|
+
end
|
23
|
+
|
24
|
+
# Disables the idle handler.
|
25
|
+
def stop
|
26
|
+
return if @closed
|
27
|
+
error = check_result ::Libuv::Ext.idle_stop(handle)
|
28
|
+
reject(error) if error
|
29
|
+
end
|
30
|
+
|
31
|
+
# Used to update the callback that will be triggered on idle
|
32
|
+
#
|
33
|
+
# @param callback [Proc] the callback to be called on idle trigger
|
34
|
+
def progress(callback = nil, &blk)
|
35
|
+
@callback = callback || blk
|
36
|
+
end
|
37
|
+
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
|
42
|
+
def on_idle(handle, status)
|
43
|
+
e = check_result(status)
|
44
|
+
|
45
|
+
if e
|
46
|
+
reject(e)
|
47
|
+
else
|
48
|
+
begin
|
49
|
+
@callback.call
|
50
|
+
rescue Exception => e
|
51
|
+
@loop.log :error, :idle_cb, e
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
data/lib/libuv/loop.rb
CHANGED
@@ -1,385 +1,387 @@
|
|
1
|
-
require 'thread'
|
2
|
-
|
3
|
-
module Libuv
|
4
|
-
class Loop
|
5
|
-
include Resource, Assertions
|
6
|
-
|
7
|
-
|
8
|
-
LOOPS = ThreadSafe::Cache.new
|
9
|
-
|
10
|
-
|
11
|
-
module ClassMethods
|
12
|
-
# Get default loop
|
13
|
-
#
|
14
|
-
# @return [::Libuv::Loop]
|
15
|
-
def default
|
16
|
-
return
|
17
|
-
end
|
18
|
-
|
19
|
-
# Create new Libuv loop
|
20
|
-
#
|
21
|
-
# @return [::Libuv::Loop]
|
22
|
-
def new
|
23
|
-
return
|
24
|
-
end
|
25
|
-
|
26
|
-
# Build a Ruby Libuv loop from an existing loop pointer
|
27
|
-
#
|
28
|
-
# @return [::Libuv::Loop]
|
29
|
-
def create(pointer)
|
30
|
-
allocate.tap { |i| i.send(:initialize, FFI::AutoPointer.new(pointer, ::Libuv::Ext.method(:loop_delete))) }
|
31
|
-
end
|
32
|
-
|
33
|
-
# Checks for the existence of a loop on the current thread
|
34
|
-
#
|
35
|
-
# @return [::Libuv::Loop | nil]
|
36
|
-
def current
|
37
|
-
LOOPS[Thread.current]
|
38
|
-
end
|
39
|
-
end
|
40
|
-
extend ClassMethods
|
41
|
-
|
42
|
-
|
43
|
-
# Initialize a loop using an FFI::Pointer to a libuv loop
|
44
|
-
def initialize(pointer) # :notnew:
|
45
|
-
@pointer = pointer
|
46
|
-
@loop = self
|
47
|
-
|
48
|
-
# Create an async call for scheduling work from other threads
|
49
|
-
@run_queue = Queue.new
|
50
|
-
@queue_proc = proc do
|
51
|
-
# ensure we only execute what was required for this tick
|
52
|
-
length = @run_queue.length
|
53
|
-
length.times do
|
54
|
-
begin
|
55
|
-
run = @run_queue.pop true # pop non-block
|
56
|
-
run.call
|
57
|
-
rescue Exception => e
|
58
|
-
@loop.log :error, :next_tick_cb, e
|
59
|
-
end
|
60
|
-
end
|
61
|
-
end
|
62
|
-
@process_queue = Async.new(@loop, @queue_proc)
|
63
|
-
|
64
|
-
# Create a next tick timer
|
65
|
-
@next_tick = @loop.timer do
|
66
|
-
@next_tick_scheduled = false
|
67
|
-
@queue_proc.call
|
68
|
-
end
|
69
|
-
|
70
|
-
# Create an async call for ending the loop
|
71
|
-
@stop_loop = Async.new @loop do
|
72
|
-
LOOPS.delete(@reactor_thread)
|
73
|
-
@reactor_thread = nil
|
74
|
-
@process_queue.close
|
75
|
-
@stop_loop.close
|
76
|
-
@next_tick.close
|
77
|
-
|
78
|
-
::Libuv::Ext.stop(@pointer)
|
79
|
-
end
|
80
|
-
end
|
81
|
-
|
82
|
-
def handle; @pointer; end
|
83
|
-
|
84
|
-
# Run the actual event loop. This method will block until the loop is stopped.
|
85
|
-
#
|
86
|
-
# @param run_type [:UV_RUN_DEFAULT, :UV_RUN_ONCE, :UV_RUN_NOWAIT]
|
87
|
-
# @yieldparam promise [::Libuv::Q::Promise] Yields a promise that can be used for logging unhandled
|
88
|
-
# exceptions on the loop.
|
89
|
-
def run(run_type = :UV_RUN_DEFAULT)
|
90
|
-
if @reactor_thread.nil?
|
91
|
-
@loop_notify = @loop.defer
|
92
|
-
|
93
|
-
begin
|
94
|
-
@reactor_thread = Thread.current
|
95
|
-
LOOPS[@reactor_thread] = @loop
|
96
|
-
yield @loop_notify.promise if block_given?
|
97
|
-
::Libuv::Ext.run(@pointer, run_type) # This is blocking
|
98
|
-
ensure
|
99
|
-
@reactor_thread = nil
|
100
|
-
@run_queue.clear
|
101
|
-
end
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
#
|
110
|
-
#
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
#
|
118
|
-
#
|
119
|
-
#
|
120
|
-
#
|
121
|
-
#
|
122
|
-
# same
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
#
|
130
|
-
#
|
131
|
-
#
|
132
|
-
#
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
#
|
140
|
-
#
|
141
|
-
#
|
142
|
-
#
|
143
|
-
#
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
#
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
#
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
#
|
166
|
-
#
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
::Libuv::
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
#
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
#
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
#
|
194
|
-
#
|
195
|
-
# @
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
#
|
205
|
-
#
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
#
|
213
|
-
#
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
#
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
#
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
#
|
235
|
-
#
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
#
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
handle
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
#
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
handle.
|
258
|
-
handle
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
#
|
264
|
-
#
|
265
|
-
# @
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
#
|
275
|
-
#
|
276
|
-
# @param
|
277
|
-
# @
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
dns
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
#
|
287
|
-
#
|
288
|
-
# @
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
#
|
297
|
-
#
|
298
|
-
# @param
|
299
|
-
# @
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
assert_type(
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
#
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
#
|
317
|
-
#
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
#
|
333
|
-
#
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
#
|
353
|
-
#
|
354
|
-
# @param
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
#
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
#
|
374
|
-
|
375
|
-
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
|
380
|
-
#
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
end
|
1
|
+
require 'thread'
|
2
|
+
|
3
|
+
module Libuv
|
4
|
+
class Loop
|
5
|
+
include Resource, Assertions
|
6
|
+
|
7
|
+
|
8
|
+
LOOPS = ThreadSafe::Cache.new
|
9
|
+
|
10
|
+
|
11
|
+
module ClassMethods
|
12
|
+
# Get default loop
|
13
|
+
#
|
14
|
+
# @return [::Libuv::Loop]
|
15
|
+
def default
|
16
|
+
return @default ||= create(::Libuv::Ext.default_loop)
|
17
|
+
end
|
18
|
+
|
19
|
+
# Create new Libuv loop
|
20
|
+
#
|
21
|
+
# @return [::Libuv::Loop]
|
22
|
+
def new
|
23
|
+
return create(::Libuv::Ext.loop_new)
|
24
|
+
end
|
25
|
+
|
26
|
+
# Build a Ruby Libuv loop from an existing loop pointer
|
27
|
+
#
|
28
|
+
# @return [::Libuv::Loop]
|
29
|
+
def create(pointer)
|
30
|
+
allocate.tap { |i| i.send(:initialize, FFI::AutoPointer.new(pointer, ::Libuv::Ext.method(:loop_delete))) }
|
31
|
+
end
|
32
|
+
|
33
|
+
# Checks for the existence of a loop on the current thread
|
34
|
+
#
|
35
|
+
# @return [::Libuv::Loop | nil]
|
36
|
+
def current
|
37
|
+
LOOPS[Thread.current]
|
38
|
+
end
|
39
|
+
end
|
40
|
+
extend ClassMethods
|
41
|
+
|
42
|
+
|
43
|
+
# Initialize a loop using an FFI::Pointer to a libuv loop
|
44
|
+
def initialize(pointer) # :notnew:
|
45
|
+
@pointer = pointer
|
46
|
+
@loop = self
|
47
|
+
|
48
|
+
# Create an async call for scheduling work from other threads
|
49
|
+
@run_queue = Queue.new
|
50
|
+
@queue_proc = proc do
|
51
|
+
# ensure we only execute what was required for this tick
|
52
|
+
length = @run_queue.length
|
53
|
+
length.times do
|
54
|
+
begin
|
55
|
+
run = @run_queue.pop true # pop non-block
|
56
|
+
run.call
|
57
|
+
rescue Exception => e
|
58
|
+
@loop.log :error, :next_tick_cb, e
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
@process_queue = Async.new(@loop, @queue_proc)
|
63
|
+
|
64
|
+
# Create a next tick timer
|
65
|
+
@next_tick = @loop.timer do
|
66
|
+
@next_tick_scheduled = false
|
67
|
+
@queue_proc.call
|
68
|
+
end
|
69
|
+
|
70
|
+
# Create an async call for ending the loop
|
71
|
+
@stop_loop = Async.new @loop do
|
72
|
+
LOOPS.delete(@reactor_thread)
|
73
|
+
@reactor_thread = nil
|
74
|
+
@process_queue.close
|
75
|
+
@stop_loop.close
|
76
|
+
@next_tick.close
|
77
|
+
|
78
|
+
::Libuv::Ext.stop(@pointer)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def handle; @pointer; end
|
83
|
+
|
84
|
+
# Run the actual event loop. This method will block until the loop is stopped.
|
85
|
+
#
|
86
|
+
# @param run_type [:UV_RUN_DEFAULT, :UV_RUN_ONCE, :UV_RUN_NOWAIT]
|
87
|
+
# @yieldparam promise [::Libuv::Q::Promise] Yields a promise that can be used for logging unhandled
|
88
|
+
# exceptions on the loop.
|
89
|
+
def run(run_type = :UV_RUN_DEFAULT)
|
90
|
+
if @reactor_thread.nil?
|
91
|
+
@loop_notify = @loop.defer
|
92
|
+
|
93
|
+
begin
|
94
|
+
@reactor_thread = Thread.current
|
95
|
+
LOOPS[@reactor_thread] = @loop
|
96
|
+
yield @loop_notify.promise if block_given?
|
97
|
+
::Libuv::Ext.run(@pointer, run_type) # This is blocking
|
98
|
+
ensure
|
99
|
+
@reactor_thread = nil
|
100
|
+
@run_queue.clear
|
101
|
+
end
|
102
|
+
elsif block_given?
|
103
|
+
schedule { yield @loop_notify.promise }
|
104
|
+
end
|
105
|
+
@loop
|
106
|
+
end
|
107
|
+
|
108
|
+
|
109
|
+
# Creates a deferred result object for where the result of an operation may only be returned
|
110
|
+
# at some point in the future or is being processed on a different thread (thread safe)
|
111
|
+
#
|
112
|
+
# @return [::Libuv::Q::Deferred]
|
113
|
+
def defer
|
114
|
+
Q.defer(@loop)
|
115
|
+
end
|
116
|
+
|
117
|
+
# Combines multiple promises into a single promise that is resolved when all of the input
|
118
|
+
# promises are resolved. (thread safe)
|
119
|
+
#
|
120
|
+
# @param *promises [::Libuv::Q::Promise] a number of promises that will be combined into a single promise
|
121
|
+
# @return [::Libuv::Q::Promise] Returns a single promise that will be resolved with an array of values,
|
122
|
+
# each value corresponding to the promise at the same index in the `promises` array. If any of
|
123
|
+
# the promises is resolved with a rejection, this resulting promise will be resolved with the
|
124
|
+
# same rejection.
|
125
|
+
def all(*promises)
|
126
|
+
Q.all(@loop, *promises)
|
127
|
+
end
|
128
|
+
|
129
|
+
#
|
130
|
+
# Combines multiple promises into a single promise that is resolved when any of the input
|
131
|
+
# promises are resolved.
|
132
|
+
#
|
133
|
+
# @param *promises [::Libuv::Q::Promise] a number of promises that will be combined into a single promise
|
134
|
+
# @return [::Libuv::Q::Promise] Returns a single promise
|
135
|
+
def any(*promises)
|
136
|
+
Q.any(@loop, *promises)
|
137
|
+
end
|
138
|
+
|
139
|
+
#
|
140
|
+
# Combines multiple promises into a single promise that is resolved when all of the input
|
141
|
+
# promises are resolved or rejected.
|
142
|
+
#
|
143
|
+
# @param *promises [::Libuv::Q::Promise] a number of promises that will be combined into a single promise
|
144
|
+
# @return [::Libuv::Q::Promise] Returns a single promise that will be resolved with an array of values,
|
145
|
+
# each [result, wasResolved] value pair corresponding to a at the same index in the `promises` array.
|
146
|
+
def finally(*promises)
|
147
|
+
Q.finally(@loop, *promises)
|
148
|
+
end
|
149
|
+
|
150
|
+
|
151
|
+
# forces loop time update, useful for getting more granular times
|
152
|
+
#
|
153
|
+
# @return nil
|
154
|
+
def update_time
|
155
|
+
::Libuv::Ext.update_time(@pointer)
|
156
|
+
end
|
157
|
+
|
158
|
+
# Get current time in milliseconds
|
159
|
+
#
|
160
|
+
# @return [Fixnum]
|
161
|
+
def now
|
162
|
+
::Libuv::Ext.now(@pointer)
|
163
|
+
end
|
164
|
+
|
165
|
+
# Lookup an error code and return is as an error object
|
166
|
+
#
|
167
|
+
# @param err [Integer] The error code to look up.
|
168
|
+
# @return [::Libuv::Error]
|
169
|
+
def lookup_error(err)
|
170
|
+
name = ::Libuv::Ext.err_name(err)
|
171
|
+
msg = ::Libuv::Ext.strerror(err)
|
172
|
+
|
173
|
+
::Libuv::Error.const_get(name.to_sym).new(msg)
|
174
|
+
rescue Exception => e
|
175
|
+
@loop.log :warn, :error_lookup_failed, e
|
176
|
+
::Libuv::Error::UNKNOWN.new("error lookup failed for code #{err} #{name} #{msg}")
|
177
|
+
end
|
178
|
+
|
179
|
+
# Get a new TCP instance
|
180
|
+
#
|
181
|
+
# @return [::Libuv::TCP]
|
182
|
+
def tcp
|
183
|
+
TCP.new(@loop)
|
184
|
+
end
|
185
|
+
|
186
|
+
# Get a new UDP instance
|
187
|
+
#
|
188
|
+
# @return [::Libuv::UDP]
|
189
|
+
def udp
|
190
|
+
UDP.new(@loop)
|
191
|
+
end
|
192
|
+
|
193
|
+
# Get a new TTY instance
|
194
|
+
#
|
195
|
+
# @param fileno [Integer] Integer file descriptor of a tty device
|
196
|
+
# @param readable [true, false] Boolean indicating if TTY is readable
|
197
|
+
# @return [::Libuv::TTY]
|
198
|
+
def tty(fileno, readable = false)
|
199
|
+
assert_type(Integer, fileno, "io#fileno must return an integer file descriptor, #{fileno.inspect} given")
|
200
|
+
|
201
|
+
TTY.new(@loop, fileno, readable)
|
202
|
+
end
|
203
|
+
|
204
|
+
# Get a new Pipe instance
|
205
|
+
#
|
206
|
+
# @param ipc [true, false] indicate if a handle will be used for ipc, useful for sharing tcp socket between processes
|
207
|
+
# @return [::Libuv::Pipe]
|
208
|
+
def pipe(ipc = false)
|
209
|
+
Pipe.new(@loop, ipc)
|
210
|
+
end
|
211
|
+
|
212
|
+
# Get a new timer instance
|
213
|
+
#
|
214
|
+
# @param callback [Proc] the callback to be called on timer trigger
|
215
|
+
# @return [::Libuv::Timer]
|
216
|
+
def timer(callback = nil, &blk)
|
217
|
+
Timer.new(@loop, callback || blk)
|
218
|
+
end
|
219
|
+
|
220
|
+
# Get a new Prepare handle
|
221
|
+
#
|
222
|
+
# @return [::Libuv::Prepare]
|
223
|
+
def prepare(callback = nil, &blk)
|
224
|
+
Prepare.new(@loop, callback || blk)
|
225
|
+
end
|
226
|
+
|
227
|
+
# Get a new Check handle
|
228
|
+
#
|
229
|
+
# @return [::Libuv::Check]
|
230
|
+
def check(callback = nil, &blk)
|
231
|
+
Check.new(@loop, callback || blk)
|
232
|
+
end
|
233
|
+
|
234
|
+
# Get a new Idle handle
|
235
|
+
#
|
236
|
+
# @param callback [Proc] the callback to be called on idle trigger
|
237
|
+
# @return [::Libuv::Idle]
|
238
|
+
def idle(callback = nil, &block)
|
239
|
+
Idle.new(@loop, callback || block)
|
240
|
+
end
|
241
|
+
|
242
|
+
# Get a new Async handle
|
243
|
+
#
|
244
|
+
# @return [::Libuv::Async]
|
245
|
+
def async(callback = nil, &block)
|
246
|
+
callback ||= block
|
247
|
+
handle = Async.new(@loop)
|
248
|
+
handle.progress callback if callback
|
249
|
+
handle
|
250
|
+
end
|
251
|
+
|
252
|
+
# Get a new signal handler
|
253
|
+
#
|
254
|
+
# @return [::Libuv::Signal]
|
255
|
+
def signal(signum = nil, callback = nil, &block)
|
256
|
+
callback ||= block
|
257
|
+
handle = Signal.new(@loop)
|
258
|
+
handle.progress callback if callback
|
259
|
+
handle.start(signum) if signum
|
260
|
+
handle
|
261
|
+
end
|
262
|
+
|
263
|
+
# Queue some work for processing in the libuv thread pool
|
264
|
+
#
|
265
|
+
# @param callback [Proc] the callback to be called in the thread pool
|
266
|
+
# @return [::Libuv::Work]
|
267
|
+
# @raise [ArgumentError] if block is not given
|
268
|
+
def work(callback = nil, &block)
|
269
|
+
callback ||= block
|
270
|
+
assert_block(callback)
|
271
|
+
Work.new(@loop, callback) # Work is a promise object
|
272
|
+
end
|
273
|
+
|
274
|
+
# Lookup a hostname
|
275
|
+
#
|
276
|
+
# @param hostname [String] the domain name to lookup
|
277
|
+
# @param port [Integer, String] the service being connected too
|
278
|
+
# @param callback [Proc] the callback to be called on success
|
279
|
+
# @return [::Libuv::Dns]
|
280
|
+
def lookup(hostname, hint = :IPv4, port = 9, &block)
|
281
|
+
dns = Dns.new(@loop, hostname, port, hint) # Work is a promise object
|
282
|
+
dns.then block if block_given?
|
283
|
+
dns
|
284
|
+
end
|
285
|
+
|
286
|
+
# Get a new FSEvent instance
|
287
|
+
#
|
288
|
+
# @param path [String] the path to the file or folder for watching
|
289
|
+
# @return [::Libuv::FSEvent]
|
290
|
+
# @raise [ArgumentError] if path is not a string
|
291
|
+
def fs_event(path)
|
292
|
+
assert_type(String, path)
|
293
|
+
FSEvent.new(@loop, path)
|
294
|
+
end
|
295
|
+
|
296
|
+
# Opens a file and returns an object that can be used to manipulate it
|
297
|
+
#
|
298
|
+
# @param path [String] the path to the file or folder for watching
|
299
|
+
# @param flags [Integer] see ruby File::Constants
|
300
|
+
# @param mode [Integer]
|
301
|
+
# @return [::Libuv::File]
|
302
|
+
def file(path, flags = 0, mode = 0)
|
303
|
+
assert_type(String, path, "path must be a String")
|
304
|
+
assert_type(Integer, flags, "flags must be an Integer")
|
305
|
+
assert_type(Integer, mode, "mode must be an Integer")
|
306
|
+
File.new(@loop, path, flags, mode)
|
307
|
+
end
|
308
|
+
|
309
|
+
# Returns an object for manipulating the filesystem
|
310
|
+
#
|
311
|
+
# @return [::Libuv::Filesystem]
|
312
|
+
def filesystem
|
313
|
+
Filesystem.new(@loop)
|
314
|
+
end
|
315
|
+
|
316
|
+
# Schedule some work to be processed on the event loop as soon as possible (thread safe)
|
317
|
+
#
|
318
|
+
# @param callback [Proc] the callback to be called on the reactor thread
|
319
|
+
# @raise [ArgumentError] if block is not given
|
320
|
+
def schedule(callback = nil, &block)
|
321
|
+
callback ||= block
|
322
|
+
assert_block(callback)
|
323
|
+
|
324
|
+
if reactor_thread?
|
325
|
+
block.call
|
326
|
+
else
|
327
|
+
@run_queue << callback
|
328
|
+
@process_queue.call
|
329
|
+
end
|
330
|
+
end
|
331
|
+
|
332
|
+
# Queue some work to be processed in the next iteration of the event loop (thread safe)
|
333
|
+
#
|
334
|
+
# @param callback [Proc] the callback to be called on the reactor thread
|
335
|
+
# @raise [ArgumentError] if block is not given
|
336
|
+
def next_tick(callback = nil, &block)
|
337
|
+
callback ||= block
|
338
|
+
assert_block(callback)
|
339
|
+
|
340
|
+
@run_queue << callback
|
341
|
+
if reactor_thread?
|
342
|
+
# Create a next tick timer
|
343
|
+
if not @next_tick_scheduled
|
344
|
+
@next_tick.start(0)
|
345
|
+
@next_tick_scheduled = true
|
346
|
+
end
|
347
|
+
else
|
348
|
+
@process_queue.call
|
349
|
+
end
|
350
|
+
end
|
351
|
+
|
352
|
+
# Notifies the loop there was an event that should be logged
|
353
|
+
#
|
354
|
+
# @param level [Symbol] the error level (info, warn, error etc)
|
355
|
+
# @param id [Object] some kind of identifying information
|
356
|
+
# @param *args [*args] any additional information
|
357
|
+
def log(level, id, *args)
|
358
|
+
@loop_notify.notify(level, id, *args)
|
359
|
+
end
|
360
|
+
|
361
|
+
# Closes handles opened by the loop class and completes the current loop iteration (thread safe)
|
362
|
+
def stop
|
363
|
+
@stop_loop.call
|
364
|
+
end
|
365
|
+
|
366
|
+
# True if the calling thread is the same thread as the reactor.
|
367
|
+
#
|
368
|
+
# @return [Boolean]
|
369
|
+
def reactor_thread?
|
370
|
+
@reactor_thread == Thread.current
|
371
|
+
end
|
372
|
+
|
373
|
+
# Exposed to allow joining on the thread, when run in a multithreaded environment. Performing other actions on the thread has undefined semantics (read: a dangerous endevor).
|
374
|
+
#
|
375
|
+
# @return [Thread]
|
376
|
+
def reactor_thread
|
377
|
+
@reactor_thread
|
378
|
+
end
|
379
|
+
|
380
|
+
# Tells you whether the Libuv reactor loop is currently running.
|
381
|
+
#
|
382
|
+
# @return [Boolean]
|
383
|
+
def reactor_running?
|
384
|
+
!@reactor_thread.nil?
|
385
|
+
end
|
386
|
+
end
|
387
|
+
end
|