concurrent-ruby-edge 0.2.0.pre2 → 0.2.0.pre3

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,127 @@
1
+ require 'concurrent/channel/buffer/base'
2
+
3
+ module Concurrent
4
+ class Channel
5
+ module Buffer
6
+
7
+ # A buffer with a fixed internal capacity. Items can be put onto the
8
+ # buffer without blocking until the internal capacity is reached. Once
9
+ # the buffer is at capacity, subsequent calls to {#put} will block until
10
+ # an item is removed from the buffer, creating spare capacity.
11
+ class Buffered < Base
12
+
13
+ # @!macro channel_buffer_initialize
14
+ #
15
+ # @param [Integer] size the maximum capacity of the buffer; must be
16
+ # greater than zero.
17
+ # @raise [ArgumentError] when the size is zero (0) or less.
18
+ def initialize(size)
19
+ raise ArgumentError.new('size must be greater than 0') if size.to_i <= 0
20
+ super()
21
+ synchronize do
22
+ @size = size.to_i
23
+ @buffer = []
24
+ end
25
+ end
26
+
27
+ # @!macro channel_buffer_empty_question
28
+ def empty?
29
+ synchronize { ns_empty? }
30
+ end
31
+
32
+ # @!macro channel_buffer_full_question
33
+ #
34
+ # Will return `true` once the number of items in the buffer reaches
35
+ # the {#size} value specified during initialization.
36
+ def full?
37
+ synchronize { ns_full? }
38
+ end
39
+
40
+ # @!macro channel_buffer_put
41
+ #
42
+ # New items can be put onto the buffer until the number of items in
43
+ # the buffer reaches the {#size} value specified during
44
+ # initialization.
45
+ def put(item)
46
+ loop do
47
+ synchronize do
48
+ if ns_closed?
49
+ return false
50
+ elsif !ns_full?
51
+ ns_put_onto_buffer(item)
52
+ return true
53
+ end
54
+ end
55
+ Thread.pass
56
+ end
57
+ end
58
+
59
+ # @!macro channel_buffer_offer
60
+ #
61
+ # New items can be put onto the buffer until the number of items in
62
+ # the buffer reaches the {#size} value specified during
63
+ # initialization.
64
+ def offer(item)
65
+ synchronize do
66
+ if ns_closed? || ns_full?
67
+ return false
68
+ else
69
+ ns_put_onto_buffer(item)
70
+ return true
71
+ end
72
+ end
73
+ end
74
+
75
+ # @!macro channel_buffer_take
76
+ def take
77
+ item, _ = self.next
78
+ item
79
+ end
80
+
81
+ # @!macro channel_buffer_next
82
+ def next
83
+ loop do
84
+ synchronize do
85
+ if ns_closed? && ns_empty?
86
+ return NO_VALUE, false
87
+ elsif !ns_empty?
88
+ item = @buffer.shift
89
+ more = !ns_empty? || !ns_closed?
90
+ return item, more
91
+ end
92
+ end
93
+ Thread.pass
94
+ end
95
+ end
96
+
97
+ # @!macro channel_buffer_poll
98
+ def poll
99
+ synchronize do
100
+ if ns_empty?
101
+ NO_VALUE
102
+ else
103
+ @buffer.shift
104
+ end
105
+ end
106
+ end
107
+
108
+ private
109
+
110
+ # @!macro channel_buffer_empty_question
111
+ def ns_empty?
112
+ @buffer.length == 0
113
+ end
114
+
115
+ # @!macro channel_buffer_full_question
116
+ def ns_full?
117
+ @buffer.length == @size
118
+ end
119
+
120
+ # @!macro channel_buffer_put
121
+ def ns_put_onto_buffer(item)
122
+ @buffer.push(item)
123
+ end
124
+ end
125
+ end
126
+ end
127
+ end
@@ -0,0 +1,53 @@
1
+ require 'concurrent/channel/buffer/base'
2
+
3
+ module Concurrent
4
+ class Channel
5
+ module Buffer
6
+
7
+ # A non-blocking, buffered buffer of fixed maximum capacity. When the
8
+ # maximum capacity is reached subsequent {#put} and {#offer} operations
9
+ # will complete but the `put` item will be discarded; no transfer will
10
+ # occur.
11
+ class Dropping < Buffered
12
+
13
+ # @!method put(item)
14
+ # @!macro channel_buffer_put
15
+ #
16
+ # When the buffer is full, this method will return `true`
17
+ # immediately but the item will be discarded. The item will *not*
18
+ # be placed into the buffer (no transfer will occur).
19
+
20
+ # @!method offer(item)
21
+ # @!macro channel_buffer_offer
22
+ #
23
+ # When the buffer is full, this method will return `true`
24
+ # immediately but the item will be discarded. The item will *not*
25
+ # be placed into the buffer (no transfer will occur).
26
+
27
+ # @!method full?
28
+ # @!macro channel_buffer_full_question
29
+ #
30
+ # Always returns `false`.
31
+
32
+ # @!macro channel_buffer_blocking_question
33
+ #
34
+ # Always returns `false`.
35
+ def blocking?
36
+ false
37
+ end
38
+
39
+ private
40
+
41
+ # @!macro channel_buffer_full_question
42
+ def ns_full?
43
+ false
44
+ end
45
+
46
+ # @!macro channel_buffer_put
47
+ def ns_put_onto_buffer(item)
48
+ @buffer.push(item) unless @buffer.size == size
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,54 @@
1
+ require 'concurrent/channel/buffer/base'
2
+
3
+ module Concurrent
4
+ class Channel
5
+ module Buffer
6
+
7
+ # A non-blocking, buffered buffer of fixed maximum capacity. When the
8
+ # maximum capacity is reached subsequent {#put} and {#offer} operations
9
+ # will complete and the item will be `put`, but the oldest elements in
10
+ # the buffer will be discarded (not transferred).
11
+ class Sliding < Buffered
12
+
13
+ # @!method put(item)
14
+ # @!macro channel_buffer_put
15
+ #
16
+ # When the buffer is full, this method will return `true`
17
+ # immediately and the item will be inserted, but the oldest
18
+ # elements in the buffer will be discarded (not transferred).
19
+
20
+ # @!method offer(item)
21
+ # @!macro channel_buffer_offer
22
+ #
23
+ # When the buffer is full, this method will return `true`
24
+ # immediately and the item will be inserted, but the oldest
25
+ # elements in the buffer will be discarded (not transferred).
26
+
27
+ # @!method full?
28
+ # @!macro channel_buffer_full_question
29
+ #
30
+ # Always returns `false`.
31
+
32
+ # @!macro channel_buffer_blocking_question
33
+ #
34
+ # Always returns `false`.
35
+ def blocking?
36
+ false
37
+ end
38
+
39
+ private
40
+
41
+ # @!macro channel_buffer_full_question
42
+ def ns_full?
43
+ false
44
+ end
45
+
46
+ # @!macro channel_buffer_put
47
+ def ns_put_onto_buffer(item)
48
+ @buffer.shift if @buffer.size == size
49
+ @buffer.push(item)
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,80 @@
1
+ require 'concurrent/utility/monotonic_time'
2
+ require 'concurrent/channel/tick'
3
+ require 'concurrent/channel/buffer/base'
4
+
5
+ module Concurrent
6
+ class Channel
7
+ module Buffer
8
+
9
+ class Ticker < Base
10
+
11
+ def initialize(interval)
12
+ super()
13
+ synchronize do
14
+ @interval = interval.to_f
15
+ @next_tick = Concurrent.monotonic_time + interval
16
+ end
17
+ end
18
+
19
+ def size() 1; end
20
+
21
+ def empty?() false; end
22
+
23
+ def full?() true; end
24
+
25
+ def put(item)
26
+ false
27
+ end
28
+
29
+ def offer(item)
30
+ false
31
+ end
32
+
33
+ def take
34
+ loop do
35
+ result, _ = do_poll
36
+ if result.nil?
37
+ return NO_VALUE
38
+ elsif result != NO_VALUE
39
+ return result
40
+ end
41
+ end
42
+ end
43
+
44
+ def next
45
+ loop do
46
+ result, _ = do_poll
47
+ if result.nil?
48
+ return NO_VALUE, false
49
+ elsif result != NO_VALUE
50
+ return result, true
51
+ end
52
+ end
53
+ end
54
+
55
+ def poll
56
+ result, _ = do_poll
57
+ if result.nil? || result == NO_VALUE
58
+ NO_VALUE
59
+ else
60
+ result
61
+ end
62
+ end
63
+
64
+ private
65
+
66
+ def do_poll
67
+ if ns_closed?
68
+ return nil, false
69
+ elsif (now = Concurrent.monotonic_time) > @next_tick
70
+ tick = Concurrent::Channel::Tick.new(@next_tick)
71
+ @next_tick = now + @interval
72
+ return tick, true
73
+ else
74
+ return NO_VALUE, true
75
+ end
76
+ end
77
+ end
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,78 @@
1
+ require 'concurrent/utility/monotonic_time'
2
+ require 'concurrent/channel/tick'
3
+ require 'concurrent/channel/buffer/base'
4
+
5
+ module Concurrent
6
+ class Channel
7
+ module Buffer
8
+
9
+ class Timer < Base
10
+
11
+ def initialize(delay)
12
+ super()
13
+ synchronize do
14
+ @tick = Concurrent.monotonic_time + delay.to_f
15
+ @closed = false
16
+ @empty = false
17
+ end
18
+ end
19
+
20
+ def size() 1; end
21
+
22
+ def empty?
23
+ synchronized { @empty }
24
+ end
25
+
26
+ def full?
27
+ !empty?
28
+ end
29
+
30
+ def put(item)
31
+ false
32
+ end
33
+
34
+ def offer(item)
35
+ false
36
+ end
37
+
38
+ def take
39
+ self.next.first
40
+ end
41
+
42
+ def next
43
+ loop do
44
+ status, tick = do_poll
45
+ if status == :tick
46
+ return tick, false
47
+ # AFAIK a Go timer will block forever if stopped
48
+ #elsif status == :closed
49
+ #return false, false
50
+ end
51
+ Thread.pass
52
+ end
53
+ end
54
+
55
+ def poll
56
+ status, tick = do_poll
57
+ status == :tick ? tick : NO_VALUE
58
+ end
59
+
60
+ private
61
+
62
+ def do_poll
63
+ synchronize do
64
+ return :closed, false if ns_closed?
65
+
66
+ if Concurrent.monotonic_time > @tick
67
+ # only one listener gets notified
68
+ @closed = @empty = true
69
+ return :tick, Concurrent::Channel::Tick.new(@tick)
70
+ else
71
+ return :wait, true
72
+ end
73
+ end
74
+ end
75
+ end
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,151 @@
1
+ require 'concurrent/channel/buffer/base'
2
+ require 'concurrent/atomic/atomic_reference'
3
+
4
+ module Concurrent
5
+ class Channel
6
+ module Buffer
7
+
8
+ # A blocking buffer with a size of zero. An item can only be put onto
9
+ # the buffer when a thread is waiting to take. Similarly, an item can
10
+ # only be put onto the buffer when a thread is waiting to put. When
11
+ # either {#put} or {#take} is called and there is no corresponding call
12
+ # in progress, the call will block indefinitely. Any other calls to the
13
+ # same method will queue behind the first call and block as well. As
14
+ # soon as a corresponding put/take call is made an exchange will occur
15
+ # and the first blocked call will return.
16
+ class Unbuffered < Base
17
+
18
+ # @!macro channel_buffer_initialize
19
+ def initialize
20
+ super
21
+ synchronize do
22
+ # one will always be empty
23
+ @putting = []
24
+ @taking = []
25
+ @closed = false
26
+ end
27
+ end
28
+
29
+ # @!macro channel_buffer_size_reader
30
+ #
31
+ # Always returns zero (0).
32
+ def size() 0; end
33
+
34
+ # @!macro channel_buffer_empty_question
35
+ #
36
+ # Always returns `true`.
37
+ def empty?() true; end
38
+
39
+ # @!macro channel_buffer_full_question
40
+ #
41
+ # Always returns `false`.
42
+ def full?() false; end
43
+
44
+ # @!macro channel_buffer_put
45
+ #
46
+ # Items can only be put onto the buffer when one or more threads are
47
+ # waiting to {#take} items off the buffer. When there is a thread
48
+ # waiting to take an item this method will give its item and return
49
+ # immediately. When there are no threads waiting to take, this method
50
+ # will block. As soon as a thread calls `take` the exchange will
51
+ # occur and this method will return.
52
+ def put(item)
53
+ mine = synchronize do
54
+ return false if ns_closed?
55
+
56
+ ref = Concurrent::AtomicReference.new(item)
57
+ if @taking.empty?
58
+ @putting.push(ref)
59
+ else
60
+ taking = @taking.shift
61
+ taking.value = item
62
+ ref.value = nil
63
+ end
64
+ ref
65
+ end
66
+ loop do
67
+ return true if mine.value.nil?
68
+ Thread.pass
69
+ end
70
+ end
71
+
72
+ # @!macro channel_buffer_offer
73
+ #
74
+ # Items can only be put onto the buffer when one or more threads are
75
+ # waiting to {#take} items off the buffer. When there is a thread
76
+ # waiting to take an item this method will give its item and return
77
+ # `true` immediately. When there are no threads waiting to take or the
78
+ # buffer is closed, this method will return `false` immediately.
79
+ def offer(item)
80
+ synchronize do
81
+ return false if ns_closed? || @taking.empty?
82
+
83
+ taking = @taking.shift
84
+ taking.value = item
85
+ true
86
+ end
87
+ end
88
+
89
+ # @!macro channel_buffer_take
90
+ #
91
+ # Items can only be taken from the buffer when one or more threads are
92
+ # waiting to {#put} items onto the buffer. When there is a thread
93
+ # waiting to put an item this method will take that item and return it
94
+ # immediately. When there are no threads waiting to put, this method
95
+ # will block. As soon as a thread calls `pur` the exchange will occur
96
+ # and this method will return.
97
+ def take
98
+ mine = synchronize do
99
+ return NO_VALUE if ns_closed? && @putting.empty?
100
+
101
+ ref = Concurrent::AtomicReference.new(nil)
102
+ if @putting.empty?
103
+ @taking.push(ref)
104
+ else
105
+ putting = @putting.shift
106
+ ref.value = putting.value
107
+ putting.value = nil
108
+ end
109
+ ref
110
+ end
111
+ loop do
112
+ item = mine.value
113
+ return item if item
114
+ Thread.pass
115
+ end
116
+ end
117
+
118
+ # @!macro channel_buffer_poll
119
+ #
120
+ # Items can only be taken off the buffer when one or more threads are
121
+ # waiting to {#put} items onto the buffer. When there is a thread
122
+ # waiting to put an item this method will take the item and return
123
+ # it immediately. When there are no threads waiting to put or the
124
+ # buffer is closed, this method will return `NO_VALUE` immediately.
125
+ def poll
126
+ synchronize do
127
+ return NO_VALUE if @putting.empty?
128
+
129
+ putting = @putting.shift
130
+ value = putting.value
131
+ putting.value = nil
132
+ value
133
+ end
134
+ end
135
+
136
+ # @!macro channel_buffer_next
137
+ #
138
+ # Items can only be taken from the buffer when one or more threads are
139
+ # waiting to {#put} items onto the buffer. This method exhibits the
140
+ # same blocking behavior as {#take}.
141
+ #
142
+ # @see {#take}
143
+ def next
144
+ item = take
145
+ more = synchronize { !@putting.empty? }
146
+ return item, more
147
+ end
148
+ end
149
+ end
150
+ end
151
+ end