ione 1.2.0.pre4 → 1.2.0.pre5
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.
- checksums.yaml +4 -4
- data/lib/ione/heap.rb +92 -0
- data/lib/ione/io/io_reactor.rb +66 -22
- data/lib/ione/version.rb +1 -1
- data/spec/ione/heap_spec.rb +160 -0
- data/spec/ione/io/io_reactor_spec.rb +13 -19
- metadata +5 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c9338bdefe6cebb6528e884eed99230b3bd6ef0c
|
4
|
+
data.tar.gz: 3e28ef46473409fcca687711a9550a4c91539d67
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2f28bebc52bf02a2d885d611b26cb68204e026e35c16d29790b320dc936adfa1e8af2f495148910696df8d7f9c5fc4346115424e3dd82ad52506193daf4ace9e
|
7
|
+
data.tar.gz: 5a466d53f3988533504292269b7494e993ad955be605eccbd9d7230ec2c238fbcb276290a6dc465eaedc6d51e49c814c610304bbb61900048fd45206452ef64a
|
data/lib/ione/heap.rb
ADDED
@@ -0,0 +1,92 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Ione
|
4
|
+
# @private
|
5
|
+
class Heap
|
6
|
+
def initialize
|
7
|
+
@items = []
|
8
|
+
end
|
9
|
+
|
10
|
+
def size
|
11
|
+
@items.size
|
12
|
+
end
|
13
|
+
|
14
|
+
def empty?
|
15
|
+
@items.empty?
|
16
|
+
end
|
17
|
+
|
18
|
+
def push(item)
|
19
|
+
@items << item
|
20
|
+
bubble_up(@items.size - 1)
|
21
|
+
end
|
22
|
+
alias_method :<<, :push
|
23
|
+
|
24
|
+
def peek
|
25
|
+
@items.first
|
26
|
+
end
|
27
|
+
|
28
|
+
def pop
|
29
|
+
if @items.size == 0
|
30
|
+
nil
|
31
|
+
elsif @items.size == 1
|
32
|
+
@items.pop
|
33
|
+
else
|
34
|
+
item = @items.first
|
35
|
+
@items[0] = @items.pop
|
36
|
+
bubble_down(0)
|
37
|
+
item
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def delete(item)
|
42
|
+
if item == @items[0]
|
43
|
+
pop
|
44
|
+
elsif (i = index(item))
|
45
|
+
item = @items[i]
|
46
|
+
@items[i] = @items.pop
|
47
|
+
bubble_down(i)
|
48
|
+
item
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
private
|
53
|
+
|
54
|
+
def index(item, root_index=0)
|
55
|
+
left_index = (root_index * 2) + 1
|
56
|
+
right_index = (root_index * 2) + 2
|
57
|
+
root_item = @items[root_index]
|
58
|
+
if root_item == item
|
59
|
+
root_index
|
60
|
+
elsif left_index < @items.length && item >= @items[left_index] && (i = index(item, left_index))
|
61
|
+
i
|
62
|
+
elsif right_index < @items.length && item >= @items[right_index] && (i = index(item, right_index))
|
63
|
+
i
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def bubble_up(index)
|
68
|
+
parent_index = (index - 1)/2
|
69
|
+
if parent_index >= 0 && @items[parent_index] > @items[index]
|
70
|
+
item = @items[index]
|
71
|
+
@items[index] = @items[parent_index]
|
72
|
+
@items[parent_index] = item
|
73
|
+
bubble_up(parent_index)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def bubble_down(index)
|
78
|
+
child_index = (index * 2) + 1
|
79
|
+
unless child_index >= @items.length
|
80
|
+
if child_index + 1 < @items.length && @items[child_index] > @items[child_index + 1]
|
81
|
+
child_index += 1
|
82
|
+
end
|
83
|
+
if @items[index] > @items[child_index]
|
84
|
+
item = @items[index]
|
85
|
+
@items[index] = @items[child_index]
|
86
|
+
@items[child_index] = item
|
87
|
+
bubble_down(child_index)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
data/lib/ione/io/io_reactor.rb
CHANGED
@@ -1,5 +1,8 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
|
3
|
+
require 'ione/heap'
|
4
|
+
|
5
|
+
|
3
6
|
module Ione
|
4
7
|
module Io
|
5
8
|
ReactorError = Class.new(IoError)
|
@@ -298,6 +301,32 @@ module Ione
|
|
298
301
|
DEFAULT_CONNECT_OPTIONS = {:timeout => 5}.freeze
|
299
302
|
end
|
300
303
|
|
304
|
+
# @private
|
305
|
+
class Timer < Promise
|
306
|
+
include Comparable
|
307
|
+
|
308
|
+
attr_reader :time
|
309
|
+
|
310
|
+
def initialize(time)
|
311
|
+
super()
|
312
|
+
@time = time
|
313
|
+
end
|
314
|
+
|
315
|
+
def <=>(other)
|
316
|
+
cmp = @time <=> other.time
|
317
|
+
if cmp == 0
|
318
|
+
self.object_id <=> other.object_id
|
319
|
+
else
|
320
|
+
cmp
|
321
|
+
end
|
322
|
+
end
|
323
|
+
|
324
|
+
def to_s
|
325
|
+
"#<Timer #{@time}>"
|
326
|
+
end
|
327
|
+
alias_method :inspect, :to_s
|
328
|
+
end
|
329
|
+
|
301
330
|
# @private
|
302
331
|
class IoLoopBody
|
303
332
|
def initialize(options={})
|
@@ -305,7 +334,8 @@ module Ione
|
|
305
334
|
@clock = options[:clock] || Time
|
306
335
|
@lock = Mutex.new
|
307
336
|
@sockets = []
|
308
|
-
@
|
337
|
+
@timer_queue = Heap.new
|
338
|
+
@pending_timers = {}
|
309
339
|
end
|
310
340
|
|
311
341
|
def add_socket(socket)
|
@@ -323,24 +353,29 @@ module Ione
|
|
323
353
|
end
|
324
354
|
end
|
325
355
|
|
326
|
-
def schedule_timer(timeout
|
356
|
+
def schedule_timer(timeout)
|
327
357
|
@lock.lock
|
328
|
-
|
329
|
-
|
330
|
-
@
|
331
|
-
|
358
|
+
timer = Timer.new(@clock.now + timeout)
|
359
|
+
@timer_queue << timer
|
360
|
+
@pending_timers[timer.future] = timer
|
361
|
+
timer.future
|
332
362
|
ensure
|
333
363
|
@lock.unlock
|
334
364
|
end
|
335
365
|
|
336
366
|
def cancel_timer(timer_future)
|
367
|
+
timer = nil
|
337
368
|
@lock.lock
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
369
|
+
begin
|
370
|
+
if (timer = @pending_timers.delete(timer_future))
|
371
|
+
@timer_queue.delete(timer)
|
372
|
+
end
|
373
|
+
ensure
|
374
|
+
@lock.unlock
|
375
|
+
end
|
376
|
+
if timer
|
377
|
+
timer.fail(CancelledError.new)
|
378
|
+
end
|
344
379
|
end
|
345
380
|
|
346
381
|
def close_sockets
|
@@ -354,11 +389,9 @@ module Ione
|
|
354
389
|
end
|
355
390
|
|
356
391
|
def cancel_timers
|
357
|
-
@
|
358
|
-
|
359
|
-
|
360
|
-
pair[1] = nil
|
361
|
-
end
|
392
|
+
while (timer = @timer_queue.pop)
|
393
|
+
@pending_timers.delete(timer.future)
|
394
|
+
timer.fail(CancelledError.new)
|
362
395
|
end
|
363
396
|
end
|
364
397
|
|
@@ -392,11 +425,22 @@ module Ione
|
|
392
425
|
end
|
393
426
|
|
394
427
|
def check_timers!
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
|
428
|
+
now = @clock.now
|
429
|
+
first_timer = @timer_queue.peek
|
430
|
+
if first_timer && first_timer.time <= now
|
431
|
+
expired_timers = []
|
432
|
+
@lock.lock
|
433
|
+
begin
|
434
|
+
while (timer = @timer_queue.peek) && timer.time <= now
|
435
|
+
@timer_queue.pop
|
436
|
+
@pending_timers.delete(timer.future)
|
437
|
+
expired_timers << timer
|
438
|
+
end
|
439
|
+
ensure
|
440
|
+
@lock.unlock
|
441
|
+
end
|
442
|
+
expired_timers.each do |timer|
|
443
|
+
timer.fulfill
|
400
444
|
end
|
401
445
|
end
|
402
446
|
end
|
data/lib/ione/version.rb
CHANGED
@@ -0,0 +1,160 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
|
6
|
+
module Ione
|
7
|
+
describe Heap do
|
8
|
+
let :heap do
|
9
|
+
described_class.new
|
10
|
+
end
|
11
|
+
|
12
|
+
describe '#size' do
|
13
|
+
it 'is zero when the heap is empty' do
|
14
|
+
heap.size.should be_zero
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'returns the number of items in the heap' do
|
18
|
+
heap.push(13)
|
19
|
+
heap.push(100)
|
20
|
+
heap.size.should == 2
|
21
|
+
heap.push(101)
|
22
|
+
heap.size.should == 3
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
describe '#empty?' do
|
27
|
+
it 'returns true when there are no items in the heap' do
|
28
|
+
heap.should be_empty
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'returns false when there are items in the heap' do
|
32
|
+
heap.push(1)
|
33
|
+
heap.should_not be_empty
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
describe '#push' do
|
38
|
+
it 'adds items to the heap' do
|
39
|
+
heap.push(4)
|
40
|
+
heap.push(3)
|
41
|
+
heap.push(6)
|
42
|
+
heap.push(5)
|
43
|
+
heap.size.should == 4
|
44
|
+
end
|
45
|
+
|
46
|
+
it 'is aliase as #<<' do
|
47
|
+
heap << 4
|
48
|
+
heap << 3
|
49
|
+
heap << 6
|
50
|
+
heap << 5
|
51
|
+
heap.size.should == 4
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
describe '#peek' do
|
56
|
+
it 'returns nil when there are no items in the heap' do
|
57
|
+
heap.peek.should be_nil
|
58
|
+
end
|
59
|
+
|
60
|
+
it 'returns the only item when there is only one' do
|
61
|
+
heap.push(3)
|
62
|
+
heap.peek.should == 3
|
63
|
+
end
|
64
|
+
|
65
|
+
it 'returns the smallest item' do
|
66
|
+
heap.push(10)
|
67
|
+
heap.push(3)
|
68
|
+
heap.push(7)
|
69
|
+
heap.peek.should == 3
|
70
|
+
end
|
71
|
+
|
72
|
+
it 'does not remove the item from the heap' do
|
73
|
+
heap.push(3)
|
74
|
+
heap.peek.should == 3
|
75
|
+
heap.peek.should == 3
|
76
|
+
heap.peek.should == 3
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
describe '#pop' do
|
81
|
+
it 'returns nil when there are no items in the heap' do
|
82
|
+
heap.pop.should be_nil
|
83
|
+
end
|
84
|
+
|
85
|
+
it 'returns and removes the only item when there is only one' do
|
86
|
+
heap.push(3)
|
87
|
+
heap.pop.should == 3
|
88
|
+
heap.should be_empty
|
89
|
+
end
|
90
|
+
|
91
|
+
it 'returns and removes the smallest item' do
|
92
|
+
heap.push(10)
|
93
|
+
heap.push(3)
|
94
|
+
heap.push(7)
|
95
|
+
heap.pop.should == 3
|
96
|
+
heap.pop.should == 7
|
97
|
+
heap.size.should == 1
|
98
|
+
end
|
99
|
+
|
100
|
+
it 'removes the item from the heap' do
|
101
|
+
heap.push(3)
|
102
|
+
heap.pop.should == 3
|
103
|
+
heap.pop.should be_nil
|
104
|
+
end
|
105
|
+
|
106
|
+
it 'returns each duplicate' do
|
107
|
+
heap.push(3)
|
108
|
+
heap.push(4)
|
109
|
+
heap.push(3)
|
110
|
+
heap.push(3)
|
111
|
+
heap.pop.should == 3
|
112
|
+
heap.pop.should == 3
|
113
|
+
heap.pop.should == 3
|
114
|
+
heap.pop.should == 4
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
describe '#delete' do
|
119
|
+
it 'removes the item from a heap with one item' do
|
120
|
+
heap.push(3)
|
121
|
+
heap.delete(3)
|
122
|
+
heap.should be_empty
|
123
|
+
end
|
124
|
+
|
125
|
+
it 'removes the item from the heap' do
|
126
|
+
heap.push(4)
|
127
|
+
heap.push(3)
|
128
|
+
heap.push(100)
|
129
|
+
heap.push(101)
|
130
|
+
heap.delete(4)
|
131
|
+
heap.pop
|
132
|
+
heap.peek.should == 100
|
133
|
+
heap.size.should == 2
|
134
|
+
end
|
135
|
+
|
136
|
+
it 'returns the item' do
|
137
|
+
heap.push(3)
|
138
|
+
heap.push(4)
|
139
|
+
heap.push(5)
|
140
|
+
heap.delete(4).should == 4
|
141
|
+
end
|
142
|
+
|
143
|
+
it 'returns nil when the item is not found' do
|
144
|
+
heap.push(3)
|
145
|
+
heap.push(4)
|
146
|
+
heap.push(5)
|
147
|
+
heap.delete(6).should be_nil
|
148
|
+
end
|
149
|
+
|
150
|
+
it 'removes the first instance of the item from the heap' do
|
151
|
+
heap.push(3)
|
152
|
+
heap.push(3)
|
153
|
+
heap.push(5)
|
154
|
+
heap.delete(3).should == 3
|
155
|
+
heap.delete(3).should == 3
|
156
|
+
heap.size.should == 1
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
@@ -435,25 +435,22 @@ module Ione
|
|
435
435
|
it 'completes timers that have expired' do
|
436
436
|
selector.stub(:select).and_return([nil, nil, nil])
|
437
437
|
clock.stub(:now).and_return(1)
|
438
|
-
|
439
|
-
loop_body.schedule_timer(1, promise)
|
438
|
+
future = loop_body.schedule_timer(1)
|
440
439
|
loop_body.tick
|
441
|
-
|
440
|
+
future.should_not be_completed
|
442
441
|
clock.stub(:now).and_return(2)
|
443
442
|
loop_body.tick
|
444
|
-
|
443
|
+
future.should be_completed
|
445
444
|
end
|
446
445
|
|
447
446
|
it 'clears out timers that have expired' do
|
448
447
|
selector.stub(:select).and_return([nil, nil, nil])
|
449
448
|
clock.stub(:now).and_return(1)
|
450
|
-
|
451
|
-
loop_body.schedule_timer(1, promise)
|
449
|
+
future = loop_body.schedule_timer(1)
|
452
450
|
clock.stub(:now).and_return(2)
|
453
451
|
loop_body.tick
|
454
|
-
|
455
|
-
|
456
|
-
loop_body.tick
|
452
|
+
future.should be_completed
|
453
|
+
expect { loop_body.tick }.to_not raise_error
|
457
454
|
end
|
458
455
|
end
|
459
456
|
|
@@ -492,20 +489,17 @@ module Ione
|
|
492
489
|
end
|
493
490
|
|
494
491
|
it 'fails all active timers with a CancelledError' do
|
495
|
-
p1 = Promise.new
|
496
|
-
p2 = Promise.new
|
497
|
-
p3 = Promise.new
|
498
492
|
clock.stub(:now).and_return(1)
|
499
|
-
loop_body.schedule_timer(1
|
500
|
-
loop_body.schedule_timer(3
|
501
|
-
loop_body.schedule_timer(3
|
493
|
+
f1 = loop_body.schedule_timer(1)
|
494
|
+
f2 = loop_body.schedule_timer(3)
|
495
|
+
f3 = loop_body.schedule_timer(3)
|
502
496
|
clock.stub(:now).and_return(2)
|
503
497
|
loop_body.tick
|
504
498
|
loop_body.cancel_timers
|
505
|
-
|
506
|
-
|
507
|
-
|
508
|
-
expect {
|
499
|
+
f1.should be_completed
|
500
|
+
f2.should be_failed
|
501
|
+
f3.should be_failed
|
502
|
+
expect { f3.value }.to raise_error(CancelledError)
|
509
503
|
end
|
510
504
|
end
|
511
505
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ione
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.2.0.
|
4
|
+
version: 1.2.0.pre5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Theo Hultberg
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-
|
11
|
+
date: 2014-10-26 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: Reactive programming framework for Ruby, painless evented IO, futures
|
14
14
|
and an efficient byte buffer
|
@@ -23,6 +23,7 @@ files:
|
|
23
23
|
- lib/ione.rb
|
24
24
|
- lib/ione/byte_buffer.rb
|
25
25
|
- lib/ione/future.rb
|
26
|
+
- lib/ione/heap.rb
|
26
27
|
- lib/ione/io.rb
|
27
28
|
- lib/ione/io/acceptor.rb
|
28
29
|
- lib/ione/io/base_connection.rb
|
@@ -37,6 +38,7 @@ files:
|
|
37
38
|
- spec/integration/ssl_spec.rb
|
38
39
|
- spec/ione/byte_buffer_spec.rb
|
39
40
|
- spec/ione/future_spec.rb
|
41
|
+
- spec/ione/heap_spec.rb
|
40
42
|
- spec/ione/io/acceptor_spec.rb
|
41
43
|
- spec/ione/io/connection_common.rb
|
42
44
|
- spec/ione/io/connection_spec.rb
|
@@ -77,6 +79,7 @@ test_files:
|
|
77
79
|
- spec/integration/ssl_spec.rb
|
78
80
|
- spec/ione/byte_buffer_spec.rb
|
79
81
|
- spec/ione/future_spec.rb
|
82
|
+
- spec/ione/heap_spec.rb
|
80
83
|
- spec/ione/io/acceptor_spec.rb
|
81
84
|
- spec/ione/io/connection_common.rb
|
82
85
|
- spec/ione/io/connection_spec.rb
|