barlume 0.0.1.rc.1 → 0.0.1.rc.2

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.
@@ -14,5 +14,6 @@ Gem::Specification.new {|s|
14
14
  s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
15
15
  s.require_paths = ['lib']
16
16
 
17
- s.add_dependency 'ffi'
17
+ s.add_runtime_dependency 'ffi'
18
+ s.add_runtime_dependency 'backports'
18
19
  }
@@ -0,0 +1,78 @@
1
+ #! /usr/bin/env ruby
2
+ require 'rubygems'
3
+ require 'barlume'
4
+
5
+ class Echo < Barlume::Lucciola
6
+ CHUNK_SIZE = 2048
7
+
8
+ def initialize (*)
9
+ super
10
+
11
+ @buffer = ''
12
+ end
13
+
14
+ def read_all
15
+ while chunk = read(CHUNK_SIZE)
16
+ @buffer << chunk
17
+ end
18
+ rescue Errno::EAGAIN; end
19
+
20
+ def write_all
21
+ while @buffer.length > 0
22
+ if @buffer.length <= CHUNK_SIZE
23
+ written = write @buffer
24
+
25
+ if written < @buffer.length
26
+ @buffer[0, written] = ''
27
+
28
+ return false
29
+ else
30
+ @buffer.clear
31
+ end
32
+ else
33
+ written = write @buffer[0, CHUNK_SIZE]
34
+
35
+ @buffer[0, written] = ''
36
+
37
+ if written < CHUNK_SIZE
38
+ return false
39
+ end
40
+ end
41
+ end
42
+
43
+ true
44
+ rescue Errno::EAGAIN
45
+ false
46
+ end
47
+ end
48
+
49
+ lanterna = Barlume::Lanterna.best
50
+ server = lanterna.add(TCPServer.new(1337)).asynchronous!.readable!
51
+ server.listen(1024)
52
+
53
+ puts "Using #{lanterna.name}..."
54
+
55
+ loop do
56
+ lanterna.available {|event, lucciola|
57
+ if event == :error
58
+ lucciola.delete!.close rescue nil
59
+ elsif event == :readable
60
+ if lucciola == server
61
+ while client = lucciola.accept rescue nil
62
+ lanterna.add(Echo.new(client)).asynchronous!.readable!
63
+ end
64
+ else
65
+ begin
66
+ lucciola.read_all
67
+ lucciola.writable!
68
+ rescue EOFError, Errno::ECONNRESET
69
+ lucciola.delete!
70
+ end
71
+ end
72
+ elsif event == :writable
73
+ if lucciola.write_all
74
+ lucciola.no_writable!
75
+ end
76
+ end
77
+ }
78
+ end
@@ -0,0 +1,75 @@
1
+ #! /usr/bin/env ruby
2
+ # encoding: utf-8
3
+ require 'rubygems'
4
+ require 'barlume'
5
+ require 'optparse'
6
+
7
+ options = {}
8
+
9
+ OptionParser.new do |o|
10
+ options[:host] = 'localhost'
11
+ options[:port] = 4567
12
+ options[:threads] = 16
13
+ options[:content] = "GET / HTTP/1.1\r\nHost: localhost\r\n\r\n"
14
+ options[:sleep] = 1
15
+
16
+ o.on '-h', '--host host', 'the host of the server' do |value|
17
+ options[:host] = value
18
+ end
19
+
20
+ o.on '-p', '--port port', Integer, 'the port of the server' do |value|
21
+ options[:port] = value
22
+ end
23
+
24
+ o.on '-t', '--threads [MAX_THREADS]', 'the max amount of threads on the server' do |value|
25
+ options[:threads] = value
26
+ end
27
+
28
+ o.on '-c', '--content CONTENT', 'the string to send' do |value|
29
+ options[:content] = value
30
+ end
31
+
32
+ o.on '-s', '--sleep SECONDS', Float, 'the time to sleep before sending every character' do |value|
33
+ options[:sleep] = value
34
+ end
35
+ end.parse!
36
+
37
+ class Slowpoke < Barlume::Lucciola
38
+ def initialize (socket, message)
39
+ super(socket)
40
+
41
+ @message = message
42
+ @offset = 0
43
+ end
44
+
45
+ def send_next
46
+ return if done?
47
+
48
+ write_nonblock @message[@offset]
49
+
50
+ @offset += 1
51
+ rescue Errno::EAGAIN, Errno::EWOULDBLOCK
52
+ end
53
+
54
+ def done?
55
+ @offset >= @message.length
56
+ end
57
+ end
58
+
59
+ lantern = Barlume::Lanterna.best
60
+
61
+ options[:threads].times {
62
+ lantern << Slowpoke.new(TCPSocket.new(options[:host], options[:port]), options[:content])
63
+ }
64
+
65
+ puts "oh noes, a wall on my path D:"
66
+
67
+ until lantern.descriptors.all?(&:done?)
68
+ lantern.writable.each {|s|
69
+ s.send_next
70
+ }
71
+
72
+ sleep options[:sleep]
73
+ end
74
+
75
+ puts "oh, there's a door ( ・ ◡◡・)"
@@ -19,94 +19,88 @@
19
19
 
20
20
  require 'barlume/lucciola'
21
21
 
22
+ require 'barlume/lanterna/helpers'
23
+ require 'barlume/lanterna/select'
24
+ require 'barlume/lanterna/poll'
25
+ require 'barlume/lanterna/epoll'
26
+ require 'barlume/lanterna/kqueue'
27
+ require 'barlume/lanterna/port'
28
+ require 'barlume/lanterna/dpoll'
29
+
22
30
  module Barlume
23
31
 
24
32
  class Lanterna
25
- class << self
26
- %w[select poll epoll kqueue].each {|name|
27
- define_method "#{name}?" do
28
- const_get(name.capitalize).supported?
29
- end
33
+ %w[select poll epoll kqueue port dpoll].each {|name|
34
+ klass = Lanterna.const_get(Lanterna.constants.find { |c| c.downcase.to_s == name })
30
35
 
31
- define_method name do
32
- const_get(name.capitalize).new
33
- end
34
- }
36
+ define_singleton_method "#{name}?" do
37
+ klass.supported?
38
+ end
35
39
 
36
- def best
37
- return kqueue if kqueue?
40
+ define_singleton_method name do
41
+ klass.new
42
+ end
43
+ }
38
44
 
39
- return epoll if epoll?
45
+ def self.best
46
+ return kqueue if kqueue?
40
47
 
41
- return poll if poll?
48
+ return epoll if epoll?
49
+
50
+ return port if port?
42
51
 
43
- return select
44
- end
52
+ return dpoll if dpoll? && RUBY_PLATFORM =~ /solaris/i
45
53
 
46
- def new (*)
47
- raise 'unsupported platform' unless supported?
54
+ return poll if poll?
48
55
 
49
- super
50
- end
56
+ return select
51
57
  end
52
58
 
53
- Available = Struct.new(:readable, :writable, :error)
59
+ def self.best_edge_triggered
60
+ return kqueue.edge_triggered! if kqueue?
54
61
 
55
- class Breaker
56
- def initialize
57
- @pipes = IO.pipe
58
- end
59
-
60
- def break
61
- @pipes.last.write_nonblock 'x'
62
- end
62
+ return epoll.edge_triggered! if epoll?
63
63
 
64
- def flush
65
- @pipes.first.read_nonblock 2048 rescue nil
66
- end
64
+ raise 'this platform does not support edge triggered primitives'
65
+ end
67
66
 
68
- def to_io
69
- @pipes.first
70
- end
67
+ def self.new (*)
68
+ raise 'unsupported platform' unless supported?
71
69
 
72
- def to_i
73
- to_io.to_i
74
- end
70
+ super
75
71
  end
76
72
 
77
- attr_reader :descriptors
73
+ include Enumerable
78
74
 
79
75
  def initialize
80
- @breaker = Breaker.new
81
- @descriptors = []
82
- @report_errors = false
76
+ @breaker = Breaker.new
77
+ @descriptors = {}
78
+ @readable = {}
79
+ @writable = {}
83
80
  end
84
81
 
85
82
  def name
86
83
  self.class.name[/(?:::)?([^:]*)$/, 1].downcase.to_sym
87
84
  end
88
85
 
89
- def report_errors?
90
- @report_errors
91
- end
92
-
93
- def report_errors!
94
- @report_errors = true
95
- end
86
+ def break (with = nil)
87
+ @breaker.break(with)
96
88
 
97
- def dont_report_errors!
98
- @report_errors = false
89
+ self
99
90
  end
100
91
 
101
- def break
102
- @breaker.break
103
- end
92
+ def add (what, mode = nil, &block)
93
+ Lucciola.wrap(what).tap {|lucciola|
94
+ if @descriptors.has_key?(lucciola.to_i)
95
+ raise ArgumentError, "#{what.inspect} is already trapped"
96
+ end
104
97
 
105
- def add (what)
106
- Lucciola.wrap(what).tap {|l|
107
- return false if @descriptors.member?(l)
98
+ @descriptors[lucciola.to_i] = lucciola
99
+ lucciola.trap_in self
100
+ block.call lucciola if block
108
101
 
109
- @descriptors.push(l)
102
+ readable! lucciola if mode == :readable || mode == :both
103
+ writable! lucciola if mode == :writable || mode == :both
110
104
  }
111
105
  end
112
106
 
@@ -118,17 +112,201 @@ class Lanterna
118
112
 
119
113
  alias << push
120
114
 
121
- def remove (what)
122
- @descriptors.delete(Lucciola.wrap(what))
115
+ def remove (what, &block)
116
+ unless lucciola = @descriptors[what.to_i]
117
+ raise ArgumentError, "#{what.inspect} isn't trapped here"
118
+ end
119
+
120
+ block.call lucciola if block
121
+
122
+ @descriptors.delete(lucciola.to_i)
123
+ @readable.delete(lucciola.to_i)
124
+ @writable.delete(lucciola.to_i)
125
+
126
+ lucciola.set_free
123
127
  end
124
128
 
125
129
  alias delete remove
126
- end
127
130
 
131
+ def has? (what)
132
+ @descriptors.has_key?(what.to_i)
133
+ end
134
+
135
+ def [] (what)
136
+ @descriptors[what.to_i]
137
+ end
138
+
139
+ def each (mode = nil, &block)
140
+ return enum_for :each, mode unless block
141
+
142
+ if mode.nil?
143
+ @descriptors.each_value(&block)
144
+ elsif mode == :readable
145
+ @readable.each_value(&block)
146
+ elsif mode == :writable
147
+ @writable.each_value(&block)
148
+ else
149
+ raise ArgumentError, "#{mode} is an unknown mode"
150
+ end
151
+
152
+ self
153
+ end
154
+
155
+ def each_readable (&block)
156
+ each(:readable, &block)
157
+ end
158
+
159
+ def each_writable (&block)
160
+ each(:writable, &block)
161
+ end
162
+
163
+ def readable! (*args, &block)
164
+ args.flatten!
165
+ args.compact!
166
+
167
+ if args.empty?
168
+ each { |c| readable! c, &block }
169
+ else
170
+ args.each {|what|
171
+ unless readable?(what)
172
+ what = self[what]
173
+
174
+ block.call what if block
175
+ @readable[what.to_i] = what
176
+ end
177
+ }
178
+ end
179
+
180
+ self
181
+ end
182
+
183
+ def no_readable! (*args, &block)
184
+ args.flatten!
185
+ args.compact!
186
+
187
+ if args.empty?
188
+ each(:readable) { |c| readable! c, &block }
189
+ else
190
+ args.each {|what|
191
+ if what = readable?(what)
192
+ @readable.delete(what.to_i)
193
+ block.call what if block
194
+ end
195
+ }
196
+ end
197
+
198
+ self
199
+ end
200
+
201
+ def readable? (what = nil)
202
+ return !@readable.empty? unless what
203
+
204
+ return false unless what = @readable[what.to_i]
205
+
206
+ what
207
+ end
208
+
209
+ def writable! (*args, &block)
210
+ args.flatten!
211
+ args.compact!
212
+
213
+ if args.empty?
214
+ each { |c| writable! c, &block }
215
+ else
216
+ args.each {|what|
217
+ unless writable?(what)
218
+ what = self[what]
219
+
220
+ block.call what if block
221
+ @writable[what.to_i] = what
222
+ end
223
+ }
224
+ end
225
+
226
+ self
227
+ end
228
+
229
+ def no_writable! (*args, &block)
230
+ args.flatten!
231
+ args.compact!
232
+
233
+ if args.empty?
234
+ each(:writable) { |c| writable! c, &block }
235
+ else
236
+ args.each {|what|
237
+ if what = writable?(what)
238
+ @writable.delete(what.to_i)
239
+ block.call what if block
240
+ end
241
+ }
242
+ end
243
+
244
+ self
245
+ end
246
+
247
+ def writable? (what = nil)
248
+ return !@writable.empty? unless what
249
+
250
+ return false unless what = @writable[what.to_i]
251
+
252
+ what
253
+ end
254
+
255
+ def available (timeout = nil, &block)
256
+ raise NotImplementedError, 'available has not been implemented'
257
+ end
258
+
259
+ def readable (timeout = nil, &block)
260
+ readable = @readable.dup
261
+ writable = @writable.dup
262
+
263
+ no_writable!
264
+ readable!
265
+
266
+ result = available(timeout, &block)
267
+
268
+ no_readable! @readable - readable
269
+ writable! writable
270
+
271
+ result
272
+ end
273
+
274
+ def writable (timeout = nil, &block)
275
+ readable = @readable.dup
276
+ writable = @writable.dup
277
+
278
+ no_readable!
279
+ writable!
280
+
281
+ result = available(timeout, &block)
282
+
283
+ no_writable! @writable - writable
284
+ readable! readable
285
+
286
+ result
287
+ end
288
+
289
+ class Breaker
290
+ def initialize
291
+ @pipes = IO.pipe
292
+ end
293
+
294
+ def break (with = nil)
295
+ @pipes.last.write(Marshal.dump(with))
296
+ end
297
+
298
+ def reason
299
+ Marshal.load(@pipes.first)
300
+ end
301
+
302
+ def to_io
303
+ @pipes.first
304
+ end
305
+
306
+ def to_i
307
+ to_io.to_i
308
+ end
309
+ end
128
310
  end
129
311
 
130
- require 'barlume/lanterna/utils'
131
- require 'barlume/lanterna/select'
132
- require 'barlume/lanterna/poll'
133
- require 'barlume/lanterna/epoll'
134
- require 'barlume/lanterna/kqueue'
312
+ end