atomic-ruby 0.3.2 → 0.5.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: 7e47f4b67bd3d4dcaecbd560ed61b8dc4c86c24882b52279a5d0e92fd6de666a
4
- data.tar.gz: 2fb299af89ccc871b7c8b3d6ef18f15924e98b90ad196143c0ea9fa3cac76419
3
+ metadata.gz: 10b5f273dd24628d2bf10a095cb875647bc922fc36697528c472053c61b2822c
4
+ data.tar.gz: b1b45d921decbc9bd2f927b8b6ed11a8de9e36ca1acacd4dde411953992ffb3a
5
5
  SHA512:
6
- metadata.gz: fb201628eedea540bea2a8436daf0bb9f9db3cf2f0cd1caa5d27a54af840b992474cf4e08496a52c16f963ab311cd14f3796977f6777140c648e38cd283a6679
7
- data.tar.gz: fe346ae21ffa67e8ed82048a1b79fe2eada3f101e2b812112eef689139f924e01b420b6e0d56f37ca2c795e597bd86631e79f8d93fa5d32f50a77ef7eb1f9734
6
+ metadata.gz: c5ed7e71b51fa5b45e7031c7a08399f0158c588f3fe5b64844f7511fd959ebe7735fcc042d61b84c05729b961a133e7fa12a84663fe2e791b79fe3d02d6319f1
7
+ data.tar.gz: cd538c8c88fa9aa636ae0c0c69063b8288cde1af7b533c958c76096a7ecede7967ab45159ca04d010d507e084b8b13c38aa225d2ba675b146d2c7e8c473d147f
data/CHANGELOG.md CHANGED
@@ -1,5 +1,15 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [0.5.0] - 2025-07-17
4
+
5
+ - Add shortcut aliases for `AtomicRuby` namespaced classes
6
+
7
+ ## [0.4.0] - 2025-07-06
8
+
9
+ - Revert "Fix `AtomicThreadPool#<<` shutdown check race condition"
10
+ - Add `:name` to `AtomicThreadPool` initializer
11
+ - Add `AtomicCountDownLatch`
12
+
3
13
  ## [0.3.2] - 2025-06-14
4
14
 
5
15
  - Fix `AtomicThreadPool#<<` shutdown check race condition
data/README.md CHANGED
@@ -21,12 +21,12 @@ gem install atomic-ruby
21
21
 
22
22
  ## Usage
23
23
 
24
- `AtomicRuby::Atom`:
24
+ `Atom`:
25
25
 
26
26
  ```ruby
27
27
  require "atomic-ruby"
28
28
 
29
- atom = AtomicRuby::Atom.new(0)
29
+ atom = Atom.new(0)
30
30
  p atom.value #=> 0
31
31
  atom.swap { |current_value| current_value + 1 }
32
32
  p atom.value #=> 1
@@ -34,12 +34,12 @@ atom.swap { |current_value| current_value + 1 }
34
34
  p atom.value #=> 2
35
35
  ```
36
36
 
37
- `AtomicRuby::AtomicBoolean`:
37
+ `AtomicBoolean`:
38
38
 
39
39
  ```ruby
40
40
  require "atomic-ruby"
41
41
 
42
- atom = AtomicRuby::AtomicBoolean.new(false)
42
+ atom = AtomicBoolean.new(false)
43
43
  p atom.value #=> false
44
44
  p atom.false? #=> true
45
45
  p atom.true? #=> false
@@ -49,14 +49,14 @@ atom.toggle
49
49
  p atom.false? #=> true
50
50
  ```
51
51
 
52
- `AtomicRuby::AtomicThreadPool`:
52
+ `AtomicThreadPool`:
53
53
 
54
54
  ```ruby
55
55
  require "atomic-ruby"
56
56
 
57
57
  results = []
58
58
 
59
- pool = AtomicRuby::AtomicThreadPool.new(size: 4)
59
+ pool = AtomicThreadPool.new(size: 4)
60
60
  p pool.length #=> 4
61
61
 
62
62
  10.times do |idx|
@@ -78,11 +78,30 @@ p results #=> [8, 7, 10, 9, 6, 5, 3, 4, 2, 1]
78
78
  p results.sort #=> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
79
79
  ```
80
80
 
81
+ `AtomicCountDownLatch`:
82
+
83
+ ```ruby
84
+ require "atomic-ruby"
85
+
86
+ latch = AtomicCountDownLatch.new(3)
87
+ p latch.count #=> 3
88
+
89
+ threads = 3.times.map do
90
+ Thread.new do
91
+ sleep(rand(5))
92
+ latch.count_down
93
+ end
94
+ end
95
+
96
+ latch.wait
97
+ p latch.count #=> 0
98
+ ```
99
+
81
100
  ## Benchmarks
82
101
 
83
102
  <details>
84
103
 
85
- <summary>AtomicRuby::Atom</summary>
104
+ <summary>Atom</summary>
86
105
 
87
106
  <br>
88
107
 
@@ -128,7 +147,7 @@ end
128
147
 
129
148
  class AtomicRubyAtomicBankAccount
130
149
  def initialize(balance)
131
- @balance = AtomicRuby::Atom.new(balance)
150
+ @balance = Atom.new(balance)
132
151
  end
133
152
 
134
153
  def balance
@@ -190,7 +209,7 @@ puts "Atomic Ruby Atomic Bank Account: #{results[2].real.round(6)} seconds"
190
209
 
191
210
  ruby version: ruby 3.4.4 (2025-05-14 revision a38531fd3f) +YJIT +PRISM [arm64-darwin24]
192
211
  concurrent-ruby version: 1.3.5
193
- atomic-ruby version: 0.3.2
212
+ atomic-ruby version: 0.4.0
194
213
 
195
214
  Balances:
196
215
  Synchronized Bank Account Balance: 975
@@ -198,16 +217,16 @@ Concurrent Ruby Atomic Bank Account Balance: 975
198
217
  Atomic Ruby Atomic Bank Account Balance: 975
199
218
 
200
219
  Benchmark Results:
201
- Synchronized Bank Account: 5.130792 seconds
202
- Concurrent Ruby Atomic Bank Account: 5.121400 seconds
203
- Atomic Ruby Atomic Bank Account: 5.119191 seconds
220
+ Synchronized Bank Account: 5.110062 seconds
221
+ Concurrent Ruby Atomic Bank Account: 5.107966 seconds
222
+ Atomic Ruby Atomic Bank Account: 5.107739 seconds
204
223
  ```
205
224
 
206
225
  </details>
207
226
 
208
227
  <details>
209
228
 
210
- <summary>AtomicRuby::AtomicBoolean</summary>
229
+ <summary>AtomicBoolean</summary>
211
230
 
212
231
  ```ruby
213
232
  # frozen_string_literal: true
@@ -262,7 +281,7 @@ Benchmark.ips do |x|
262
281
  end
263
282
 
264
283
  x.report("Atomic Ruby Atomic Boolean Toggle") do
265
- boolean = AtomicRuby::AtomicBoolean.new(false)
284
+ boolean = AtomicBoolean.new(false)
266
285
  20.times.map do
267
286
  Thread.new do
268
287
  100.times do
@@ -281,34 +300,34 @@ end
281
300
 
282
301
  ruby version: ruby 3.4.4 (2025-05-14 revision a38531fd3f) +YJIT +PRISM [arm64-darwin24]
283
302
  concurrent-ruby version: 1.3.5
284
- atomic-ruby version: 0.3.2
303
+ atomic-ruby version: 0.4.0
285
304
 
286
305
  Warming up --------------------------------------
287
306
  Synchronized Boolean Toggle
288
- 87.000 i/100ms
307
+ 102.000 i/100ms
289
308
  Concurrent Ruby Atomic Boolean Toggle
290
- 81.000 i/100ms
309
+ 88.000 i/100ms
291
310
  Atomic Ruby Atomic Boolean Toggle
292
- 92.000 i/100ms
311
+ 109.000 i/100ms
293
312
  Calculating -------------------------------------
294
313
  Synchronized Boolean Toggle
295
- 969.205 (± 5.8%) i/s (1.03 ms/i) - 4.872k in 5.043520s
314
+ 1.062k2.5%) i/s (941.81 μs/i) - 5.406k in 5.094827s
296
315
  Concurrent Ruby Atomic Boolean Toggle
297
- 885.546 (± 5.2%) i/s (1.13 ms/i) - 4.455k in 5.044282s
316
+ 981.4953.5%) i/s (1.02 ms/i) - 4.928k in 5.027167s
298
317
  Atomic Ruby Atomic Boolean Toggle
299
- 1.154k (± 5.3%) i/s (866.65 μs/i) - 5.796k in 5.037272s
318
+ 1.274k1.5%) i/s (784.70 μs/i) - 6.431k in 5.047458s
300
319
 
301
320
  Comparison:
302
- Atomic Ruby Atomic Boolean Toggle: 1153.9 i/s
303
- Synchronized Boolean Toggle: 969.2 i/s - 1.19x slower
304
- Concurrent Ruby Atomic Boolean Toggle: 885.5 i/s - 1.30x slower
321
+ Atomic Ruby Atomic Boolean Toggle: 1274.4 i/s
322
+ Synchronized Boolean Toggle: 1061.8 i/s - 1.20x slower
323
+ Concurrent Ruby Atomic Boolean Toggle: 981.5 i/s - 1.30x slower
305
324
  ```
306
325
 
307
326
  </details>
308
327
 
309
328
  <details>
310
329
 
311
- <summary>AtomicRuby::AtomicThreadPool</summary>
330
+ <summary>AtomicThreadPool</summary>
312
331
 
313
332
  <br>
314
333
 
@@ -325,7 +344,7 @@ results = []
325
344
  result = Benchmark.measure do
326
345
  pool = case idx
327
346
  when 0 then Concurrent::FixedThreadPool.new(20)
328
- when 1 then AtomicRuby::AtomicThreadPool.new(size: 20)
347
+ when 1 then AtomicThreadPool.new(size: 20)
329
348
  end
330
349
 
331
350
  100.times do
@@ -359,11 +378,11 @@ puts "Atomic Ruby Atomic Thread Pool: #{results[1].real.round(6)} seconds"
359
378
 
360
379
  ruby version: ruby 3.4.4 (2025-05-14 revision a38531fd3f) +YJIT +PRISM [arm64-darwin24]
361
380
  concurrent-ruby version: 1.3.5
362
- atomic-ruby version: 0.3.2
381
+ atomic-ruby version: 0.4.0
363
382
 
364
383
  Benchmark Results:
365
- Concurrent Ruby Thread Pool: 5.032940 seconds
366
- Atomic Ruby Atomic Thread Pool: 4.508259 seconds
384
+ Concurrent Ruby Thread Pool: 5.02207 seconds
385
+ Atomic Ruby Atomic Thread Pool: 4.503302 seconds
367
386
  ```
368
387
 
369
388
  </details>
@@ -24,7 +24,7 @@ static void atomic_ruby_atom_compact(void *ptr) {
24
24
  }
25
25
 
26
26
  static const rb_data_type_t atomic_ruby_atom_type = {
27
- .wrap_struct_name = "AtomicRuby::Atom",
27
+ .wrap_struct_name = "Atom",
28
28
  .function = {
29
29
  .dmark = atomic_ruby_atom_mark,
30
30
  .dfree = atomic_ruby_atom_free,
@@ -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)
@@ -23,13 +24,11 @@ module AtomicRuby
23
24
  raise UnsupportedWorkTypeError, "expected work to be a `Proc`, got #{work.class}"
24
25
  end
25
26
 
26
- @queue.swap do |current_queue|
27
- if @stopping.true?
28
- raise InvalidWorkQueueingError, "cannot queue work during or after pool shutdown"
29
- end
30
-
31
- current_queue += [work]
27
+ if @stopping.true?
28
+ raise InvalidWorkQueueingError, "cannot queue work during or after pool shutdown"
32
29
  end
30
+
31
+ @queue.swap { |current_queue| current_queue += [work] }
33
32
  true
34
33
  end
35
34
 
@@ -52,8 +51,9 @@ module AtomicRuby
52
51
  def start
53
52
  @threads = @size.times.map do |num|
54
53
  Thread.new(num) do |idx|
55
- name = "AtomicRuby::AtomicThreadPool thread #{idx}"
56
- Thread.current.name = name
54
+ thread_name = String.new("AtomicThreadPool thread #{idx}")
55
+ thread_name << " for #{@name}" if @name
56
+ Thread.current.name = thread_name
57
57
 
58
58
  @started_threads.swap { |current_count| current_count + 1 }
59
59
 
@@ -65,7 +65,7 @@ module AtomicRuby
65
65
  begin
66
66
  work.call
67
67
  rescue => err
68
- puts "#{name} rescued:"
68
+ puts "#{thread_name} rescued:"
69
69
  puts "#{err.class}: #{err.message}"
70
70
  puts err.backtrace.join("\n")
71
71
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module AtomicRuby
4
- VERSION = "0.3.2"
4
+ VERSION = "0.5.0"
5
5
  end
data/lib/atomic-ruby.rb CHANGED
@@ -3,8 +3,11 @@
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
- module AtomicRuby
9
- class Error < StandardError; end
10
- end
10
+ Atom = AtomicRuby::Atom
11
+ AtomicBoolean = AtomicRuby::AtomicBoolean
12
+ AtomicThreadPool = AtomicRuby::AtomicThreadPool
13
+ AtomicCountDownLatch = AtomicRuby::AtomicCountDownLatch
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.2
4
+ version: 0.5.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.7.0.dev
54
+ rubygems_version: 3.6.9
54
55
  specification_version: 4
55
56
  summary: Atomic primitives for Ruby
56
57
  test_files: []