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

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.
@@ -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