Skip to content

Commit 50ed4a5

Browse files
authored
Merge pull request #666 from haridutt12/master
thread safe set implementation
2 parents da5c4a9 + cef13b9 commit 50ed4a5

File tree

7 files changed

+75
-5
lines changed

7 files changed

+75
-5
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ Collection classes that were originally part of the (deprecated) `thread_safe` g
6767

6868
* [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).
6969
* [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).
70+
* [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).
7071
* [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`.
7172
* [Tuple](http://ruby-concurrency.github.io/concurrent-ruby/Concurrent/Tuple.html) A fixed size array with volatile (synchronized, thread safe) getters/setters.
7273

lib/concurrent.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
require 'concurrent/atom'
1313
require 'concurrent/array'
1414
require 'concurrent/hash'
15+
require 'concurrent/set'
1516
require 'concurrent/map'
1617
require 'concurrent/tuple'
1718
require 'concurrent/async'
@@ -126,5 +127,4 @@
126127
# * Exclude features that don't make sense in Ruby
127128
# * Be small, lean, and loosely coupled
128129
module Concurrent
129-
130-
end
130+
end

lib/concurrent/set.rb

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
require 'concurrent/utility/engine'
2+
require 'concurrent/thread_safe/util'
3+
require 'set'
4+
5+
module Concurrent
6+
if Concurrent.on_cruby?
7+
8+
# Because MRI never runs code in parallel, the existing
9+
# non-thread-safe structures should usually work fine.
10+
11+
# @!macro [attach] concurrent_Set
12+
#
13+
# A thread-safe subclass of Set. This version locks against the object
14+
# itself for every method call, ensuring only one thread can be reading
15+
# or writing at a time. This includes iteration methods like `#each`.
16+
#
17+
# @note `a += b` is **not** a **thread-safe** operation on
18+
# `Concurrent::Set`. It reads Set `a`, then it creates new `Concurrent::Set`
19+
# which is union of `a` and `b`, then it writes the union to `a`.
20+
# The read and write are independent operations they do not form a single atomic
21+
# operation therefore when two `+=` operations are executed concurrently updates
22+
# may be lost. Use `#merge` instead.
23+
#
24+
# @see http://ruby-doc.org/stdlib-2.4.0/libdoc/set/rdoc/Set.html Ruby standard library `Set`
25+
class Set < ::Set;
26+
end
27+
28+
elsif Concurrent.on_jruby?
29+
require 'jruby/synchronized'
30+
31+
# @!macro concurrent_Set
32+
class Set < ::Set
33+
include JRuby::Synchronized
34+
end
35+
36+
elsif Concurrent.on_rbx? || Concurrent.on_truffle?
37+
require 'monitor'
38+
require 'concurrent/thread_safe/util/array_hash_rbx'
39+
40+
# @!macro concurrent_Set
41+
class Set < ::Set
42+
end
43+
44+
ThreadSafe::Util.make_synchronized_on_rbx Set
45+
end
46+
end
47+

spec/concurrent/array_spec.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ module Concurrent
1313
end
1414
end
1515
end.map(&:join)
16+
expect(ary).to be_empty
1617
end
1718

1819
describe '#slice' do

spec/concurrent/channel_spec.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -611,7 +611,7 @@ module Concurrent
611611
latch.wait(10)
612612
expect(actual).to eq expected
613613
end
614-
end
614+
end
615615

616616
context '.go_loop_via' do
617617

spec/concurrent/hash_spec.rb

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,12 @@ module Concurrent
77
Thread.new do
88
1000.times do |j|
99
hsh[i * 1000 + j] = i
10-
hsh[i * 1000 + j]
11-
hsh.delete(i * 1000 + j)
10+
expect(hsh[i * 1000 + j]).to eq(i)
11+
expect(hsh.delete(i * 1000 + j)).to eq(i)
1212
end
1313
end
1414
end.map(&:join)
15+
expect(hsh).to be_empty
1516
end
1617
end
1718
end

spec/concurrent/set_spec.rb

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
require 'set'
2+
module Concurrent
3+
RSpec.describe Set do
4+
let!(:set) { described_class.new }
5+
6+
it 'concurrency' do
7+
(1..THREADS).map do |i|
8+
Thread.new do
9+
1000.times do
10+
v = i
11+
set << v
12+
expect(set).not_to be_empty
13+
set.delete(v)
14+
end
15+
end
16+
end.map(&:join)
17+
expect(set).to be_empty
18+
end
19+
end
20+
end

0 commit comments

Comments
 (0)