ione 1.2.0.pre4 → 1.2.0.pre5

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: b0d198d610290a10e580f755f216bbae8ccca0ab
4
- data.tar.gz: ada926b95e4a03d55ffad99687ca9f468d1cdd1f
3
+ metadata.gz: c9338bdefe6cebb6528e884eed99230b3bd6ef0c
4
+ data.tar.gz: 3e28ef46473409fcca687711a9550a4c91539d67
5
5
  SHA512:
6
- metadata.gz: 85e98209e280e3fcd0ca52577ffde6fdbe337191af45830bf9b43d3ded0839aa3ef902267da22abb62f937a2d3321ca0abaf15cd66a0920dfe2a9dc3357d2f19
7
- data.tar.gz: e5eac3de0c0024e20bad63f82a32a57973d50081df75bf789f7d76bfda413bb8a180bb5ae6919be3aff91c4fa809d8596bfa26a02b0f3bc995b023801cbf3133
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
@@ -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
- @timers = []
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, promise=Promise.new)
356
+ def schedule_timer(timeout)
327
357
  @lock.lock
328
- timers = @timers.reject { |pair| pair[1].nil? }
329
- timers << [@clock.now + timeout, promise]
330
- @timers = timers
331
- promise.future
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
- timer_pair = @timers.find { |pair| (p = pair[1]) && p.future == timer_future }
339
- @timers = @timers.reject { |pair| pair[1].nil? || pair == timer_pair }
340
- timer_pair && timer_pair[1].fail(CancelledError.new)
341
- nil
342
- ensure
343
- @lock.unlock
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
- @timers.each do |pair|
358
- if pair[1]
359
- pair[1].fail(CancelledError.new)
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
- timers = @timers
396
- timers.each do |pair|
397
- if pair[1] && pair[0] <= @clock.now
398
- pair[1].fulfill
399
- pair[1] = nil
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
@@ -1,5 +1,5 @@
1
1
  # encoding: utf-8
2
2
 
3
3
  module Ione
4
- VERSION = '1.2.0.pre4'.freeze
4
+ VERSION = '1.2.0.pre5'.freeze
5
5
  end
@@ -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
- promise = Promise.new
439
- loop_body.schedule_timer(1, promise)
438
+ future = loop_body.schedule_timer(1)
440
439
  loop_body.tick
441
- promise.future.should_not be_completed
440
+ future.should_not be_completed
442
441
  clock.stub(:now).and_return(2)
443
442
  loop_body.tick
444
- promise.future.should be_completed
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
- promise = Promise.new
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
- promise.future.should be_completed
455
- promise.should_not_receive(:fulfill)
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, p1)
500
- loop_body.schedule_timer(3, p2)
501
- loop_body.schedule_timer(3, p3)
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
- p1.future.should be_completed
506
- p2.future.should be_failed
507
- p3.future.should be_failed
508
- expect { p3.future.value }.to raise_error(CancelledError)
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.pre4
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-09-24 00:00:00.000000000 Z
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