Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

thread safe set implementation #666

Merged
merged 3 commits into from Feb 24, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Expand Up @@ -67,6 +67,7 @@ Collection classes that were originally part of the (deprecated) `thread_safe` g

* [Array](http://ruby-concurrency.github.io/concurrent-ruby/Concurrent/Array.html) A thread-safe subclass of Ruby's standard [Array](http://ruby-doc.org/core-2.2.0/Array.html).
* [Hash](http://ruby-concurrency.github.io/concurrent-ruby/Concurrent/Hash.html) A thread-safe subclass of Ruby's standard [Hash](http://ruby-doc.org/core-2.2.0/Hash.html).
* [Set](http://ruby-concurrency.github.io/concurrent-ruby/Concurrent/Set.html) A thread-safe subclass of Ruby's standard [Set](http://ruby-doc.org/stdlib-2.4.0/libdoc/set/rdoc/Set.html).
* [Map](http://ruby-concurrency.github.io/concurrent-ruby/Concurrent/Map.html) A hash-like object that should have much better performance characteristics, especially under high concurrency, than `Concurrent::Hash`.
* [Tuple](http://ruby-concurrency.github.io/concurrent-ruby/Concurrent/Tuple.html) A fixed size array with volatile (synchronized, thread safe) getters/setters.

Expand Down
4 changes: 2 additions & 2 deletions lib/concurrent.rb
Expand Up @@ -12,6 +12,7 @@
require 'concurrent/atom'
require 'concurrent/array'
require 'concurrent/hash'
require 'concurrent/set'
require 'concurrent/map'
require 'concurrent/tuple'
require 'concurrent/async'
Expand Down Expand Up @@ -126,5 +127,4 @@
# * Exclude features that don't make sense in Ruby
# * Be small, lean, and loosely coupled
module Concurrent

end
end
47 changes: 47 additions & 0 deletions lib/concurrent/set.rb
@@ -0,0 +1,47 @@
require 'concurrent/utility/engine'
require 'concurrent/thread_safe/util'
require 'set'

module Concurrent
if Concurrent.on_cruby?

# Because MRI never runs code in parallel, the existing
# non-thread-safe structures should usually work fine.

# @!macro [attach] concurrent_Set
#
# A thread-safe subclass of Set. This version locks against the object
# itself for every method call, ensuring only one thread can be reading
# or writing at a time. This includes iteration methods like `#each`.
#
# @note `a += b` is **not** a **thread-safe** operation on
# `Concurrent::Set`. It reads Set `a`, then it creates new `Concurrent::Set`
# which is union of `a` and `b`, then it writes the union to `a`.
# The read and write are independent operations they do not form a single atomic
# operation therefore when two `+=` operations are executed concurrently updates
# may be lost. Use `#merge` instead.
#
# @see http://ruby-doc.org/stdlib-2.4.0/libdoc/set/rdoc/Set.html Ruby standard library `Set`
class Set < ::Set;
end

elsif Concurrent.on_jruby?
require 'jruby/synchronized'

# @!macro concurrent_Set
class Set < ::Set
include JRuby::Synchronized
end

elsif Concurrent.on_rbx? || Concurrent.on_truffle?
require 'monitor'
require 'concurrent/thread_safe/util/array_hash_rbx'

# @!macro concurrent_Set
class Set < ::Set
end

ThreadSafe::Util.make_synchronized_on_rbx Set
end
end

1 change: 1 addition & 0 deletions spec/concurrent/array_spec.rb
Expand Up @@ -13,6 +13,7 @@ module Concurrent
end
end
end.map(&:join)
expect(ary).to be_empty
end

describe '#slice' do
Expand Down
2 changes: 1 addition & 1 deletion spec/concurrent/channel_spec.rb
Expand Up @@ -611,7 +611,7 @@ module Concurrent
latch.wait(10)
expect(actual).to eq expected
end
end
end

context '.go_loop_via' do

Expand Down
5 changes: 3 additions & 2 deletions spec/concurrent/hash_spec.rb
Expand Up @@ -7,11 +7,12 @@ module Concurrent
Thread.new do
1000.times do |j|
hsh[i * 1000 + j] = i
hsh[i * 1000 + j]
hsh.delete(i * 1000 + j)
expect(hsh[i * 1000 + j]).to eq(i)
expect(hsh.delete(i * 1000 + j)).to eq(i)
end
end
end.map(&:join)
expect(hsh).to be_empty
end
end
end
20 changes: 20 additions & 0 deletions spec/concurrent/set_spec.rb
@@ -0,0 +1,20 @@
require 'set'
module Concurrent
RSpec.describe Set do
let!(:set) { described_class.new }

it 'concurrency' do
(1..THREADS).map do |i|
Thread.new do
1000.times do
v = i
set << v
expect(set).not_to be_empty
set.delete(v)
end
end
end.map(&:join)
expect(set).to be_empty
end
end
end