Either implementation will be a poor choice for some use cases (hence the bug reports about both).
Either implementation will be a poor choice for some use cases (hence the bug reports about both).
If I remember correctly, that should ensure the code is executed even if the block contains a break.
That said, I'd wager Ruby's strange way of handling breaks in blocks passed to functions is the real source of problems here.
However, a bigger problem is concurrent modification: What happens if your reject! predicate has a side effect which modifies the array? Ideally you'd receive a "ConcurrentModificationException" or the like.
iirc, Ruby's array doesn't expose the distinction between length and capacity, but internally it may decide to amortize the cost of such copies. For example, if reject only removes a small % of elements, it may choose not to shrink capacity.
(That's assuming you copy at all, instead of simply updating the reference.)
You have an "ensure" block (finally) protecting the code which handles the cases where dirty is true (moving all the remaining elements after the current max). Then you truncate the array.
It appears order is guaranteed to be maintained but it's not specified :)
Here's an example of how you'd manage the contention yourself:
my_array = [1, 2, 3]
my_array_lock = Mutex.new
Thread.new do
my_array_lock.synchronize do
my_array.reject! { |x| x % 2 == 0 }
end
end
Thread.new do
my_array_lock.synchronize do
my_array.push(4)
end
end
Thread.new do
my_array_lock.synchronize do
# Because we've serialised our operations on the array, there's no chance
# that we read the elements in an incorrect order
my_array.each do |x|
puts x
end
end
end