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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: fc55b63bd9de85a395232f89184e97aa80e08e8e082981f2b17012fd1b56b5c0
4
- data.tar.gz: 25cffc0cbb45f1d145d3b99380decc91a39ef82415833781bed40ba2583286a8
3
+ metadata.gz: 21542683125e7a193b4500ff05f3ee74249db2e3b16e71a3b10b46ff53a52d4b
4
+ data.tar.gz: 609c1f8e8edb35ad1ad049a33c515a74522803b64a592ed49ac8666d8c06a8ba
5
5
  SHA512:
6
- metadata.gz: 1420ff50010eec4386301619d4866c1e96aec8f91936e464b2ed56276607fd6dfaf88d0cc5582ee255c06a15928a2b1390f1a2e18ceb443ee0d74fa190a36ee9
7
- data.tar.gz: 80ceb6dd2348ea38bbd8b63223fc7ab27a6d36026d0e028fc0aef60dd9f92885b29adb5fa997b551b54c9aadbaca24fdd71a4f7b1d94337f3a3887955c108a5b
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.3.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.125638 seconds
201
- Concurrent Ruby Atomic Bank Account: 5.114936 seconds
202
- Atomic Ruby Atomic Bank Account: 5.108171 seconds
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
- 83.000 i/100ms
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
- 775.5526.2%) i/s (1.29 ms/i) - 3.901k in 5.051649s
295
+ 1.062k2.5%) i/s (941.81 μs/i) - 5.406k in 5.094827s
275
296
  Concurrent Ruby Atomic Boolean Toggle
276
- 741.655 (± 3.5%) i/s (1.35 ms/i) - 3.712k in 5.011183s
297
+ 981.495 (± 3.5%) i/s (1.02 ms/i) - 4.928k in 5.027167s
277
298
  Atomic Ruby Atomic Boolean Toggle
278
- 881.9162.8%) i/s (1.13 ms/i) - 4.488k in 5.092910s
299
+ 1.274k1.5%) i/s (784.70 μs/i) - 6.431k in 5.047458s
279
300
 
280
301
  Comparison:
281
- Atomic Ruby Atomic Boolean Toggle: 881.9 i/s
282
- Synchronized Boolean Toggle: 775.6 i/s - 1.14x slower
283
- Concurrent Ruby Atomic Boolean Toggle: 741.7 i/s - 1.19x slower
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&.each(&:join)
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.3.0
362
+ atomic-ruby version: 0.4.0
344
363
 
345
364
  Benchmark Results:
346
- Concurrent Ruby Thread Pool: 5.136456 seconds
347
- Atomic Ruby Atomic Thread Pool: 4.700981 seconds
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
- @atom = Atom.new(value)
14
+ @boolean = Atom.new(value)
15
15
  end
16
16
 
17
17
  def value
18
- @atom.value
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
- @atom.swap { true }
30
+ @boolean.swap { true }
31
31
  end
32
32
 
33
33
  def make_false
34
- @atom.swap { false }
34
+ @boolean.swap { false }
35
35
  end
36
36
 
37
37
  def toggle
38
- @atom.swap { |current_value| !current_value }
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
- name = "AtomicRuby::AtomicThreadPool thread #{idx}"
54
- Thread.current.name = name
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 "#{name} rescued:"
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
- sleep(0.001) until @started_threads.value == @size
85
+ Thread.pass until @started_threads.value == @size
84
86
  end
85
87
  end
86
88
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module AtomicRuby
4
- VERSION = "0.3.1"
4
+ VERSION = "0.4.0"
5
5
  end
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.3.1
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.6.7
54
+ rubygems_version: 3.7.0.dev
54
55
  specification_version: 4
55
56
  summary: Atomic primitives for Ruby
56
57
  test_files: []