atomic-ruby 0.3.1 → 0.4.0
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/CHANGELOG.md +10 -0
- data/README.md +41 -22
- data/lib/atomic-ruby/atomic_boolean.rb +5 -5
- data/lib/atomic-ruby/atomic_count_down_latch.rb +34 -0
- data/lib/atomic-ruby/atomic_ruby.bundle +0 -0
- data/lib/atomic-ruby/atomic_thread_pool.rb +7 -5
- data/lib/atomic-ruby/version.rb +1 -1
- data/lib/atomic-ruby.rb +2 -0
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 21542683125e7a193b4500ff05f3ee74249db2e3b16e71a3b10b46ff53a52d4b
|
4
|
+
data.tar.gz: 609c1f8e8edb35ad1ad049a33c515a74522803b64a592ed49ac8666d8c06a8ba
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8a3e15dee614e4455540272dba417192800fe85d41acdd0ff30babe8681f1d768b408a38fcf8acba878054fab96c94e085516299670b1643c591cc617a165919
|
7
|
+
data.tar.gz: ae7b2e2e3a03019471d741a995d95a83f2e52eeb36137942b14cea55e8e76f8fd48ae734acbad5c2ac1a98a2fc8c88ddf5192ff6a2598f53555d7c08bfcbce9e
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,15 @@
|
|
1
1
|
## [Unreleased]
|
2
2
|
|
3
|
+
## [0.4.0] - 2025-07-06
|
4
|
+
|
5
|
+
- Revert "Fix `AtomicThreadPool#<<` shutdown check race condition"
|
6
|
+
- Add `:name` to `AtomicThreadPool` initializer
|
7
|
+
- Add `AtomicCountDownLatch`
|
8
|
+
|
9
|
+
## [0.3.2] - 2025-06-14
|
10
|
+
|
11
|
+
- Fix `AtomicThreadPool#<<` shutdown check race condition
|
12
|
+
|
3
13
|
## [0.3.1] - 2025-06-08
|
4
14
|
|
5
15
|
- Fix current queue being mutated in `AtomicThreadPool#<<`
|
data/README.md
CHANGED
@@ -169,6 +169,7 @@ results = []
|
|
169
169
|
results << result
|
170
170
|
end
|
171
171
|
|
172
|
+
puts "\n"
|
172
173
|
puts "ruby version: #{RUBY_DESCRIPTION}"
|
173
174
|
puts "concurrent-ruby version: #{Concurrent::VERSION}"
|
174
175
|
puts "atomic-ruby version: #{AtomicRuby::VERSION}"
|
@@ -189,7 +190,7 @@ puts "Atomic Ruby Atomic Bank Account: #{results[2].real.round(6)} seconds"
|
|
189
190
|
|
190
191
|
ruby version: ruby 3.4.4 (2025-05-14 revision a38531fd3f) +YJIT +PRISM [arm64-darwin24]
|
191
192
|
concurrent-ruby version: 1.3.5
|
192
|
-
atomic-ruby version: 0.
|
193
|
+
atomic-ruby version: 0.4.0
|
193
194
|
|
194
195
|
Balances:
|
195
196
|
Synchronized Bank Account Balance: 975
|
@@ -197,9 +198,9 @@ Concurrent Ruby Atomic Bank Account Balance: 975
|
|
197
198
|
Atomic Ruby Atomic Bank Account Balance: 975
|
198
199
|
|
199
200
|
Benchmark Results:
|
200
|
-
Synchronized Bank Account: 5.
|
201
|
-
Concurrent Ruby Atomic Bank Account: 5.
|
202
|
-
Atomic Ruby Atomic Bank Account: 5.
|
201
|
+
Synchronized Bank Account: 5.110062 seconds
|
202
|
+
Concurrent Ruby Atomic Bank Account: 5.107966 seconds
|
203
|
+
Atomic Ruby Atomic Bank Account: 5.107739 seconds
|
203
204
|
```
|
204
205
|
|
205
206
|
</details>
|
@@ -215,6 +216,23 @@ require "benchmark/ips"
|
|
215
216
|
require "concurrent-ruby"
|
216
217
|
require_relative "../lib/atomic-ruby"
|
217
218
|
|
219
|
+
module Benchmark
|
220
|
+
module IPS
|
221
|
+
class Job
|
222
|
+
class StreamReport
|
223
|
+
def start_warming
|
224
|
+
@out.puts "\n"
|
225
|
+
@out.puts "ruby version: #{RUBY_DESCRIPTION}"
|
226
|
+
@out.puts "concurrent-ruby version: #{Concurrent::VERSION}"
|
227
|
+
@out.puts "atomic-ruby version: #{AtomicRuby::VERSION}"
|
228
|
+
@out.puts "\n"
|
229
|
+
@out.puts "Warming up --------------------------------------"
|
230
|
+
end
|
231
|
+
end
|
232
|
+
end
|
233
|
+
end
|
234
|
+
end
|
235
|
+
|
218
236
|
Benchmark.ips do |x|
|
219
237
|
x.report("Synchronized Boolean Toggle") do
|
220
238
|
boolean = false
|
@@ -261,26 +279,29 @@ end
|
|
261
279
|
```
|
262
280
|
> bundle exec rake compile && bundle exec ruby examples/atomic_boolean_benchmark.rb
|
263
281
|
|
264
|
-
ruby 3.4.4 (2025-05-14 revision a38531fd3f) +YJIT +PRISM [arm64-darwin24]
|
282
|
+
ruby version: ruby 3.4.4 (2025-05-14 revision a38531fd3f) +YJIT +PRISM [arm64-darwin24]
|
283
|
+
concurrent-ruby version: 1.3.5
|
284
|
+
atomic-ruby version: 0.4.0
|
285
|
+
|
265
286
|
Warming up --------------------------------------
|
266
287
|
Synchronized Boolean Toggle
|
267
|
-
|
288
|
+
102.000 i/100ms
|
268
289
|
Concurrent Ruby Atomic Boolean Toggle
|
269
|
-
58.000 i/100ms
|
270
|
-
Atomic Ruby Atomic Boolean Toggle
|
271
290
|
88.000 i/100ms
|
291
|
+
Atomic Ruby Atomic Boolean Toggle
|
292
|
+
109.000 i/100ms
|
272
293
|
Calculating -------------------------------------
|
273
294
|
Synchronized Boolean Toggle
|
274
|
-
|
295
|
+
1.062k (± 2.5%) i/s (941.81 μs/i) - 5.406k in 5.094827s
|
275
296
|
Concurrent Ruby Atomic Boolean Toggle
|
276
|
-
|
297
|
+
981.495 (± 3.5%) i/s (1.02 ms/i) - 4.928k in 5.027167s
|
277
298
|
Atomic Ruby Atomic Boolean Toggle
|
278
|
-
|
299
|
+
1.274k (± 1.5%) i/s (784.70 μs/i) - 6.431k in 5.047458s
|
279
300
|
|
280
301
|
Comparison:
|
281
|
-
Atomic Ruby Atomic Boolean Toggle:
|
282
|
-
Synchronized Boolean Toggle:
|
283
|
-
Concurrent Ruby Atomic Boolean Toggle:
|
302
|
+
Atomic Ruby Atomic Boolean Toggle: 1274.4 i/s
|
303
|
+
Synchronized Boolean Toggle: 1061.8 i/s - 1.20x slower
|
304
|
+
Concurrent Ruby Atomic Boolean Toggle: 981.5 i/s - 1.30x slower
|
284
305
|
```
|
285
306
|
|
286
307
|
</details>
|
@@ -315,17 +336,15 @@ results = []
|
|
315
336
|
pool << -> { 1_000_000.times.map(&:itself).sum }
|
316
337
|
end
|
317
338
|
|
318
|
-
# concurrent-ruby does not wait for threads to die on shutdown
|
319
|
-
threads = if idx == 0
|
320
|
-
pool.instance_variable_get(:@pool).map { |worker| worker.instance_variable_get(:@thread) }
|
321
|
-
end
|
322
339
|
pool.shutdown
|
323
|
-
threads
|
340
|
+
# concurrent-ruby's #shutdown does not wait for threads to terminate
|
341
|
+
pool.wait_for_termination if idx == 0
|
324
342
|
end
|
325
343
|
|
326
344
|
results << result
|
327
345
|
end
|
328
346
|
|
347
|
+
puts "\n"
|
329
348
|
puts "ruby version: #{RUBY_DESCRIPTION}"
|
330
349
|
puts "concurrent-ruby version: #{Concurrent::VERSION}"
|
331
350
|
puts "atomic-ruby version: #{AtomicRuby::VERSION}"
|
@@ -340,11 +359,11 @@ puts "Atomic Ruby Atomic Thread Pool: #{results[1].real.round(6)} seconds"
|
|
340
359
|
|
341
360
|
ruby version: ruby 3.4.4 (2025-05-14 revision a38531fd3f) +YJIT +PRISM [arm64-darwin24]
|
342
361
|
concurrent-ruby version: 1.3.5
|
343
|
-
atomic-ruby version: 0.
|
362
|
+
atomic-ruby version: 0.4.0
|
344
363
|
|
345
364
|
Benchmark Results:
|
346
|
-
Concurrent Ruby Thread Pool: 5.
|
347
|
-
Atomic Ruby Atomic Thread Pool: 4.
|
365
|
+
Concurrent Ruby Thread Pool: 5.02207 seconds
|
366
|
+
Atomic Ruby Atomic Thread Pool: 4.503302 seconds
|
348
367
|
```
|
349
368
|
|
350
369
|
</details>
|
@@ -11,11 +11,11 @@ module AtomicRuby
|
|
11
11
|
raise InvalidBooleanError, "expected boolean to be a `TrueClass` or `FalseClass`, got #{value.class}"
|
12
12
|
end
|
13
13
|
|
14
|
-
@
|
14
|
+
@boolean = Atom.new(value)
|
15
15
|
end
|
16
16
|
|
17
17
|
def value
|
18
|
-
@
|
18
|
+
@boolean.value
|
19
19
|
end
|
20
20
|
|
21
21
|
def true?
|
@@ -27,15 +27,15 @@ module AtomicRuby
|
|
27
27
|
end
|
28
28
|
|
29
29
|
def make_true
|
30
|
-
@
|
30
|
+
@boolean.swap { true }
|
31
31
|
end
|
32
32
|
|
33
33
|
def make_false
|
34
|
-
@
|
34
|
+
@boolean.swap { false }
|
35
35
|
end
|
36
36
|
|
37
37
|
def toggle
|
38
|
-
@
|
38
|
+
@boolean.swap { |current_value| !current_value }
|
39
39
|
end
|
40
40
|
end
|
41
41
|
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "atom"
|
4
|
+
|
5
|
+
module AtomicRuby
|
6
|
+
class AtomicCountDownLatch
|
7
|
+
class InvalidCountError < StandardError; end
|
8
|
+
class AlreadyCountedDownError < StandardError; end
|
9
|
+
|
10
|
+
def initialize(count)
|
11
|
+
unless count.is_a?(Integer)
|
12
|
+
raise InvalidCountError, "expected count to be an `Integer`, got #{count.class}"
|
13
|
+
end
|
14
|
+
|
15
|
+
@count = Atom.new(count)
|
16
|
+
end
|
17
|
+
|
18
|
+
def count
|
19
|
+
@count.value
|
20
|
+
end
|
21
|
+
|
22
|
+
def count_down
|
23
|
+
unless @count.value > 0
|
24
|
+
raise AlreadyCountedDownError, "count has already reached zero"
|
25
|
+
end
|
26
|
+
|
27
|
+
@count.swap { |current_value| current_value - 1 }
|
28
|
+
end
|
29
|
+
|
30
|
+
def wait
|
31
|
+
Thread.pass while @count.value > 0
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
Binary file
|
@@ -8,8 +8,9 @@ module AtomicRuby
|
|
8
8
|
class UnsupportedWorkTypeError < StandardError; end
|
9
9
|
class InvalidWorkQueueingError < StandardError; end
|
10
10
|
|
11
|
-
def initialize(size:)
|
11
|
+
def initialize(size:, name: nil)
|
12
12
|
@size = size
|
13
|
+
@name = name
|
13
14
|
@queue = Atom.new([])
|
14
15
|
@threads = []
|
15
16
|
@started_threads = Atom.new(0)
|
@@ -50,8 +51,9 @@ module AtomicRuby
|
|
50
51
|
def start
|
51
52
|
@threads = @size.times.map do |num|
|
52
53
|
Thread.new(num) do |idx|
|
53
|
-
|
54
|
-
|
54
|
+
thread_name = String.new("AtomicRuby::AtomicThreadPool thread #{idx}")
|
55
|
+
thread_name << " for #{@name}" if @name
|
56
|
+
Thread.current.name = thread_name
|
55
57
|
|
56
58
|
@started_threads.swap { |current_count| current_count + 1 }
|
57
59
|
|
@@ -63,7 +65,7 @@ module AtomicRuby
|
|
63
65
|
begin
|
64
66
|
work.call
|
65
67
|
rescue => err
|
66
|
-
puts "#{
|
68
|
+
puts "#{thread_name} rescued:"
|
67
69
|
puts "#{err.class}: #{err.message}"
|
68
70
|
puts err.backtrace.join("\n")
|
69
71
|
end
|
@@ -80,7 +82,7 @@ module AtomicRuby
|
|
80
82
|
end
|
81
83
|
end
|
82
84
|
|
83
|
-
|
85
|
+
Thread.pass until @started_threads.value == @size
|
84
86
|
end
|
85
87
|
end
|
86
88
|
end
|
data/lib/atomic-ruby/version.rb
CHANGED
data/lib/atomic-ruby.rb
CHANGED
@@ -3,7 +3,9 @@
|
|
3
3
|
require_relative "atomic-ruby/version"
|
4
4
|
require_relative "atomic-ruby/atomic_ruby"
|
5
5
|
require_relative "atomic-ruby/atom"
|
6
|
+
require_relative "atomic-ruby/atomic_boolean"
|
6
7
|
require_relative "atomic-ruby/atomic_thread_pool"
|
8
|
+
require_relative "atomic-ruby/atomic_count_down_latch"
|
7
9
|
|
8
10
|
module AtomicRuby
|
9
11
|
class Error < StandardError; end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: atomic-ruby
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Joshua Young
|
@@ -27,6 +27,7 @@ files:
|
|
27
27
|
- lib/atomic-ruby.rb
|
28
28
|
- lib/atomic-ruby/atom.rb
|
29
29
|
- lib/atomic-ruby/atomic_boolean.rb
|
30
|
+
- lib/atomic-ruby/atomic_count_down_latch.rb
|
30
31
|
- lib/atomic-ruby/atomic_ruby.bundle
|
31
32
|
- lib/atomic-ruby/atomic_thread_pool.rb
|
32
33
|
- lib/atomic-ruby/version.rb
|
@@ -50,7 +51,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
50
51
|
- !ruby/object:Gem::Version
|
51
52
|
version: '0'
|
52
53
|
requirements: []
|
53
|
-
rubygems_version: 3.
|
54
|
+
rubygems_version: 3.7.0.dev
|
54
55
|
specification_version: 4
|
55
56
|
summary: Atomic primitives for Ruby
|
56
57
|
test_files: []
|