ratomic 0.2.0 → 0.2.1

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: 6fbef9f27ee3ebd3839fb95b3e3c4685aaa34be9c22f1314974726c5f38aada8
4
- data.tar.gz: 668591405949669d19687a678029ca07f46f155b3740f9a7d5fff599a5d8003c
3
+ metadata.gz: b4ca3b68838c5f869284756b01f00e56960cbb5dff8e640efc03682d10d28a50
4
+ data.tar.gz: 959794b19074d1ecfba5bdb08223ba372a6443fbc96fe833d0ce324aa3c52d5e
5
5
  SHA512:
6
- metadata.gz: e31111fffe0abc54d5e7c945c7bd618dc7b201a0b35c48c438dbe7cf3db414106c5638ac4680f67a018a55545f5f62d09e6efe79887577f9a8516b5d7d6569cc
7
- data.tar.gz: b46b875f77412cfbb3edba3ed07fd2087fb70ded07023ad091d32f911d4fa5b9af06fa42a1db770bc24d357683828cc0ec3df5f429ae177c45bc3df4f7ebc9fb
6
+ metadata.gz: 9697f964c65bdc3a951da84836ebadf947d370d74efd258015fb5401267acd6d78248de238f9da6db60edecccbe0a8bbd83a327e424d6fb6e74066ee60fe16de
7
+ data.tar.gz: 8bbbba4064050e577b23b48e8363653b5438ea157a68cffc607a53588e2785fdc9c2d5879d846593fa9f2757667df1df77784811c239b4f5b5d9cc684f00ae74
data/CHANGELOG.md CHANGED
@@ -1,5 +1,10 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [0.2.1] - 2026-06-04
4
+
5
+ - Fix `Ratomic::Queue` slot indexing for non-power-of-two capacities.
6
+ - Fix `Ratomic::Map#fetch_and_modify` to propagate block exceptions instead of panicking.
7
+
3
8
  ## [0.2.0] - 2026-06-04
4
9
 
5
10
  - Drop Ruby 3.x support and require Ruby 4.
data/README.md CHANGED
@@ -1,5 +1,12 @@
1
1
  # Ratomic
2
2
 
3
+ [![Gem Version](https://badge.fury.io/rb/ratomic.svg)](https://badge.fury.io/rb/ratomic)
4
+ [![CI](https://github.com/mperham/ratomic/workflows/CI/badge.svg)](https://github.com/mperham/ratomic/actions)
5
+ [![Coverage Status](https://codecov.io/gh/mperham/ratomic/branch/main/graph/badge.svg)](https://codecov.io/gh/mperham/ratomic)
6
+ [![Ruby Version](https://img.shields.io/badge/ruby-%3E%3D%204.0-ruby.svg)](https://www.ruby-lang.org/en/)
7
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
8
+
9
+
3
10
  Ratomic provides mutable data structures for use with Ruby's Ractors.
4
11
  This allows Ruby code to scale beyond the infamous GVL.
5
12
 
@@ -31,8 +38,6 @@ Install the gem and add to the application's Gemfile by executing:
31
38
  bundle add ratomic
32
39
  ```
33
40
 
34
- TODO: We have not released a gem yet.
35
-
36
41
  ## Usage
37
42
 
38
43
  Ratomic provides several useful Ractor-safe structures.
@@ -115,11 +115,22 @@ impl HashMap {
115
115
  }
116
116
 
117
117
  let proc = ruby.block_proc()?;
118
+ let mut error = None;
118
119
  rb_self.0.fetch_and_modify(value_to_raw(key), |value| {
119
- let result: Value = proc.call((unsafe { value_from_raw(value) },)).unwrap();
120
- value_to_raw(result)
120
+ match proc.call::<_, Value>((unsafe { value_from_raw(value) },)) {
121
+ Ok(result) => value_to_raw(result),
122
+ Err(err) => {
123
+ error = Some(err);
124
+ value
125
+ }
126
+ }
121
127
  });
122
- Ok(())
128
+
129
+ if let Some(err) = error {
130
+ Err(err)
131
+ } else {
132
+ Ok(())
133
+ }
123
134
  }
124
135
  }
125
136
 
@@ -15,7 +15,7 @@ unsafe impl Sync for QueueElement {}
15
15
 
16
16
  pub struct MpmcQueue {
17
17
  buffer: Vec<QueueElement>,
18
- buffer_mask: usize,
18
+ buffer_size: usize,
19
19
  enqueue_pos: AtomicUsize,
20
20
  dequeue_pos: AtomicUsize,
21
21
  gc_guard: GcGuard,
@@ -25,7 +25,7 @@ pub struct MpmcQueue {
25
25
 
26
26
  impl MpmcQueue {
27
27
  pub fn new(buffer_size: usize, default: VALUE) -> Self {
28
- let mut buffer = Vec::with_capacity(buffer_size.next_power_of_two());
28
+ let mut buffer = Vec::with_capacity(buffer_size);
29
29
  for i in 0..buffer_size {
30
30
  buffer.push(QueueElement {
31
31
  sequence: AtomicUsize::new(i),
@@ -42,7 +42,7 @@ impl MpmcQueue {
42
42
 
43
43
  Self {
44
44
  buffer,
45
- buffer_mask: buffer_size - 1,
45
+ buffer_size,
46
46
  enqueue_pos: AtomicUsize::new(0),
47
47
  dequeue_pos: AtomicUsize::new(0),
48
48
  gc_guard,
@@ -55,7 +55,7 @@ impl MpmcQueue {
55
55
  let mut cell;
56
56
  let mut pos = self.enqueue_pos.load(Ordering::Relaxed);
57
57
  loop {
58
- cell = &self.buffer[pos & self.buffer_mask];
58
+ cell = &self.buffer[pos % self.buffer_size];
59
59
  let seq = cell.sequence.load(Ordering::Acquire);
60
60
  let diff = seq as isize - pos as isize;
61
61
  if diff == 0 {
@@ -82,7 +82,7 @@ impl MpmcQueue {
82
82
  let mut cell;
83
83
  let mut pos = self.dequeue_pos.load(Ordering::Relaxed);
84
84
  loop {
85
- cell = &self.buffer[pos & self.buffer_mask];
85
+ cell = &self.buffer[pos % self.buffer_size];
86
86
  let seq = cell.sequence.load(Ordering::Acquire);
87
87
  let diff = seq as isize - (pos + 1) as isize;
88
88
  if diff == 0 {
@@ -102,7 +102,7 @@ impl MpmcQueue {
102
102
 
103
103
  let data = cell.data.get();
104
104
  cell.sequence
105
- .store(pos + self.buffer_mask + 1, Ordering::Release);
105
+ .store(pos + self.buffer_size, Ordering::Release);
106
106
  self.write_sem.post();
107
107
 
108
108
  #[cfg(feature = "simulation")]
@@ -131,7 +131,7 @@ impl MpmcQueue {
131
131
 
132
132
  pub fn peek(&self) -> Option<VALUE> {
133
133
  let pos = self.dequeue_pos.load(Ordering::Relaxed);
134
- let cell = &self.buffer[pos & self.buffer_mask];
134
+ let cell = &self.buffer[pos % self.buffer_size];
135
135
  let seq = cell.sequence.load(Ordering::Acquire);
136
136
  let diff = seq as isize - (pos + 1) as isize;
137
137
  if diff == 0 {
data/lib/ratomic/pool.rb CHANGED
@@ -73,6 +73,20 @@ module Ratomic
73
73
  nil
74
74
  end
75
75
 
76
+ # Stop the private coordinator Ractor.
77
+ #
78
+ # This is primarily useful for tests and short-lived scripts. A closed pool
79
+ # should not be used for further checkout/checkin operations.
80
+ #
81
+ # @return [nil]
82
+ def close
83
+ @control << [:shutdown]
84
+ @control.value
85
+ nil
86
+ rescue Ractor::ClosedError, Ractor::Error
87
+ nil
88
+ end
89
+
76
90
  # Checkout an object, yield it, then move it back to the pool.
77
91
  #
78
92
  # This is the preferred API because it guarantees checkin through an ensure
@@ -101,7 +115,7 @@ module Ratomic
101
115
 
102
116
  loop do
103
117
  command, *args = Ractor.receive
104
- handle_command(command, args, available, waiting)
118
+ break if handle_command(command, args, available, waiting) == :shutdown
105
119
  end
106
120
  end
107
121
  private_class_method :run_control_loop
@@ -114,6 +128,8 @@ module Ratomic
114
128
  handle_checkin(args.fetch(0), available, waiting)
115
129
  when :cancel
116
130
  waiting.delete(args.fetch(0))
131
+ when :shutdown
132
+ :shutdown
117
133
  end
118
134
  end
119
135
  private_class_method :handle_command
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Ratomic
4
- VERSION = "0.2.0"
4
+ VERSION = "0.2.1"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ratomic
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mike Perham