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

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