mt-libuv 4.1.0

Sign up to get free protection for your applications and to get access to all the features.
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