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.
- checksums.yaml +4 -4
- data/README.md +7 -5
- data/lib/concurrent/channel.rb +272 -6
- data/lib/concurrent/channel/buffer.rb +9 -0
- data/lib/concurrent/channel/buffer/base.rb +197 -0
- data/lib/concurrent/channel/buffer/buffered.rb +127 -0
- data/lib/concurrent/channel/buffer/dropping.rb +53 -0
- data/lib/concurrent/channel/buffer/sliding.rb +54 -0
- data/lib/concurrent/channel/buffer/ticker.rb +80 -0
- data/lib/concurrent/channel/buffer/timer.rb +78 -0
- data/lib/concurrent/channel/buffer/unbuffered.rb +151 -0
- data/lib/concurrent/channel/selector.rb +70 -0
- data/lib/concurrent/channel/selector/after_clause.rb +27 -0
- data/lib/concurrent/channel/selector/default_clause.rb +19 -0
- data/lib/concurrent/channel/selector/error_clause.rb +21 -0
- data/lib/concurrent/channel/selector/put_clause.rb +26 -0
- data/lib/concurrent/channel/selector/take_clause.rb +24 -0
- data/lib/concurrent/channel/tick.rb +55 -0
- data/lib/concurrent/edge/future.rb +23 -41
- data/lib/concurrent/edge/lock_free_linked_set.rb +2 -1
- metadata +20 -11
- data/lib/concurrent/channel/blocking_ring_buffer.rb +0 -82
- data/lib/concurrent/channel/buffered_channel.rb +0 -89
- data/lib/concurrent/channel/channel.rb +0 -19
- data/lib/concurrent/channel/ring_buffer.rb +0 -65
- data/lib/concurrent/channel/unbuffered_channel.rb +0 -39
- data/lib/concurrent/channel/waitable_list.rb +0 -48
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a4d13aad3839f1bfa159af7cc02464800dd062fb
|
4
|
+
data.tar.gz: 936d530a0a9368d1947339a2d83a6d9f36c979fd
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f0fd846fc8ab69642ac313f12e298547ec1958d98c50466ceae1df09df7d047eccea65426fb3c04cb5d4433c933cc73f2ffb60ad81bd4f36a97418c11e58b37d
|
7
|
+
data.tar.gz: d3a92fbc3ebfb09256ba159b4016c0b0caaa371c762ccfb47d01789a09cd8d906f7052f6fa37f2b5c6d7057aff1561f384fe5e032cf25347663a3e93885b0c41
|
data/README.md
CHANGED
@@ -130,8 +130,10 @@ be obeyed though. Features developed in `concurrent-ruby-edge` are expected to m
|
|
130
130
|
`Promise`, `IVar`, `Event`, `dataflow`, `Delay`, and `TimerTask` into a single framework. It extensively uses the
|
131
131
|
new synchronization layer to make all the features **non-blocking** and **lock-free**, with the exception of obviously blocking
|
132
132
|
operations like `#wait`, `#value`. It also offers better performance.
|
133
|
-
* [Channel](http://ruby-concurrency.github.io/concurrent-ruby/Concurrent/Channel.html):
|
134
|
-
Communicating Sequential Processes (CSP).
|
133
|
+
* [Channel](http://ruby-concurrency.github.io/concurrent-ruby/Concurrent/Edge/Channel.html):
|
134
|
+
Communicating Sequential Processes ([CSP](https://en.wikipedia.org/wiki/Communicating_sequential_processes)).
|
135
|
+
Functionally equivalent to Go [channels](https://tour.golang.org/concurrency/2) with additional
|
136
|
+
inspiration from Clojure [core.async](https://clojure.github.io/core.async/).
|
135
137
|
* [LazyRegister](http://ruby-concurrency.github.io/concurrent-ruby/Concurrent/LazyRegister.html)
|
136
138
|
* [AtomicMarkableReference](http://ruby-concurrency.github.io/concurrent-ruby/Concurrent/Edge/AtomicMarkableReference.html)
|
137
139
|
* [LockFreeLinkedSet](http://ruby-concurrency.github.io/concurrent-ruby/Concurrent/Edge/LockFreeLinkedSet.html)
|
@@ -142,10 +144,10 @@ be obeyed though. Features developed in `concurrent-ruby-edge` are expected to m
|
|
142
144
|
*Why are these not in core?*
|
143
145
|
|
144
146
|
- **Actor** - Partial documentation and tests; depends on new future/promise framework; stability is good.
|
145
|
-
- **
|
146
|
-
- **
|
147
|
+
- **Channel** - Brand new implementation; partial documentation and tests; stability is good.
|
148
|
+
- **Future/Promise Framework** - API changes; partial documentation and tests; stability is good.
|
147
149
|
- **LazyRegister** - Missing documentation and tests.
|
148
|
-
- **AtomicMarkableReference, LockFreeLinkedSet, LockFreeStack** - Need real world battle testing
|
150
|
+
- **AtomicMarkableReference, LockFreeLinkedSet, LockFreeStack** - Need real world battle testing.
|
149
151
|
|
150
152
|
## Usage
|
151
153
|
|
data/lib/concurrent/channel.rb
CHANGED
@@ -1,6 +1,272 @@
|
|
1
|
-
require 'concurrent/channel/
|
2
|
-
require 'concurrent/channel/
|
3
|
-
|
4
|
-
require 'concurrent/
|
5
|
-
require 'concurrent/
|
6
|
-
|
1
|
+
require 'concurrent/channel/buffer'
|
2
|
+
require 'concurrent/channel/selector'
|
3
|
+
|
4
|
+
require 'concurrent/maybe'
|
5
|
+
require 'concurrent/executor/cached_thread_pool'
|
6
|
+
|
7
|
+
module Concurrent
|
8
|
+
|
9
|
+
# {include:file:doc/channel.md}
|
10
|
+
class Channel
|
11
|
+
include Enumerable
|
12
|
+
|
13
|
+
GOROUTINES = Concurrent::CachedThreadPool.new
|
14
|
+
private_constant :GOROUTINES
|
15
|
+
|
16
|
+
BUFFER_TYPES = {
|
17
|
+
unbuffered: Buffer::Unbuffered,
|
18
|
+
buffered: Buffer::Buffered,
|
19
|
+
dropping: Buffer::Dropping,
|
20
|
+
sliding: Buffer::Sliding
|
21
|
+
}.freeze
|
22
|
+
private_constant :BUFFER_TYPES
|
23
|
+
|
24
|
+
DEFAULT_VALIDATOR = ->(value){ true }
|
25
|
+
private_constant :DEFAULT_VALIDATOR
|
26
|
+
|
27
|
+
Error = Class.new(StandardError)
|
28
|
+
|
29
|
+
class ValidationError < Error
|
30
|
+
def initialize(message = nil)
|
31
|
+
message ||= 'invalid value'
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def initialize(opts = {})
|
36
|
+
# undocumented -- for internal use only
|
37
|
+
if opts.is_a? Buffer::Base
|
38
|
+
@buffer = opts
|
39
|
+
return
|
40
|
+
end
|
41
|
+
|
42
|
+
size = opts[:size]
|
43
|
+
buffer = opts[:buffer]
|
44
|
+
|
45
|
+
if size && buffer == :unbuffered
|
46
|
+
raise ArgumentError.new('unbuffered channels cannot have a size')
|
47
|
+
elsif size.nil? && buffer.nil?
|
48
|
+
@buffer = BUFFER_TYPES[:unbuffered].new
|
49
|
+
elsif size == 0 && buffer == :buffered
|
50
|
+
@buffer = BUFFER_TYPES[:unbuffered].new
|
51
|
+
elsif buffer == :unbuffered
|
52
|
+
@buffer = BUFFER_TYPES[:unbuffered].new
|
53
|
+
elsif size.nil? || size < 1
|
54
|
+
raise ArgumentError.new('size must be at least 1 for this buffer type')
|
55
|
+
else
|
56
|
+
buffer ||= :buffered
|
57
|
+
@buffer = BUFFER_TYPES[buffer].new(size)
|
58
|
+
end
|
59
|
+
|
60
|
+
@validator = opts.fetch(:validator, DEFAULT_VALIDATOR)
|
61
|
+
end
|
62
|
+
|
63
|
+
def size
|
64
|
+
@buffer.size
|
65
|
+
end
|
66
|
+
alias_method :capacity, :size
|
67
|
+
|
68
|
+
def put(item)
|
69
|
+
return false unless validate(item, false, false)
|
70
|
+
do_put(item)
|
71
|
+
end
|
72
|
+
alias_method :send, :put
|
73
|
+
alias_method :<<, :put
|
74
|
+
|
75
|
+
def put!(item)
|
76
|
+
validate(item, false, true)
|
77
|
+
ok = do_put(item)
|
78
|
+
raise Error if !ok
|
79
|
+
ok
|
80
|
+
end
|
81
|
+
|
82
|
+
def put?(item)
|
83
|
+
if !validate(item, true, false)
|
84
|
+
Concurrent::Maybe.nothing('invalid value')
|
85
|
+
elsif do_put(item)
|
86
|
+
Concurrent::Maybe.just(true)
|
87
|
+
else
|
88
|
+
Concurrent::Maybe.nothing
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
def offer(item)
|
93
|
+
return false unless validate(item, false, false)
|
94
|
+
do_offer(item)
|
95
|
+
end
|
96
|
+
|
97
|
+
def offer!(item)
|
98
|
+
validate(item, false, true)
|
99
|
+
ok = do_offer(item)
|
100
|
+
raise Error if !ok
|
101
|
+
ok
|
102
|
+
end
|
103
|
+
|
104
|
+
def offer?(item)
|
105
|
+
if !validate(item, true, false)
|
106
|
+
Concurrent::Maybe.nothing('invalid value')
|
107
|
+
elsif do_offer(item)
|
108
|
+
Concurrent::Maybe.just(true)
|
109
|
+
else
|
110
|
+
Concurrent::Maybe.nothing
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
def take
|
115
|
+
item, _ = self.next
|
116
|
+
item
|
117
|
+
end
|
118
|
+
alias_method :receive, :take
|
119
|
+
alias_method :~, :take
|
120
|
+
|
121
|
+
def take!
|
122
|
+
item, _ = do_next
|
123
|
+
raise Error if item == Buffer::NO_VALUE
|
124
|
+
item
|
125
|
+
end
|
126
|
+
|
127
|
+
def take?
|
128
|
+
item, _ = self.next?
|
129
|
+
item
|
130
|
+
end
|
131
|
+
|
132
|
+
#
|
133
|
+
# @example
|
134
|
+
#
|
135
|
+
# jobs = Channel.new
|
136
|
+
#
|
137
|
+
# Channel.go do
|
138
|
+
# loop do
|
139
|
+
# j, more = jobs.next
|
140
|
+
# if more
|
141
|
+
# print "received job #{j}\n"
|
142
|
+
# else
|
143
|
+
# print "received all jobs\n"
|
144
|
+
# break
|
145
|
+
# end
|
146
|
+
# end
|
147
|
+
# end
|
148
|
+
def next
|
149
|
+
item, more = do_next
|
150
|
+
item = nil if item == Buffer::NO_VALUE
|
151
|
+
return item, more
|
152
|
+
end
|
153
|
+
|
154
|
+
def next?
|
155
|
+
item, more = do_next
|
156
|
+
item = if item == Buffer::NO_VALUE
|
157
|
+
Concurrent::Maybe.nothing
|
158
|
+
else
|
159
|
+
Concurrent::Maybe.just(item)
|
160
|
+
end
|
161
|
+
return item, more
|
162
|
+
end
|
163
|
+
|
164
|
+
def poll
|
165
|
+
(item = do_poll) == Buffer::NO_VALUE ? nil : item
|
166
|
+
end
|
167
|
+
|
168
|
+
def poll!
|
169
|
+
item = do_poll
|
170
|
+
raise Error if item == Buffer::NO_VALUE
|
171
|
+
item
|
172
|
+
end
|
173
|
+
|
174
|
+
def poll?
|
175
|
+
if (item = do_poll) == Buffer::NO_VALUE
|
176
|
+
Concurrent::Maybe.nothing
|
177
|
+
else
|
178
|
+
Concurrent::Maybe.just(item)
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
def each
|
183
|
+
raise ArgumentError.new('no block given') unless block_given?
|
184
|
+
loop do
|
185
|
+
item, more = do_next
|
186
|
+
if item != Buffer::NO_VALUE
|
187
|
+
yield(item)
|
188
|
+
elsif !more
|
189
|
+
break
|
190
|
+
end
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
def close
|
195
|
+
@buffer.close
|
196
|
+
end
|
197
|
+
alias_method :stop, :close
|
198
|
+
|
199
|
+
class << self
|
200
|
+
def timer(seconds)
|
201
|
+
Channel.new(Buffer::Timer.new(seconds))
|
202
|
+
end
|
203
|
+
alias_method :after, :timer
|
204
|
+
|
205
|
+
def ticker(interval)
|
206
|
+
Channel.new(Buffer::Ticker.new(interval))
|
207
|
+
end
|
208
|
+
alias_method :tick, :ticker
|
209
|
+
|
210
|
+
def select(*args)
|
211
|
+
raise ArgumentError.new('no block given') unless block_given?
|
212
|
+
selector = Selector.new
|
213
|
+
yield(selector, *args)
|
214
|
+
selector.execute
|
215
|
+
end
|
216
|
+
alias_method :alt, :select
|
217
|
+
|
218
|
+
def go(*args, &block)
|
219
|
+
go_via(GOROUTINES, *args, &block)
|
220
|
+
end
|
221
|
+
|
222
|
+
def go_via(executor, *args, &block)
|
223
|
+
raise ArgumentError.new('no block given') unless block_given?
|
224
|
+
executor.post(*args, &block)
|
225
|
+
end
|
226
|
+
|
227
|
+
def go_loop(*args, &block)
|
228
|
+
go_loop_via(GOROUTINES, *args, &block)
|
229
|
+
end
|
230
|
+
|
231
|
+
def go_loop_via(executor, *args, &block)
|
232
|
+
raise ArgumentError.new('no block given') unless block_given?
|
233
|
+
executor.post(block, *args) do
|
234
|
+
loop do
|
235
|
+
break unless block.call(*args)
|
236
|
+
end
|
237
|
+
end
|
238
|
+
end
|
239
|
+
end
|
240
|
+
|
241
|
+
private
|
242
|
+
|
243
|
+
def validate(value, allow_nil, raise_error)
|
244
|
+
if !allow_nil && value.nil?
|
245
|
+
raise_error ? raise(ValidationError.new('nil is not a valid value')) : false
|
246
|
+
elsif !@validator.call(value)
|
247
|
+
raise_error ? raise(ValidationError) : false
|
248
|
+
else
|
249
|
+
true
|
250
|
+
end
|
251
|
+
rescue => ex
|
252
|
+
# the validator raised an exception
|
253
|
+
return raise_error ? raise(ex) : false
|
254
|
+
end
|
255
|
+
|
256
|
+
def do_put(item)
|
257
|
+
@buffer.put(item)
|
258
|
+
end
|
259
|
+
|
260
|
+
def do_offer(item)
|
261
|
+
@buffer.offer(item)
|
262
|
+
end
|
263
|
+
|
264
|
+
def do_next
|
265
|
+
@buffer.next
|
266
|
+
end
|
267
|
+
|
268
|
+
def do_poll
|
269
|
+
@buffer.poll
|
270
|
+
end
|
271
|
+
end
|
272
|
+
end
|
@@ -0,0 +1,9 @@
|
|
1
|
+
require 'concurrent/channel/buffer/base'
|
2
|
+
|
3
|
+
require 'concurrent/channel/buffer/buffered'
|
4
|
+
require 'concurrent/channel/buffer/dropping'
|
5
|
+
require 'concurrent/channel/buffer/sliding'
|
6
|
+
require 'concurrent/channel/buffer/unbuffered'
|
7
|
+
|
8
|
+
require 'concurrent/channel/buffer/ticker'
|
9
|
+
require 'concurrent/channel/buffer/timer'
|
@@ -0,0 +1,197 @@
|
|
1
|
+
require 'concurrent/synchronization/lockable_object'
|
2
|
+
|
3
|
+
module Concurrent
|
4
|
+
class Channel
|
5
|
+
module Buffer
|
6
|
+
|
7
|
+
# Placeholder for when a buffer slot contains no value.
|
8
|
+
NO_VALUE = Object.new
|
9
|
+
|
10
|
+
# Abstract base class for all Channel buffers.
|
11
|
+
#
|
12
|
+
# {Concurrent::Channel} objects maintain an internal, queue-like
|
13
|
+
# object called a buffer. It's the storage bin for values put onto or
|
14
|
+
# taken from the channel. Different buffer types have different
|
15
|
+
# characteristics. Subsequently, the behavior of any given channel is
|
16
|
+
# highly dependent uping the type of its buffer. This is the base class
|
17
|
+
# which defines the common buffer interface. Any class intended to be
|
18
|
+
# used as a channel buffer should extend this class.
|
19
|
+
class Base < Synchronization::LockableObject
|
20
|
+
|
21
|
+
# @!macro [attach] channel_buffer_size_reader
|
22
|
+
#
|
23
|
+
# The maximum number of values which can be {#put} onto the buffer
|
24
|
+
# it becomes full.
|
25
|
+
attr_reader :size
|
26
|
+
alias_method :capacity, :size
|
27
|
+
|
28
|
+
# @!macro [attach] channel_buffer_initialize
|
29
|
+
#
|
30
|
+
# Creates a new buffer.
|
31
|
+
def initialize
|
32
|
+
super()
|
33
|
+
synchronize do
|
34
|
+
@closed = false
|
35
|
+
@size = 0
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
# @!macro [attach] channel_buffer_blocking_question
|
40
|
+
#
|
41
|
+
# Predicate indicating if this buffer will block {#put} operations
|
42
|
+
# once it reaches its maximum capacity.
|
43
|
+
#
|
44
|
+
# @return [Boolean] true if this buffer blocks else false
|
45
|
+
def blocking?
|
46
|
+
true
|
47
|
+
end
|
48
|
+
|
49
|
+
# @!macro [attach] channel_buffer_empty_question
|
50
|
+
#
|
51
|
+
# Predicate indicating if the buffer is empty.
|
52
|
+
#
|
53
|
+
# @return [Boolean] true if this buffer is empty else false
|
54
|
+
#
|
55
|
+
# @raise [NotImplementedError] until overridden in a subclass.
|
56
|
+
def empty?
|
57
|
+
raise NotImplementedError
|
58
|
+
end
|
59
|
+
|
60
|
+
# @!macro [attach] channel_buffer_full_question
|
61
|
+
#
|
62
|
+
# Predicate indicating if the buffer is full.
|
63
|
+
#
|
64
|
+
# @return [Boolean] true if this buffer is full else false
|
65
|
+
#
|
66
|
+
# @raise [NotImplementedError] until overridden in a subclass.
|
67
|
+
def full?
|
68
|
+
raise NotImplementedError
|
69
|
+
end
|
70
|
+
|
71
|
+
# @!macro [attach] channel_buffer_put
|
72
|
+
#
|
73
|
+
# Put an item onto the buffer if possible. If the buffer is open
|
74
|
+
# but not able to accept the item the calling thread will block
|
75
|
+
# until the item can be put onto the buffer.
|
76
|
+
#
|
77
|
+
# @param [Object] item the item/value to put onto the buffer.
|
78
|
+
# @return [Boolean] true if the item was added to the buffer else
|
79
|
+
# false (always false when closed).
|
80
|
+
#
|
81
|
+
# @raise [NotImplementedError] until overridden in a subclass.
|
82
|
+
def put(item)
|
83
|
+
raise NotImplementedError
|
84
|
+
end
|
85
|
+
|
86
|
+
# @!macro [attach] channel_buffer_offer
|
87
|
+
#
|
88
|
+
# Put an item onto the buffer is possible. If the buffer is open but
|
89
|
+
# unable to add an item, probably due to being full, the method will
|
90
|
+
# return immediately. Similarly, the method will return immediately
|
91
|
+
# when the buffer is closed. A return value of `false` does not
|
92
|
+
# necessarily indicate that the buffer is closed, just that the item
|
93
|
+
# could not be added.
|
94
|
+
#
|
95
|
+
# @param [Object] item the item/value to put onto the buffer.
|
96
|
+
# @return [Boolean] true if the item was added to the buffer else
|
97
|
+
# false (always false when closed).
|
98
|
+
#
|
99
|
+
# @raise [NotImplementedError] until overridden in a subclass.
|
100
|
+
def offer(item)
|
101
|
+
raise NotImplementedError
|
102
|
+
end
|
103
|
+
|
104
|
+
# @!macro [attach] channel_buffer_take
|
105
|
+
#
|
106
|
+
# Take an item from the buffer if one is available. If the buffer
|
107
|
+
# is open and no item is available the calling thread will block
|
108
|
+
# until an item is available. If the buffer is closed but items
|
109
|
+
# are available the remaining items can still be taken. Once the
|
110
|
+
# buffer closes, no remaining items can be taken.
|
111
|
+
#
|
112
|
+
# @return [Object] the item removed from the buffer; `NO_VALUE` once
|
113
|
+
# the buffer has closed.
|
114
|
+
#
|
115
|
+
# @raise [NotImplementedError] until overridden in a subclass.
|
116
|
+
def take
|
117
|
+
raise NotImplementedError
|
118
|
+
end
|
119
|
+
|
120
|
+
# @!macro [attach] channel_buffer_next
|
121
|
+
#
|
122
|
+
# Take the next item from the buffer and also return a boolean
|
123
|
+
# indicating if subsequent items can be taken. Used for iterating
|
124
|
+
# over a buffer until it is closed and empty.
|
125
|
+
#
|
126
|
+
# If the buffer is open but no items remain the calling thread will
|
127
|
+
# block until an item is available. The second of the two return
|
128
|
+
# values, a boolean, will always be `true` when the buffer is open.
|
129
|
+
# When the buffer is closed but more items remain the second return
|
130
|
+
# value will also be `true`. When the buffer is closed and the last
|
131
|
+
# item is taken the second return value will be `false`. When the
|
132
|
+
# buffer is both closed and empty the first return value will be
|
133
|
+
# `NO_VALUE` and the second return value will be `false`.
|
134
|
+
# be `false` when the buffer is both closed and empty.
|
135
|
+
#
|
136
|
+
# Note that when multiple threads access the same channel a race
|
137
|
+
# condition can occur when using this method. A call to `next` from
|
138
|
+
# one thread may return `true` for the second return value, but
|
139
|
+
# another thread may `take` the last value before the original
|
140
|
+
# thread makes another call. Code which iterates over a channel
|
141
|
+
# must be programmed to properly handle these race conditions.
|
142
|
+
#
|
143
|
+
# @return [Object, Boolean] the first return value will be the item
|
144
|
+
# taken from the buffer and the second return value will be a
|
145
|
+
# boolean indicating whether or not more items remain.
|
146
|
+
#
|
147
|
+
# @raise [NotImplementedError] until overridden in a subclass.
|
148
|
+
def next
|
149
|
+
raise NotImplementedError
|
150
|
+
end
|
151
|
+
|
152
|
+
# @!macro [attach] channel_buffer_poll
|
153
|
+
#
|
154
|
+
# Take the next item from the buffer if one is available else return
|
155
|
+
# immediately. Failing to return a value does not necessarily
|
156
|
+
# indicate that the buffer is closed, just that it is empty.
|
157
|
+
#
|
158
|
+
# @return [Object] the next item from the buffer or `NO_VALUE` if
|
159
|
+
# the buffer is empty.
|
160
|
+
#
|
161
|
+
# @raise [NotImplementedError] until overridden in a subclass.
|
162
|
+
def poll
|
163
|
+
raise NotImplementedError
|
164
|
+
end
|
165
|
+
|
166
|
+
# @!macro [attach] channel_buffer_close
|
167
|
+
#
|
168
|
+
# Close the buffer, preventing new items from being added. Once a
|
169
|
+
# buffer is closed it cannot be opened again.
|
170
|
+
#
|
171
|
+
# @return [Boolean] true if the buffer was open and successfully
|
172
|
+
# closed else false.
|
173
|
+
def close
|
174
|
+
synchronize do
|
175
|
+
@closed ? false : @closed = true
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
# @!macro [attach] channel_buffer_closed_question
|
180
|
+
#
|
181
|
+
# Predicate indicating is this buffer closed.
|
182
|
+
#
|
183
|
+
# @return [Boolea] true when closed else false.
|
184
|
+
def closed?
|
185
|
+
synchronize { ns_closed? }
|
186
|
+
end
|
187
|
+
|
188
|
+
private
|
189
|
+
|
190
|
+
# @!macro channel_buffer_closed_question
|
191
|
+
def ns_closed?
|
192
|
+
@closed
|
193
|
+
end
|
194
|
+
end
|
195
|
+
end
|
196
|
+
end
|
197
|
+
end
|