mt-libuv 4.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.
Files changed (70) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +22 -0
  3. data/.gitmodules +6 -0
  4. data/.rspec +1 -0
  5. data/.travis.yml +24 -0
  6. data/Gemfile +9 -0
  7. data/LICENSE +24 -0
  8. data/README.md +195 -0
  9. data/Rakefile +31 -0
  10. data/ext/README.md +6 -0
  11. data/ext/Rakefile +28 -0
  12. data/lib/mt-libuv/async.rb +51 -0
  13. data/lib/mt-libuv/check.rb +59 -0
  14. data/lib/mt-libuv/coroutines.rb +79 -0
  15. data/lib/mt-libuv/dns.rb +98 -0
  16. data/lib/mt-libuv/error.rb +88 -0
  17. data/lib/mt-libuv/ext/ext.rb +322 -0
  18. data/lib/mt-libuv/ext/platform/darwin_x64.rb +61 -0
  19. data/lib/mt-libuv/ext/platform/unix.rb +69 -0
  20. data/lib/mt-libuv/ext/platform/windows.rb +83 -0
  21. data/lib/mt-libuv/ext/tasks/mac.rb +24 -0
  22. data/lib/mt-libuv/ext/tasks/unix.rb +42 -0
  23. data/lib/mt-libuv/ext/tasks/win.rb +29 -0
  24. data/lib/mt-libuv/ext/tasks.rb +27 -0
  25. data/lib/mt-libuv/ext/types.rb +253 -0
  26. data/lib/mt-libuv/fiber_pool.rb +83 -0
  27. data/lib/mt-libuv/file.rb +309 -0
  28. data/lib/mt-libuv/filesystem.rb +263 -0
  29. data/lib/mt-libuv/fs_event.rb +37 -0
  30. data/lib/mt-libuv/handle.rb +108 -0
  31. data/lib/mt-libuv/idle.rb +59 -0
  32. data/lib/mt-libuv/mixins/accessors.rb +41 -0
  33. data/lib/mt-libuv/mixins/assertions.rb +25 -0
  34. data/lib/mt-libuv/mixins/fs_checks.rb +96 -0
  35. data/lib/mt-libuv/mixins/listener.rb +69 -0
  36. data/lib/mt-libuv/mixins/net.rb +42 -0
  37. data/lib/mt-libuv/mixins/resource.rb +30 -0
  38. data/lib/mt-libuv/mixins/stream.rb +276 -0
  39. data/lib/mt-libuv/pipe.rb +217 -0
  40. data/lib/mt-libuv/prepare.rb +59 -0
  41. data/lib/mt-libuv/q.rb +475 -0
  42. data/lib/mt-libuv/reactor.rb +567 -0
  43. data/lib/mt-libuv/signal.rb +62 -0
  44. data/lib/mt-libuv/spawn.rb +113 -0
  45. data/lib/mt-libuv/tcp.rb +465 -0
  46. data/lib/mt-libuv/timer.rb +107 -0
  47. data/lib/mt-libuv/tty.rb +42 -0
  48. data/lib/mt-libuv/udp.rb +302 -0
  49. data/lib/mt-libuv/version.rb +5 -0
  50. data/lib/mt-libuv/work.rb +86 -0
  51. data/lib/mt-libuv.rb +80 -0
  52. data/mt-libuv.gemspec +62 -0
  53. data/spec/async_spec.rb +67 -0
  54. data/spec/coroutines_spec.rb +121 -0
  55. data/spec/cpu_spec.rb +10 -0
  56. data/spec/defer_spec.rb +906 -0
  57. data/spec/dns_spec.rb +110 -0
  58. data/spec/dsl_spec.rb +43 -0
  59. data/spec/filesystem_spec.rb +270 -0
  60. data/spec/idle_spec.rb +44 -0
  61. data/spec/pipe_spec.rb +151 -0
  62. data/spec/spawn_spec.rb +119 -0
  63. data/spec/tcp_spec.rb +272 -0
  64. data/spec/test.sh +4 -0
  65. data/spec/test_fail.sh +3 -0
  66. data/spec/test_read.sh +3 -0
  67. data/spec/timer_spec.rb +14 -0
  68. data/spec/udp_spec.rb +73 -0
  69. data/spec/zen_spec.rb +34 -0
  70. metadata +196 -0
data/spec/dns_spec.rb ADDED
@@ -0,0 +1,110 @@
1
+ require 'mt-libuv'
2
+
3
+
4
+ describe MTLibuv::Dns do
5
+ before :each do
6
+ @log = []
7
+ @general_failure = []
8
+
9
+ @reactor = MTLibuv::Reactor.default
10
+ @reactor.notifier do |error, context|
11
+ begin
12
+ puts "Log called: #{context}\n#{error.message}\n#{error.backtrace.join("\n")}\n"
13
+ rescue Exception
14
+ puts 'error in logger'
15
+ end
16
+ end
17
+ @timeout = @reactor.timer do
18
+ @reactor.stop
19
+ @general_failure << "test timed out"
20
+ end
21
+ @timeout.start(5000)
22
+
23
+ @reactor.all(@server, @client, @timeout).catch do |reason|
24
+ @general_failure << reason.inspect
25
+ puts "Failed with: #{reason.message}\n#{reason.backtrace.join("\n")}\n"
26
+ end
27
+ end
28
+
29
+ after :each do
30
+ @reactor.notifier
31
+ end
32
+
33
+ it "should resolve localhost using IP4", :network => true do
34
+ @reactor.run { |reactor|
35
+ @reactor.lookup('localhost', wait: false).then proc { |addrinfo|
36
+ @result = addrinfo[0][0]
37
+ @timeout.close
38
+ @reactor.stop
39
+ }, proc { |err|
40
+ @general_failure << err
41
+ @timeout.close
42
+ @reactor.stop
43
+ }
44
+ }
45
+
46
+ expect(@general_failure).to eq([])
47
+ expect(@result).to eq('127.0.0.1')
48
+ end
49
+
50
+ it "should resolve localhost using IP6", :network => true do
51
+ @reactor.run { |reactor|
52
+ @reactor.lookup('localhost', :IPv6, wait: false).then proc { |addrinfo|
53
+ @result = addrinfo[0][0]
54
+ @timeout.close
55
+ @reactor.stop
56
+ }, proc { |err|
57
+ @general_failure << err
58
+ @timeout.close
59
+ @reactor.stop
60
+ }
61
+ }
62
+
63
+ expect(@general_failure).to eq([])
64
+ expect(@result).to eq('::1')
65
+ end
66
+
67
+ it "should resolve reactor back" do
68
+ @reactor.run { |reactor|
69
+ @reactor.lookup('127.0.0.1', wait: false).then proc { |addrinfo|
70
+ @result = addrinfo[0][0]
71
+ @timeout.close
72
+ @reactor.stop
73
+ }, proc { |err|
74
+ @general_failure << err
75
+ @timeout.close
76
+ @reactor.stop
77
+ }
78
+ }
79
+
80
+ expect(@general_failure).to eq([])
81
+ expect(@result).to eq('127.0.0.1')
82
+ end
83
+
84
+ it "should work with coroutines" do
85
+ @reactor.run { |reactor|
86
+ begin
87
+ addrinfo = @reactor.lookup('127.0.0.1')
88
+ @result = [addrinfo[0][0]]
89
+
90
+ begin
91
+ addrinfo = @reactor.lookup('test.fail.blah').results
92
+ @general_failure << "should have failed"
93
+ @timeout.close
94
+ @reactor.stop
95
+ rescue => err
96
+ @result << err.class
97
+ @timeout.close
98
+ @reactor.stop
99
+ end
100
+ rescue => err
101
+ @general_failure << err
102
+ @timeout.close
103
+ @reactor.stop
104
+ end
105
+ }
106
+
107
+ expect(@general_failure).to eq([])
108
+ expect(@result).to eq(['127.0.0.1', MTLibuv::Error::EAI_NONAME])
109
+ end
110
+ end
data/spec/dsl_spec.rb ADDED
@@ -0,0 +1,43 @@
1
+ require 'mt-libuv'
2
+
3
+ describe MTLibuv::Accessors do
4
+ describe 'basic usage' do
5
+ it 'should work seamlessly with the default thread' do
6
+ count = 0
7
+ reactor do |reactor|
8
+ reactor.timer {
9
+ count += 1
10
+ reactor.stop if count == 3
11
+ }.start(50, 10)
12
+ end
13
+
14
+ expect(count).to eq(3)
15
+ end
16
+
17
+ it 'work simply with new threads' do
18
+ count = 0
19
+ sig = ConditionVariable.new
20
+ mutex = Mutex.new
21
+ mutex.synchronize {
22
+
23
+ # This will run on a new thread
24
+ MTLibuv::Reactor.new do |reactor|
25
+ reactor.timer {
26
+ count += 1
27
+
28
+ if count == 3
29
+ reactor.stop
30
+ mutex.synchronize {
31
+ sig.signal
32
+ }
33
+ end
34
+ }.start(50, 10)
35
+ end
36
+
37
+ sig.wait(mutex)
38
+ }
39
+
40
+ expect(count).to eq(3)
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,270 @@
1
+ require 'mt-libuv'
2
+ require 'thread'
3
+
4
+
5
+ describe MTLibuv::Filesystem do
6
+ before :each do
7
+ @log = []
8
+ @general_failure = []
9
+
10
+ @reactor = MTLibuv::Reactor.default
11
+ @filesystem = @reactor.filesystem
12
+ @timeout = @reactor.timer do
13
+ @reactor.stop
14
+ @general_failure << "test timed out"
15
+ end
16
+ @timeout.start(4000)
17
+
18
+ @reactor.notifier do |error, context|
19
+ begin
20
+ @general_failure << "Log called: #{context}\n#{error.message}\n#{error.backtrace.join("\n") if error.backtrace}\n"
21
+ rescue Exception => e
22
+ @general_failure << "error in logger #{e.inspect}"
23
+ end
24
+ end
25
+
26
+ @thefile = "test-file.txt"
27
+
28
+ @reactor.all(@filesystem, @timeout).catch do |reason|
29
+ @general_failure << reason.inspect
30
+ end
31
+ end
32
+
33
+ describe 'directory navigation' do
34
+ it "should list the contents of a folder" do
35
+ @reactor.run { |reactor|
36
+ currentDir = Dir.pwd
37
+ listing = @filesystem.readdir(currentDir, wait: false)
38
+ listing.then do |result|
39
+ @log = result
40
+ end
41
+ listing.catch do |error|
42
+ @general_failure << error
43
+ end
44
+ listing.finally do
45
+ @timeout.close
46
+ @reactor.stop
47
+ end
48
+ }
49
+
50
+ expect(@general_failure).to eq([])
51
+ expect((@log.length > 0)).to eq(true)
52
+ end
53
+ end
54
+
55
+ describe 'file manipulation' do
56
+ it "should create and write to a file" do
57
+ @reactor.run { |reactor|
58
+ file = @reactor.file(@thefile, File::CREAT|File::WRONLY)
59
+ begin
60
+ file.write('write some data to a file')
61
+ file.chmod(777)
62
+ @timeout.close
63
+ @reactor.stop
64
+ @log = :success
65
+ rescue => error
66
+ @general_failure << error
67
+ @timeout.close
68
+ @reactor.stop
69
+ ensure
70
+ file.close
71
+ end
72
+ }
73
+
74
+ expect(@general_failure).to eq([])
75
+ expect(@log).to eq(:success)
76
+ end
77
+
78
+ it "should return stats on the file" do
79
+ @reactor.run { |reactor|
80
+ file = @reactor.file(@thefile, File::RDONLY)
81
+ begin
82
+ stats = file.stat
83
+ @timeout.close
84
+ @reactor.stop
85
+ @log << stats[:st_mtim][:tv_sec]
86
+ rescue => error
87
+ @general_failure << error
88
+ @timeout.close
89
+ @reactor.stop
90
+ ensure
91
+ file.close
92
+ end
93
+ }
94
+
95
+ expect(@general_failure).to eq([])
96
+ expect(@log[0]).to be_kind_of(Integer)
97
+ expect(@log.length).to eql(1)
98
+ end
99
+
100
+ it "should read from a file" do
101
+ @reactor.run { |reactor|
102
+ file = @reactor.file(@thefile, File::RDONLY)
103
+ begin
104
+ result = file.read(100)
105
+ @timeout.close
106
+ @reactor.stop
107
+ @log = result
108
+ rescue => error
109
+ @general_failure << error
110
+ @timeout.close
111
+ file.close
112
+ @reactor.stop
113
+ ensure
114
+ file.close
115
+ end
116
+ }
117
+
118
+ expect(@general_failure).to eq([])
119
+ expect(@log).to eq('write some data to a file')
120
+ end
121
+
122
+ it "should delete a file" do
123
+ @reactor.run { |reactor|
124
+ op = @reactor.filesystem.unlink(@thefile, wait: false)
125
+ op.then do
126
+ @timeout.close
127
+ @reactor.stop
128
+ @log = :success
129
+ end
130
+ op.catch do |error|
131
+ @general_failure << error
132
+ @timeout.close
133
+ @reactor.stop
134
+ end
135
+ }
136
+
137
+ expect(@general_failure).to eq([])
138
+ expect(@log).to eq(:success)
139
+ end
140
+ end
141
+
142
+ describe 'file streaming' do
143
+ it "should send a file over a stream", :network => true do
144
+ @reactor.run { |reactor|
145
+ @server = @reactor.tcp
146
+ @client = @reactor.tcp
147
+
148
+ @server.bind('127.0.0.1', 34570) do |client|
149
+ client.progress do |data|
150
+ file = @reactor.file('.rspec', File::RDONLY) do
151
+ file.send_file(client, wait: false).then(proc {
152
+ file.close
153
+ client.close
154
+ }, proc { |error|
155
+ @general_failure << error
156
+ })
157
+ end
158
+ file.catch do |error|
159
+ @general_failure << error.inspect
160
+ file.close
161
+ client.close
162
+ end
163
+ end
164
+ client.start_read
165
+ client.finally do
166
+ @timeout.close
167
+ @server.close
168
+ @reactor.stop
169
+ end
170
+ end
171
+ # catch errors
172
+ @server.catch do |reason|
173
+ @general_failure << reason.inspect
174
+ @reactor.stop
175
+ end
176
+ # start listening
177
+ @server.listen(5)
178
+
179
+
180
+ # connect client to server
181
+ @client.connect('127.0.0.1', 34570) do |client|
182
+ client.progress do |data|
183
+ @log << data
184
+ end
185
+
186
+ @client.start_read
187
+ @client.write('send file')
188
+ end
189
+ # catch errors
190
+ @client.catch do |reason|
191
+ @general_failure << reason.inspect
192
+ @server.close
193
+ @reactor.stop
194
+ end
195
+ }
196
+
197
+ expect(@general_failure).to eq([])
198
+ # Windows GIT adds the carriage return
199
+ if FFI::Platform.windows?
200
+ expect(@log).to eq(["--format progress\r\n"])
201
+ else
202
+ expect(@log).to eq(["--format progress\n"])
203
+ end
204
+ end
205
+
206
+ it "should send a file as a HTTP chunked response", :network => true do
207
+ @reactor.run { |reactor|
208
+ @server = @reactor.tcp
209
+ @client = @reactor.tcp
210
+
211
+ @server.bind('127.0.0.1', 34568) do |client|
212
+ client.progress do |data|
213
+ file = @reactor.file('.rspec', File::RDONLY) do
214
+ file.send_file(client, using: :http, wait: false).then(proc {
215
+ file.close
216
+ client.close
217
+ }, proc { |error|
218
+ @general_failure << error
219
+ })
220
+ end
221
+ file.catch do |error|
222
+ @general_failure << error.inspect
223
+ file.close
224
+ client.close
225
+ end
226
+ end
227
+ client.start_read
228
+ client.finally do
229
+ @timeout.close
230
+ @server.close
231
+ @reactor.stop
232
+ end
233
+ end
234
+ # catch errors
235
+ @server.catch do |reason|
236
+ @general_failure << reason.inspect
237
+ @reactor.stop
238
+ end
239
+ # start listening
240
+ @server.listen(5)
241
+
242
+
243
+ # connect client to server
244
+ @client.connect('127.0.0.1', 34568) do |client|
245
+ client.progress do |data|
246
+ @log << data
247
+ end
248
+
249
+ @client.start_read
250
+ @client.write('send file')
251
+ end
252
+ # catch errors
253
+ @client.catch do |reason|
254
+ @general_failure << reason.inspect
255
+ @server.close
256
+ @reactor.stop
257
+ end
258
+ }
259
+
260
+ expect(@general_failure).to eq([])
261
+
262
+ # Windows GIT adds the carriage return
263
+ if FFI::Platform.windows?
264
+ expect(@log.join('')).to eq("13\r\n--format progress\r\n\r\n0\r\n\r\n")
265
+ else
266
+ expect(@log.join('')).to eq("12\r\n--format progress\n\r\n0\r\n\r\n")
267
+ end
268
+ end
269
+ end
270
+ end
data/spec/idle_spec.rb ADDED
@@ -0,0 +1,44 @@
1
+ require 'mt-libuv'
2
+
3
+
4
+ describe MTLibuv::Idle do
5
+ before :each do
6
+ @log = []
7
+ @general_failure = []
8
+
9
+ @reactor = MTLibuv::Reactor.default
10
+ @reactor.notifier do |error, context|
11
+ begin
12
+ @general_failure << "Log called: #{context}\n#{error.message}\n#{error.backtrace.join("\n") if error.backtrace}\n"
13
+ rescue Exception => e
14
+ @general_failure << "error in logger #{e.inspect}"
15
+ end
16
+ end
17
+ @timeout = @reactor.timer {
18
+ @reactor.stop
19
+ @general_failure << "test timed out"
20
+ }.start(5000)
21
+ end
22
+
23
+ it "should increase the idle count when there is nothing to process" do
24
+ @reactor.run { |reactor|
25
+ @idle_calls = 0
26
+
27
+ idle = @reactor.idle { |e|
28
+ @idle_calls += 1
29
+ }.start
30
+
31
+ stopper = @reactor.timer {
32
+ idle.stop.close
33
+ stopper.close
34
+ @timeout.close
35
+ @reactor.stop
36
+ }.start(1000)
37
+
38
+ expect(@reactor.active_handles).to be >= 4
39
+ }
40
+
41
+ expect(@general_failure).to eq([])
42
+ expect((@idle_calls > 0)).to eq(true)
43
+ end
44
+ end
data/spec/pipe_spec.rb ADDED
@@ -0,0 +1,151 @@
1
+ require 'mt-libuv'
2
+
3
+
4
+ describe MTLibuv::Pipe do
5
+ before :each do
6
+ @log = []
7
+ @general_failure = []
8
+
9
+ @reactor = MTLibuv::Reactor.new
10
+ @server = @reactor.pipe
11
+ @client = @reactor.pipe
12
+ @timeout = @reactor.timer do
13
+ @reactor.stop
14
+ @general_failure << "test timed out"
15
+ end
16
+ @timeout.start(5000)
17
+
18
+ @pipefile = "/tmp/test-pipes.pipe"
19
+
20
+ @reactor.all(@server, @client, @timeout).catch do |reason|
21
+ @general_failure << reason.inspect
22
+ @general_failure << "Failed with: #{reason.message}\n#{reason.backtrace}\n"
23
+ end
24
+
25
+ @reactor.notifier do |error, context|
26
+ begin
27
+ @general_failure << "Log called: #{context}\n#{error.message}\n#{error.backtrace.join("\n") if error.backtrace}\n"
28
+ rescue Exception => e
29
+ @general_failure << "error in logger #{e.inspect}"
30
+ end
31
+ end
32
+
33
+ begin
34
+ File.unlink(@pipefile)
35
+ rescue
36
+ end
37
+ end
38
+
39
+ after :each do
40
+ begin
41
+ File.unlink(@pipefile)
42
+ rescue
43
+ end
44
+ end
45
+
46
+ describe 'bidirectional inter process communication' do
47
+
48
+ it "should send a ping and return a pong" do
49
+ @reactor.run { |reactor|
50
+ @server.bind(@pipefile) do |client|
51
+ client.progress do |data|
52
+ @log << data
53
+ client.write('pong')
54
+ end
55
+ client.start_read
56
+ end
57
+
58
+ # catch server errors
59
+ @server.catch do |reason|
60
+ @general_failure << reason.inspect
61
+ @reactor.stop
62
+
63
+ @general_failure << "Failed with: #{reason.message}\n#{reason.backtrace}\n"
64
+ end
65
+
66
+ # start listening
67
+ @server.listen(1024)
68
+
69
+
70
+
71
+ # connect client to server
72
+ @client.connect(@pipefile) do |client|
73
+ @client.progress do |data|
74
+ @log << data
75
+
76
+ @client.close
77
+ end
78
+
79
+ @client.start_read
80
+ @client.write('ping')
81
+ end
82
+
83
+ @client.catch do |reason|
84
+ @general_failure << reason.inspect
85
+ @reactor.stop
86
+
87
+ @general_failure << "Failed with: #{reason.message}\n#{reason.backtrace}\n"
88
+ end
89
+
90
+ # Stop the reactor once the client handle is closed
91
+ @client.finally do
92
+ @server.close
93
+ @reactor.stop
94
+ end
95
+ }
96
+
97
+ expect(@general_failure).to eq([])
98
+ expect(@log).to eq(['ping', 'pong'])
99
+ end
100
+ end
101
+
102
+ # This test won't pass on windows as pipes don't work like this on windows
103
+ describe 'unidirectional pipeline', :unix_only => true do
104
+ before :each do
105
+ system "/usr/bin/mkfifo", @pipefile
106
+ end
107
+
108
+ it "should send work to a consumer" do
109
+ @reactor.run { |reactor|
110
+ heartbeat = @reactor.timer
111
+ @file1 = @reactor.file(@pipefile, File::RDWR|File::NONBLOCK) do
112
+ @server.open(@file1.fileno) do |server|
113
+ heartbeat.progress do
114
+ @server.write('workload').catch do |err|
115
+ @general_failure << err
116
+ end
117
+ end
118
+ heartbeat.start(0, 200)
119
+ end
120
+ end
121
+ @file1.catch do |e|
122
+ @general_failure << "Log called: #{e.inspect} - #{e.message}\n"
123
+ end
124
+
125
+ @file2 = @reactor.file(@pipefile, File::RDWR|File::NONBLOCK) do
126
+ # connect client to server
127
+ @client.open(@file2.fileno) do |consumer|
128
+ consumer.progress do |data|
129
+ @log = data
130
+ end
131
+
132
+ consumer.start_read
133
+ end
134
+ end
135
+
136
+
137
+ timeout = @reactor.timer do
138
+ @server.close
139
+ @client.close
140
+ timeout.close
141
+ heartbeat.close
142
+ @reactor.stop
143
+ end
144
+ timeout.start(1000)
145
+ }
146
+
147
+ expect(@general_failure).to eq([])
148
+ expect(@log).to eq('workload')
149
+ end
150
+ end
151
+ end