io-buffer-atomic 0.1.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: aaf99978c44e1771b3e54d16c21ca881a9a1959a672f09992fcf2ea89c3057f0
4
+ data.tar.gz: 3225f264e8b69a7b94157896dadc1d2e14257fcb40cb9240d10f50712f8434dd
5
+ SHA512:
6
+ metadata.gz: 6cc7de2b83ceefefda9a2cab4b2f1b68d5c2b1ce0e74dbdcb4b1db523ab3d8fe4a19bee66e2f123d2b893d9282743a9fd61023002f1cf0c40f5cc620697a3af0
7
+ data.tar.gz: 9409c51c60fa6904342db119e934de234e5a46d4b1ef6a70728acf9643aca021551b1a5acff796e22b0c38a195b51bfb1b87c9ea8772bca706d9c3f5d7ff7bb9
data/agents.md ADDED
@@ -0,0 +1,421 @@
1
+ # Agent
2
+
3
+ ## Context
4
+
5
+ This section provides links to documentation from installed packages. It is automatically generated and may be updated by running `bake agent:context:install`.
6
+
7
+ **Important:** Before performing any code, documentation, or analysis tasks, always read and apply the full content of any relevant documentation referenced in the following sections. These context files contain authoritative standards and best practices for documentation, code style, and project-specific workflows. **Do not proceed with any actions until you have read and incorporated the guidance from relevant context files.**
8
+
9
+ **Setup Instructions:** If the referenced files are not present or if dependencies have been updated, run `bake agent:context:install` to install the latest context files.
10
+
11
+ ### agent-context
12
+
13
+ Install and manage context files from Ruby gems.
14
+
15
+ #### [Getting Started](.context/agent-context/getting-started.md)
16
+
17
+ This guide explains how to use `agent-context`, a tool for discovering and installing contextual information from Ruby gems to help AI agents.
18
+
19
+ ### async
20
+
21
+ A concurrency framework for Ruby.
22
+
23
+ #### [Getting Started](.context/async/getting-started.md)
24
+
25
+ This guide shows how to add async to your project and run code asynchronously.
26
+
27
+ #### [Scheduler](.context/async/scheduler.md)
28
+
29
+ This guide gives an overview of how the scheduler is implemented.
30
+
31
+ #### [Tasks](.context/async/tasks.md)
32
+
33
+ This guide explains how asynchronous tasks work and how to use them.
34
+
35
+ #### [Best Practices](.context/async/best-practices.md)
36
+
37
+ This guide gives an overview of best practices for using Async.
38
+
39
+ #### [Debugging](.context/async/debugging.md)
40
+
41
+ This guide explains how to debug issues with programs that use Async.
42
+
43
+ #### [Thread safety](.context/async/thread-safety.md)
44
+
45
+ This guide explains thread safety in Ruby, focusing on fibers and threads, common pitfalls, and best practices to avoid problems like data corruption, race conditions, and deadlocks.
46
+
47
+ ### async-container-supervisor
48
+
49
+ A supervisor for managing multiple container processes.
50
+
51
+ #### [Getting Started](.context/async-container-supervisor/getting-started.md)
52
+
53
+ This guide explains how to get started with `async-container-supervisor` to supervise and monitor worker processes in your Ruby applications.
54
+
55
+ #### [Memory Monitor](.context/async-container-supervisor/memory-monitor.md)
56
+
57
+ This guide explains how to use the <code class="language-ruby">Async::Container::Supervisor::MemoryMonitor</code> to detect and restart workers that exceed memory limits or develop memory leaks.
58
+
59
+ #### [Process Monitor](.context/async-container-supervisor/process-monitor.md)
60
+
61
+ This guide explains how to use the <code class="language-ruby">Async::Container::Supervisor::ProcessMonitor</code> to log CPU and memory metrics for your worker processes.
62
+
63
+ ### async-http
64
+
65
+ A HTTP client and server library.
66
+
67
+ #### [Getting Started](.context/async-http/getting-started.md)
68
+
69
+ This guide explains how to get started with `Async::HTTP`.
70
+
71
+ #### [Testing](.context/async-http/testing.md)
72
+
73
+ This guide explains how to use `Async::HTTP` clients and servers in your tests.
74
+
75
+ ### async-http-cache
76
+
77
+ Standard-compliant cache for async-http.
78
+
79
+ #### [Getting Started](.context/async-http-cache/getting-started.md)
80
+
81
+ This guide explains how to get started with `async-http-cache`, a cache middleware for `Async::HTTP` clients and servers.
82
+
83
+ ### async-service
84
+
85
+ A service layer for Async.
86
+
87
+ #### [Getting Started](.context/async-service/getting-started.md)
88
+
89
+ This guide explains how to get started with `async-service` to create and run services in Ruby.
90
+
91
+ #### [Container Policies](.context/async-service/policies.md)
92
+
93
+ This guide explains how to configure container policies for your services and understand the default failure handling behavior.
94
+
95
+ #### [Service Architecture](.context/async-service/service-architecture.md)
96
+
97
+ This guide explains the key architectural components of `async-service` and how they work together to provide a clean separation of concerns.
98
+
99
+ #### [Best Practices](.context/async-service/best-practices.md)
100
+
101
+ This guide outlines recommended patterns and practices for building robust, maintainable services with `async-service`.
102
+
103
+ #### [Deployment](.context/async-service/deployment.md)
104
+
105
+ This guide explains how to deploy `async-service` applications using systemd and Kubernetes. We'll use a simple example service to demonstrate deployment configurations.
106
+
107
+ ### bake
108
+
109
+ A replacement for rake with a simpler syntax.
110
+
111
+ #### [Getting Started](.context/bake/getting-started.md)
112
+
113
+ This guide gives a general overview of `bake` and how to use it.
114
+
115
+ #### [Command Line Interface](.context/bake/command-line-interface.md)
116
+
117
+ The `bake` command is broken up into two main functions: `list` and `call`.
118
+
119
+ #### [Project Integration](.context/bake/project-integration.md)
120
+
121
+ This guide explains how to add `bake` to a Ruby project.
122
+
123
+ #### [Gem Integration](.context/bake/gem-integration.md)
124
+
125
+ This guide explains how to add `bake` to a Ruby gem and export standardised tasks for use by other gems and projects.
126
+
127
+ #### [Input and Output](.context/bake/input-and-output.md)
128
+
129
+ `bake` has built in tasks for reading input and writing output in different formats. While this can be useful for general processing, there are some limitations, notably that rich object representations like `json` and `yaml` often don't support stream processing.
130
+
131
+ ### bake-gem
132
+
133
+ Release management for Ruby gems.
134
+
135
+ #### [Getting Started](.context/bake-gem/getting-started.md)
136
+
137
+ This guide explains how to use `bake-gem` to release gems safely and efficiently.
138
+
139
+ ### bake-releases
140
+
141
+ Releases document management.
142
+
143
+ #### [Getting Started](.context/bake-releases/getting-started.md)
144
+
145
+ This guide explains how to use `bake-releases` to manage release documentation for your Ruby gem.
146
+
147
+ ### covered
148
+
149
+ A modern approach to code coverage.
150
+
151
+ #### [Getting Started](.context/covered/getting-started.md)
152
+
153
+ This guide explains how to get started with `covered` and integrate it with your test suite.
154
+
155
+ #### [Configuration](.context/covered/configuration.md)
156
+
157
+ This guide will help you to configure covered for your project's specific requirements.
158
+
159
+ ### decode
160
+
161
+ Code analysis for documentation generation.
162
+
163
+ #### [Getting Started](.context/decode/getting-started.md)
164
+
165
+ This guide explains how to use `decode` for source code analysis.
166
+
167
+ #### [Documentation Coverage](.context/decode/documentation-coverage.md)
168
+
169
+ This guide explains how to test and monitor documentation coverage in your Ruby projects using the Decode gem's built-in bake tasks.
170
+
171
+ #### [Ruby Documentation](.context/decode/ruby-documentation.md)
172
+
173
+ This guide covers documentation practices and pragmas supported by the Decode gem for documenting Ruby code. These pragmas provide structured documentation that can be parsed and used to generate API documentation and achieve complete documentation coverage.
174
+
175
+ ### falcon
176
+
177
+ A fast, asynchronous, rack-compatible web server.
178
+
179
+ #### [Getting Started](.context/falcon/getting-started.md)
180
+
181
+ This guide gives an overview of how to use Falcon for running Ruby web applications.
182
+
183
+ #### [Rails Integration](.context/falcon/rails-integration.md)
184
+
185
+ This guide explains how to host Rails applications with Falcon.
186
+
187
+ #### [Deployment](.context/falcon/deployment.md)
188
+
189
+ This guide explains how to deploy applications using the Falcon web server. It covers the recommended deployment methods, configuration options, and examples for different environments, including systemd and kubernetes.
190
+
191
+ #### [Performance Tuning](.context/falcon/performance-tuning.md)
192
+
193
+ This guide explains the performance characteristics of Falcon.
194
+
195
+ #### [WebSockets](.context/falcon/websockets.md)
196
+
197
+ This guide explains how to use WebSockets with Falcon.
198
+
199
+ #### [Interim Responses](.context/falcon/interim-responses.md)
200
+
201
+ This guide explains how to use interim responses in Falcon to send early hints to the client.
202
+
203
+ #### [How It Works](.context/falcon/how-it-works.md)
204
+
205
+ This guide gives an overview of how Falcon handles an incoming web request.
206
+
207
+ ### io-endpoint
208
+
209
+ Provides a separation of concerns interface for IO endpoints.
210
+
211
+ #### [Getting Started](.context/io-endpoint/getting-started.md)
212
+
213
+ This guide explains how to get started with `io-endpoint`, a library that provides a separation of concerns interface for network I/O endpoints.
214
+
215
+ #### [Named Endpoints](.context/io-endpoint/named-endpoints.md)
216
+
217
+ This guide explains how to use `IO::Endpoint::NamedEndpoints` to manage multiple endpoints by name, enabling scenarios like running the same application on different protocols or ports.
218
+
219
+ ### io-event
220
+
221
+ An event loop.
222
+
223
+ #### [Getting Started](.context/io-event/getting-started.md)
224
+
225
+ This guide explains how to use `io-event` for non-blocking IO.
226
+
227
+ ### markly
228
+
229
+ CommonMark parser and renderer. Written in C, wrapped in Ruby.
230
+
231
+ #### [Getting Started](.context/markly/getting-started.md)
232
+
233
+ This guide explains now to install and use Markly.
234
+
235
+ #### [Abstract Syntax Tree](.context/markly/abstract-syntax-tree.md)
236
+
237
+ This guide explains how to use Markly's abstract syntax tree (AST) to parse and manipulate Markdown documents.
238
+
239
+ #### [Headings](.context/markly/headings.md)
240
+
241
+ This guide explains how to work with headings in Markly, including extracting them for navigation and handling duplicate heading text.
242
+
243
+ ### memory
244
+
245
+ Memory profiling routines for Ruby 2.3+
246
+
247
+ #### [Getting Started](.context/memory/getting-started.md)
248
+
249
+ This guide explains how to get started with `memory`, a Ruby gem for profiling memory allocations in your applications.
250
+
251
+ ### metrics
252
+
253
+ Application metrics and instrumentation.
254
+
255
+ #### [Getting Started](.context/metrics/getting-started.md)
256
+
257
+ This guide explains how to use `metrics` for capturing run-time metrics.
258
+
259
+ #### [Capture](.context/metrics/capture.md)
260
+
261
+ This guide explains how to use `metrics` for exporting metric definitions from your application.
262
+
263
+ #### [Testing](.context/metrics/testing.md)
264
+
265
+ This guide explains how to write assertions in your test suite to validate `metrics` are being emitted correctly.
266
+
267
+ ### process-metrics
268
+
269
+ Provide detailed OS-specific process metrics.
270
+
271
+ #### [Getting Started](.context/process-metrics/getting-started.md)
272
+
273
+ This guide explains how to use the `process-metrics` gem to collect and analyze process metrics including processor and memory utilization.
274
+
275
+ ### protocol-http
276
+
277
+ Provides abstractions to handle HTTP protocols.
278
+
279
+ #### [Getting Started](.context/protocol-http/getting-started.md)
280
+
281
+ This guide explains how to use `protocol-http` for building abstract HTTP interfaces.
282
+
283
+ #### [Message Body](.context/protocol-http/message-body.md)
284
+
285
+ This guide explains how to work with HTTP request and response message bodies using `Protocol::HTTP::Body` classes.
286
+
287
+ #### [Headers](.context/protocol-http/headers.md)
288
+
289
+ This guide explains how to work with HTTP headers using `protocol-http`.
290
+
291
+ #### [Middleware](.context/protocol-http/middleware.md)
292
+
293
+ This guide explains how to build and use HTTP middleware with `Protocol::HTTP::Middleware`.
294
+
295
+ #### [Streaming](.context/protocol-http/streaming.md)
296
+
297
+ This guide gives an overview of how to implement streaming requests and responses.
298
+
299
+ #### [Design Overview](.context/protocol-http/design-overview.md)
300
+
301
+ This guide explains the high level design of `protocol-http` in the context of wider design patterns that can be used to implement HTTP clients and servers.
302
+
303
+ ### protocol-http1
304
+
305
+ A low level implementation of the HTTP/1 protocol.
306
+
307
+ #### [Getting Started](.context/protocol-http1/getting-started.md)
308
+
309
+ This guide explains how to get started with `protocol-http1`, a low-level implementation of the HTTP/1 protocol for building HTTP clients and servers.
310
+
311
+ ### protocol-http2
312
+
313
+ A low level implementation of the HTTP/2 protocol.
314
+
315
+ #### [Getting Started](.context/protocol-http2/getting-started.md)
316
+
317
+ This guide explains how to use the `protocol-http2` gem to implement a basic HTTP/2 client.
318
+
319
+ ### protocol-rack
320
+
321
+ An implementation of the Rack protocol/specification.
322
+
323
+ #### [Getting Started](.context/protocol-rack/getting-started.md)
324
+
325
+ This guide explains how to get started with `protocol-rack` and integrate Rack applications with `Protocol::HTTP` servers.
326
+
327
+ #### [Request and Response Handling](.context/protocol-rack/request-response.md)
328
+
329
+ This guide explains how to work with requests and responses when bridging between Rack and `Protocol::HTTP`, covering advanced use cases and edge cases.
330
+
331
+ ### samovar
332
+
333
+ Samovar is a flexible option parser excellent support for sub-commands and help documentation.
334
+
335
+ #### [Getting Started](.context/samovar/getting-started.md)
336
+
337
+ This guide explains how to use `samovar` to build command-line tools and applications.
338
+
339
+ ### sus
340
+
341
+ A fast and scalable test runner.
342
+
343
+ #### [Getting Started](.context/sus/getting-started.md)
344
+
345
+ This guide explains how to use the `sus` gem to write tests for your Ruby projects.
346
+
347
+ #### [Mocking](.context/sus/mocking.md)
348
+
349
+ This guide explains how to use mocking in sus to isolate dependencies and verify interactions in your tests.
350
+
351
+ #### [Shared Test Behaviors and Fixtures](.context/sus/shared-contexts.md)
352
+
353
+ This guide explains how to use shared test contexts and fixtures in sus to reduce duplication and ensure consistent test behavior across your test suite.
354
+
355
+ ### traces
356
+
357
+ Application instrumentation and tracing.
358
+
359
+ #### [Getting Started](.context/traces/getting-started.md)
360
+
361
+ This guide explains how to use `traces` for tracing code execution.
362
+
363
+ #### [Context Propagation](.context/traces/context-propagation.md)
364
+
365
+ This guide explains how to propagate trace context between different execution contexts within your application using `Traces.current_context` and `Traces.with_context`.
366
+
367
+ #### [Testing](.context/traces/testing.md)
368
+
369
+ This guide explains how to test traces in your code.
370
+
371
+ #### [Capture](.context/traces/capture.md)
372
+
373
+ This guide explains how to use `traces` for exporting traces from your application. This can be used to document all possible traces.
374
+
375
+ ### utopia
376
+
377
+ Utopia is a framework for building dynamic content-driven websites.
378
+
379
+ #### [Getting Started](.context/utopia/getting-started.md)
380
+
381
+ This guide explains how to set up a `utopia` website for local development and deployment.
382
+
383
+ #### [Middleware](.context/utopia/middleware.md)
384
+
385
+ This guide gives an overview of the different Rack middleware used by Utopia.
386
+
387
+ #### [Server Setup](.context/utopia/server-setup.md)
388
+
389
+ This guide explains how to deploy a `utopia` web application.
390
+
391
+ #### [Integrating with JavaScript](.context/utopia/integrating-with-javascript.md)
392
+
393
+ This guide explains how to integrate JavaScript into your Utopia application.
394
+
395
+ #### [What is XNode?](.context/utopia/what-is-xnode.md)
396
+
397
+ This guide explains the `xnode` view layer and how it can be used to build efficient websites.
398
+
399
+ #### [Updating Utopia](.context/utopia/updating-utopia.md)
400
+
401
+ This guide explains how to update existing `utopia` websites.
402
+
403
+ ### utopia-project
404
+
405
+ A project documentation tool based on Utopia.
406
+
407
+ #### [Getting Started](.context/utopia-project/getting-started.md)
408
+
409
+ This guide explains how to use `utopia-project` to add documentation to your project.
410
+
411
+ #### [Documentation Guides](.context/utopia-project/documentation-guidelines.md)
412
+
413
+ This guide explains how to create and maintain documentation for your project using `utopia-project`.
414
+
415
+ #### [Mermaid Diagrams](.context/utopia-project/mermaid-diagrams.md)
416
+
417
+ This guide explains how to use Mermaid diagrams in your documentation to visualize complex relationships, flows, and architectures.
418
+
419
+ #### [GitHub Pages Integration](.context/utopia-project/github-pages-integration.md)
420
+
421
+ This guide shows you how to use `utopia-project` with GitHub Pages to deploy documentation.
data/ext/extconf.rb ADDED
@@ -0,0 +1,51 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ # Released under the MIT License.
5
+ # Copyright, 2026, by Samuel Williams.
6
+
7
+ return if RUBY_DESCRIPTION =~ /jruby/
8
+
9
+ require "mkmf"
10
+
11
+ gem_name = File.basename(__dir__)
12
+ extension_name = "IO_Buffer_Atomic"
13
+
14
+ append_cflags(["-Wall", "-Wno-unknown-pragmas", "-std=c11"])
15
+
16
+ if ENV.key?("RUBY_DEBUG")
17
+ $stderr.puts "Enabling debug mode..."
18
+
19
+ append_cflags(["-DRUBY_DEBUG", "-O0"])
20
+ end
21
+
22
+ $srcs = ["io/buffer/atomic.c"]
23
+ $VPATH << "$(srcdir)/io/buffer"
24
+
25
+ have_header("ruby/io/buffer.h")
26
+ have_header("ruby/atomic.h")
27
+
28
+ # Check for atomic operations support
29
+ # Ruby's atomic.h handles platform detection, but we also need stdatomic.h for typed operations
30
+ if have_header("stdatomic.h")
31
+ have_func("atomic_fetch_add", "stdatomic.h")
32
+ elsif have_func("__atomic_add_fetch", "stdint.h")
33
+ # GCC built-in atomics are available
34
+ elsif have_func("_InterlockedExchangeAdd", "intrin.h")
35
+ # MSVC intrinsics are available
36
+ else
37
+ $stderr.puts "Warning: Atomic operations may not be available."
38
+ end
39
+
40
+ if ENV.key?("RUBY_SANITIZE")
41
+ $stderr.puts "Enabling sanitizers..."
42
+
43
+ # Add address and undefined behaviour sanitizers:
44
+ append_cflags(["-fsanitize=address", "-fsanitize=undefined", "-fno-omit-frame-pointer"])
45
+ $LDFLAGS << " -fsanitize=address -fsanitize=undefined"
46
+ end
47
+
48
+ create_header
49
+
50
+ # Generate the makefile to compile the native binary into `lib`:
51
+ create_makefile(extension_name)
@@ -0,0 +1,506 @@
1
+ // Released under the MIT License.
2
+ // Copyright, 2025, by Samuel Williams.
3
+
4
+ #include <ruby.h>
5
+ #include <ruby/io/buffer.h>
6
+ #include <ruby/atomic.h>
7
+ #include <stdint.h>
8
+ #include <string.h>
9
+
10
+ // We need stdatomic.h for typed atomic operations on different widths
11
+ #ifdef HAVE_STDATOMIC_H
12
+ #include <stdatomic.h>
13
+ #define HAVE_TYPED_ATOMICS 1
14
+ #elif defined(__GNUC__) && (__GNUC__ >= 4)
15
+ // Use GCC built-in atomic operations
16
+ #define USE_GCC_ATOMICS 1
17
+ #define HAVE_TYPED_ATOMICS 1
18
+ #elif defined(_MSC_VER)
19
+ // Use MSVC intrinsics
20
+ #include <intrin.h>
21
+ #define USE_MSVC_ATOMICS 1
22
+ #define HAVE_TYPED_ATOMICS 1
23
+ #else
24
+ #error "Atomic operations not available on this platform"
25
+ #endif
26
+
27
+ // Helper to get buffer pointer and validate
28
+ static void* get_buffer_pointer(VALUE buffer, size_t offset, size_t size) {
29
+ void *base;
30
+ size_t buffer_size;
31
+
32
+ rb_io_buffer_get_bytes_for_writing(buffer, &base, &buffer_size);
33
+
34
+ if (offset + size > buffer_size) {
35
+ rb_raise(rb_eRangeError, "Offset + size exceeds buffer size");
36
+ }
37
+
38
+ return (char*)base + offset;
39
+ }
40
+
41
+ // Convert Ruby symbol to ID
42
+ static ID get_type_id(VALUE type_sym) {
43
+ return RB_SYM2ID(type_sym);
44
+ }
45
+
46
+ #ifdef HAVE_TYPED_ATOMICS
47
+
48
+ #ifdef HAVE_STDATOMIC_H
49
+ // Using stdatomic.h - proper typed atomic operations
50
+
51
+ // Static IDs for type checking (initialized in Init function)
52
+ static ID RB_IO_BUFFER_DATA_TYPE_U8;
53
+ static ID RB_IO_BUFFER_DATA_TYPE_S8;
54
+ static ID RB_IO_BUFFER_DATA_TYPE_u16;
55
+ static ID RB_IO_BUFFER_DATA_TYPE_U16;
56
+ static ID RB_IO_BUFFER_DATA_TYPE_s16;
57
+ static ID RB_IO_BUFFER_DATA_TYPE_S16;
58
+ static ID RB_IO_BUFFER_DATA_TYPE_u32;
59
+ static ID RB_IO_BUFFER_DATA_TYPE_U32;
60
+ static ID RB_IO_BUFFER_DATA_TYPE_s32;
61
+ static ID RB_IO_BUFFER_DATA_TYPE_S32;
62
+ static ID RB_IO_BUFFER_DATA_TYPE_u64;
63
+ static ID RB_IO_BUFFER_DATA_TYPE_U64;
64
+ static ID RB_IO_BUFFER_DATA_TYPE_s64;
65
+ static ID RB_IO_BUFFER_DATA_TYPE_S64;
66
+
67
+ // Macro for atomic fetch_add operations (returns new value)
68
+ #define ATOMIC_FETCH_ADD_IMPL(name, ctype, atomic_type, size, num2val, val2num) \
69
+ static VALUE atomic_fetch_add_##name(VALUE self, size_t offset, long value) { \
70
+ void *pointer = get_buffer_pointer(self, offset, size); \
71
+ ctype result = atomic_fetch_add((atomic_type*)pointer, (ctype)value) + (ctype)value; \
72
+ return val2num(result); \
73
+ }
74
+
75
+ // Macro for atomic bitwise operations (returns new value)
76
+ #define ATOMIC_BITWISE_IMPL(name, ctype, atomic_type, size, num2val, val2num, op) \
77
+ static VALUE atomic_##op##_##name(VALUE self, size_t offset, long value) { \
78
+ void *pointer = get_buffer_pointer(self, offset, size); \
79
+ atomic_fetch_##op((atomic_type*)pointer, (ctype)value); \
80
+ ctype result = atomic_load((atomic_type*)pointer); \
81
+ return val2num(result); \
82
+ }
83
+
84
+ // Macro for atomic load (works on raw bytes in host endian)
85
+ #define ATOMIC_LOAD_IMPL(name, ctype, atomic_type, size, num2val, val2num) \
86
+ static VALUE atomic_load_##name(VALUE self, size_t offset) { \
87
+ void *pointer = get_buffer_pointer(self, offset, size); \
88
+ ctype result = atomic_load((atomic_type*)pointer); \
89
+ return val2num(result); \
90
+ }
91
+
92
+ // Macro for atomic store (works on raw bytes in host endian)
93
+ #define ATOMIC_STORE_IMPL(name, ctype, atomic_type, size, num2val, val2num) \
94
+ static VALUE atomic_store_##name(VALUE self, size_t offset, VALUE value) { \
95
+ void *pointer = get_buffer_pointer(self, offset, size); \
96
+ ctype converted_value = (ctype)num2val(value); \
97
+ atomic_store((atomic_type*)pointer, converted_value); \
98
+ return self; \
99
+ }
100
+
101
+ // Macro for atomic compare and swap
102
+ #define ATOMIC_CAS_IMPL(name, ctype, atomic_type, size, num2val, val2num) \
103
+ static int atomic_cas_##name(VALUE self, size_t offset, VALUE expected_value, VALUE desired_value) { \
104
+ void *pointer = get_buffer_pointer(self, offset, size); \
105
+ ctype expected = (ctype)num2val(expected_value); \
106
+ ctype desired = (ctype)num2val(desired_value); \
107
+ /* atomic_compare_exchange_strong already checks if current == expected */ \
108
+ return atomic_compare_exchange_strong((atomic_type*)pointer, &expected, desired); \
109
+ }
110
+
111
+ // Generate implementations for each type
112
+ ATOMIC_FETCH_ADD_IMPL(u8, uint8_t, atomic_uint_least8_t, 1, NUM2UINT, UINT2NUM)
113
+ ATOMIC_FETCH_ADD_IMPL(i8, int8_t, atomic_int_least8_t, 1, NUM2INT, INT2NUM)
114
+ ATOMIC_FETCH_ADD_IMPL(u16, uint16_t, atomic_uint_least16_t, 2, NUM2UINT, UINT2NUM)
115
+ ATOMIC_FETCH_ADD_IMPL(i16, int16_t, atomic_int_least16_t, 2, NUM2INT, INT2NUM)
116
+ ATOMIC_FETCH_ADD_IMPL(u32, uint32_t, atomic_uint_least32_t, 4, NUM2UINT, UINT2NUM)
117
+ ATOMIC_FETCH_ADD_IMPL(i32, int32_t, atomic_int_least32_t, 4, NUM2INT, INT2NUM)
118
+ ATOMIC_FETCH_ADD_IMPL(u64, uint64_t, atomic_uint_least64_t, 8, NUM2ULL, ULL2NUM)
119
+ ATOMIC_FETCH_ADD_IMPL(i64, int64_t, atomic_int_least64_t, 8, NUM2LL, LL2NUM)
120
+
121
+ ATOMIC_BITWISE_IMPL(u8, uint8_t, atomic_uint_least8_t, 1, NUM2UINT, UINT2NUM, and)
122
+ ATOMIC_BITWISE_IMPL(i8, int8_t, atomic_int_least8_t, 1, NUM2INT, INT2NUM, and)
123
+ ATOMIC_BITWISE_IMPL(u16, uint16_t, atomic_uint_least16_t, 2, NUM2UINT, UINT2NUM, and)
124
+ ATOMIC_BITWISE_IMPL(i16, int16_t, atomic_int_least16_t, 2, NUM2INT, INT2NUM, and)
125
+ ATOMIC_BITWISE_IMPL(u32, uint32_t, atomic_uint_least32_t, 4, NUM2UINT, UINT2NUM, and)
126
+ ATOMIC_BITWISE_IMPL(i32, int32_t, atomic_int_least32_t, 4, NUM2INT, INT2NUM, and)
127
+ ATOMIC_BITWISE_IMPL(u64, uint64_t, atomic_uint_least64_t, 8, NUM2ULL, ULL2NUM, and)
128
+ ATOMIC_BITWISE_IMPL(i64, int64_t, atomic_int_least64_t, 8, NUM2LL, LL2NUM, and)
129
+
130
+ ATOMIC_BITWISE_IMPL(u8, uint8_t, atomic_uint_least8_t, 1, NUM2UINT, UINT2NUM, or)
131
+ ATOMIC_BITWISE_IMPL(i8, int8_t, atomic_int_least8_t, 1, NUM2INT, INT2NUM, or)
132
+ ATOMIC_BITWISE_IMPL(u16, uint16_t, atomic_uint_least16_t, 2, NUM2UINT, UINT2NUM, or)
133
+ ATOMIC_BITWISE_IMPL(i16, int16_t, atomic_int_least16_t, 2, NUM2INT, INT2NUM, or)
134
+ ATOMIC_BITWISE_IMPL(u32, uint32_t, atomic_uint_least32_t, 4, NUM2UINT, UINT2NUM, or)
135
+ ATOMIC_BITWISE_IMPL(i32, int32_t, atomic_int_least32_t, 4, NUM2INT, INT2NUM, or)
136
+ ATOMIC_BITWISE_IMPL(u64, uint64_t, atomic_uint_least64_t, 8, NUM2ULL, ULL2NUM, or)
137
+ ATOMIC_BITWISE_IMPL(i64, int64_t, atomic_int_least64_t, 8, NUM2LL, LL2NUM, or)
138
+
139
+ ATOMIC_BITWISE_IMPL(u8, uint8_t, atomic_uint_least8_t, 1, NUM2UINT, UINT2NUM, xor)
140
+ ATOMIC_BITWISE_IMPL(i8, int8_t, atomic_int_least8_t, 1, NUM2INT, INT2NUM, xor)
141
+ ATOMIC_BITWISE_IMPL(u16, uint16_t, atomic_uint_least16_t, 2, NUM2UINT, UINT2NUM, xor)
142
+ ATOMIC_BITWISE_IMPL(i16, int16_t, atomic_int_least16_t, 2, NUM2INT, INT2NUM, xor)
143
+ ATOMIC_BITWISE_IMPL(u32, uint32_t, atomic_uint_least32_t, 4, NUM2UINT, UINT2NUM, xor)
144
+ ATOMIC_BITWISE_IMPL(i32, int32_t, atomic_int_least32_t, 4, NUM2INT, INT2NUM, xor)
145
+ ATOMIC_BITWISE_IMPL(u64, uint64_t, atomic_uint_least64_t, 8, NUM2ULL, ULL2NUM, xor)
146
+ ATOMIC_BITWISE_IMPL(i64, int64_t, atomic_int_least64_t, 8, NUM2LL, LL2NUM, xor)
147
+
148
+ // Atomic load/store work on raw bytes in host endian (both :u32 and :U32 map to same implementation)
149
+ ATOMIC_LOAD_IMPL(u8, uint8_t, atomic_uint_least8_t, 1, NUM2UINT, UINT2NUM)
150
+ ATOMIC_LOAD_IMPL(s8, int8_t, atomic_int_least8_t, 1, NUM2INT, INT2NUM)
151
+ ATOMIC_LOAD_IMPL(u16, uint16_t, atomic_uint_least16_t, 2, NUM2UINT, UINT2NUM)
152
+ ATOMIC_LOAD_IMPL(s16, int16_t, atomic_int_least16_t, 2, NUM2INT, INT2NUM)
153
+ ATOMIC_LOAD_IMPL(u32, uint32_t, atomic_uint_least32_t, 4, NUM2UINT, UINT2NUM)
154
+ ATOMIC_LOAD_IMPL(s32, int32_t, atomic_int_least32_t, 4, NUM2INT, INT2NUM)
155
+ ATOMIC_LOAD_IMPL(u64, uint64_t, atomic_uint_least64_t, 8, NUM2ULL, ULL2NUM)
156
+ ATOMIC_LOAD_IMPL(s64, int64_t, atomic_int_least64_t, 8, NUM2LL, LL2NUM)
157
+
158
+ ATOMIC_STORE_IMPL(u8, uint8_t, atomic_uint_least8_t, 1, NUM2UINT, UINT2NUM)
159
+ ATOMIC_STORE_IMPL(s8, int8_t, atomic_int_least8_t, 1, NUM2INT, INT2NUM)
160
+ ATOMIC_STORE_IMPL(u16, uint16_t, atomic_uint_least16_t, 2, NUM2UINT, UINT2NUM)
161
+ ATOMIC_STORE_IMPL(s16, int16_t, atomic_int_least16_t, 2, NUM2INT, INT2NUM)
162
+ ATOMIC_STORE_IMPL(u32, uint32_t, atomic_uint_least32_t, 4, NUM2UINT, UINT2NUM)
163
+ ATOMIC_STORE_IMPL(s32, int32_t, atomic_int_least32_t, 4, NUM2INT, INT2NUM)
164
+ ATOMIC_STORE_IMPL(u64, uint64_t, atomic_uint_least64_t, 8, NUM2ULL, ULL2NUM)
165
+ ATOMIC_STORE_IMPL(s64, int64_t, atomic_int_least64_t, 8, NUM2LL, LL2NUM)
166
+
167
+ ATOMIC_CAS_IMPL(u8, uint8_t, atomic_uint_least8_t, 1, NUM2UINT, UINT2NUM)
168
+ ATOMIC_CAS_IMPL(i8, int8_t, atomic_int_least8_t, 1, NUM2INT, INT2NUM)
169
+ ATOMIC_CAS_IMPL(u16, uint16_t, atomic_uint_least16_t, 2, NUM2UINT, UINT2NUM)
170
+ ATOMIC_CAS_IMPL(i16, int16_t, atomic_int_least16_t, 2, NUM2INT, INT2NUM)
171
+ ATOMIC_CAS_IMPL(u32, uint32_t, atomic_uint_least32_t, 4, NUM2UINT, UINT2NUM)
172
+ ATOMIC_CAS_IMPL(i32, int32_t, atomic_int_least32_t, 4, NUM2INT, INT2NUM)
173
+ ATOMIC_CAS_IMPL(u64, uint64_t, atomic_uint_least64_t, 8, NUM2ULL, ULL2NUM)
174
+ ATOMIC_CAS_IMPL(i64, int64_t, atomic_int_least64_t, 8, NUM2LL, LL2NUM)
175
+
176
+ // Generic implementations using macros (similar to Ruby's IO_BUFFER_SET_VALUE pattern)
177
+ static VALUE atomic_increment_impl(VALUE self, ID type_identifier, size_t offset, long value) {
178
+ #define ATOMIC_FETCH_ADD_CASE(name, impl_name) \
179
+ if (type_identifier == RB_IO_BUFFER_DATA_TYPE_##name) { \
180
+ return atomic_fetch_add_##impl_name(self, offset, value); \
181
+ }
182
+
183
+ ATOMIC_FETCH_ADD_CASE(U8, u8)
184
+ ATOMIC_FETCH_ADD_CASE(S8, i8)
185
+ ATOMIC_FETCH_ADD_CASE(u16, u16)
186
+ ATOMIC_FETCH_ADD_CASE(U16, u16)
187
+ ATOMIC_FETCH_ADD_CASE(s16, i16)
188
+ ATOMIC_FETCH_ADD_CASE(S16, i16)
189
+ ATOMIC_FETCH_ADD_CASE(u32, u32)
190
+ ATOMIC_FETCH_ADD_CASE(U32, u32)
191
+ ATOMIC_FETCH_ADD_CASE(s32, i32)
192
+ ATOMIC_FETCH_ADD_CASE(S32, i32)
193
+ ATOMIC_FETCH_ADD_CASE(u64, u64)
194
+ ATOMIC_FETCH_ADD_CASE(U64, u64)
195
+ ATOMIC_FETCH_ADD_CASE(s64, i64)
196
+ ATOMIC_FETCH_ADD_CASE(S64, i64)
197
+ #undef ATOMIC_FETCH_ADD_CASE
198
+
199
+ rb_raise(rb_eArgError, "Unsupported type for atomic operations: %"PRIsVALUE, rb_id2str(type_identifier));
200
+ return Qnil;
201
+ }
202
+
203
+ static VALUE atomic_and_impl(VALUE self, ID type_identifier, size_t offset, long value) {
204
+ #define ATOMIC_AND_CASE(name, impl_name) \
205
+ if (type_identifier == RB_IO_BUFFER_DATA_TYPE_##name) { \
206
+ return atomic_and_##impl_name(self, offset, value); \
207
+ }
208
+
209
+ ATOMIC_AND_CASE(U8, u8)
210
+ ATOMIC_AND_CASE(S8, i8)
211
+ ATOMIC_AND_CASE(u16, u16)
212
+ ATOMIC_AND_CASE(U16, u16)
213
+ ATOMIC_AND_CASE(s16, i16)
214
+ ATOMIC_AND_CASE(S16, i16)
215
+ ATOMIC_AND_CASE(u32, u32)
216
+ ATOMIC_AND_CASE(U32, u32)
217
+ ATOMIC_AND_CASE(s32, i32)
218
+ ATOMIC_AND_CASE(S32, i32)
219
+ ATOMIC_AND_CASE(u64, u64)
220
+ ATOMIC_AND_CASE(U64, u64)
221
+ ATOMIC_AND_CASE(s64, i64)
222
+ ATOMIC_AND_CASE(S64, i64)
223
+ #undef ATOMIC_AND_CASE
224
+
225
+ rb_raise(rb_eArgError, "Unsupported type for atomic operations: %"PRIsVALUE, rb_id2str(type_identifier));
226
+ return Qnil;
227
+ }
228
+
229
+ static VALUE atomic_or_impl(VALUE self, ID type_identifier, size_t offset, long value) {
230
+ #define ATOMIC_OR_CASE(name, impl_name) \
231
+ if (type_identifier == RB_IO_BUFFER_DATA_TYPE_##name) { \
232
+ return atomic_or_##impl_name(self, offset, value); \
233
+ }
234
+
235
+ ATOMIC_OR_CASE(U8, u8)
236
+ ATOMIC_OR_CASE(S8, i8)
237
+ ATOMIC_OR_CASE(u16, u16)
238
+ ATOMIC_OR_CASE(U16, u16)
239
+ ATOMIC_OR_CASE(s16, i16)
240
+ ATOMIC_OR_CASE(S16, i16)
241
+ ATOMIC_OR_CASE(u32, u32)
242
+ ATOMIC_OR_CASE(U32, u32)
243
+ ATOMIC_OR_CASE(s32, i32)
244
+ ATOMIC_OR_CASE(S32, i32)
245
+ ATOMIC_OR_CASE(u64, u64)
246
+ ATOMIC_OR_CASE(U64, u64)
247
+ ATOMIC_OR_CASE(s64, i64)
248
+ ATOMIC_OR_CASE(S64, i64)
249
+ #undef ATOMIC_OR_CASE
250
+
251
+ rb_raise(rb_eArgError, "Unsupported type for atomic operations: %"PRIsVALUE, rb_id2str(type_identifier));
252
+ return Qnil;
253
+ }
254
+
255
+ static VALUE atomic_xor_impl(VALUE self, ID type_identifier, size_t offset, long value) {
256
+ #define ATOMIC_XOR_CASE(name, impl_name) \
257
+ if (type_identifier == RB_IO_BUFFER_DATA_TYPE_##name) { \
258
+ return atomic_xor_##impl_name(self, offset, value); \
259
+ }
260
+
261
+ ATOMIC_XOR_CASE(U8, u8)
262
+ ATOMIC_XOR_CASE(S8, i8)
263
+ ATOMIC_XOR_CASE(u16, u16)
264
+ ATOMIC_XOR_CASE(U16, u16)
265
+ ATOMIC_XOR_CASE(s16, i16)
266
+ ATOMIC_XOR_CASE(S16, i16)
267
+ ATOMIC_XOR_CASE(u32, u32)
268
+ ATOMIC_XOR_CASE(U32, u32)
269
+ ATOMIC_XOR_CASE(s32, i32)
270
+ ATOMIC_XOR_CASE(S32, i32)
271
+ ATOMIC_XOR_CASE(u64, u64)
272
+ ATOMIC_XOR_CASE(U64, u64)
273
+ ATOMIC_XOR_CASE(s64, i64)
274
+ ATOMIC_XOR_CASE(S64, i64)
275
+ #undef ATOMIC_XOR_CASE
276
+
277
+ rb_raise(rb_eArgError, "Unsupported type for atomic operations: %"PRIsVALUE, rb_id2str(type_identifier));
278
+ return Qnil;
279
+ }
280
+
281
+ static VALUE atomic_load_impl(VALUE self, ID type_identifier, size_t offset) {
282
+ #define ATOMIC_LOAD_CASE(name, impl_name) \
283
+ if (type_identifier == RB_IO_BUFFER_DATA_TYPE_##name) { \
284
+ return atomic_load_##impl_name(self, offset); \
285
+ }
286
+
287
+ // Support both uppercase and lowercase (they map to same implementation - atomic ops work on raw bytes)
288
+ ATOMIC_LOAD_CASE(U8, u8)
289
+ ATOMIC_LOAD_CASE(S8, s8)
290
+ ATOMIC_LOAD_CASE(u16, u16)
291
+ ATOMIC_LOAD_CASE(s16, s16)
292
+ ATOMIC_LOAD_CASE(U16, u16)
293
+ ATOMIC_LOAD_CASE(S16, s16)
294
+ ATOMIC_LOAD_CASE(u32, u32)
295
+ ATOMIC_LOAD_CASE(s32, s32)
296
+ ATOMIC_LOAD_CASE(U32, u32)
297
+ ATOMIC_LOAD_CASE(S32, s32)
298
+ ATOMIC_LOAD_CASE(u64, u64)
299
+ ATOMIC_LOAD_CASE(s64, s64)
300
+ ATOMIC_LOAD_CASE(U64, u64)
301
+ ATOMIC_LOAD_CASE(S64, s64)
302
+ #undef ATOMIC_LOAD_CASE
303
+
304
+ rb_raise(rb_eArgError, "Unsupported type for atomic operations: %"PRIsVALUE, rb_id2str(type_identifier));
305
+ return Qnil;
306
+ }
307
+
308
+ static VALUE atomic_store_impl(VALUE self, ID type_identifier, size_t offset, VALUE value) {
309
+ #define ATOMIC_STORE_CASE(name, impl_name) \
310
+ if (type_identifier == RB_IO_BUFFER_DATA_TYPE_##name) { \
311
+ return atomic_store_##impl_name(self, offset, value); \
312
+ }
313
+
314
+ // Support both uppercase and lowercase (they map to same implementation - atomic ops work on raw bytes)
315
+ ATOMIC_STORE_CASE(U8, u8)
316
+ ATOMIC_STORE_CASE(S8, s8)
317
+ ATOMIC_STORE_CASE(u16, u16)
318
+ ATOMIC_STORE_CASE(s16, s16)
319
+ ATOMIC_STORE_CASE(U16, u16)
320
+ ATOMIC_STORE_CASE(S16, s16)
321
+ ATOMIC_STORE_CASE(u32, u32)
322
+ ATOMIC_STORE_CASE(s32, s32)
323
+ ATOMIC_STORE_CASE(U32, u32)
324
+ ATOMIC_STORE_CASE(S32, s32)
325
+ ATOMIC_STORE_CASE(u64, u64)
326
+ ATOMIC_STORE_CASE(s64, s64)
327
+ ATOMIC_STORE_CASE(U64, u64)
328
+ ATOMIC_STORE_CASE(S64, s64)
329
+ #undef ATOMIC_STORE_CASE
330
+
331
+ rb_raise(rb_eArgError, "Unsupported type for atomic operations: %"PRIsVALUE, rb_id2str(type_identifier));
332
+ return Qnil;
333
+ }
334
+
335
+ static VALUE atomic_compare_and_swap_impl(VALUE self, ID type_identifier, size_t offset, VALUE expected_value, VALUE desired_value) {
336
+ #define ATOMIC_CAS_CASE(name, impl_name) \
337
+ if (type_identifier == RB_IO_BUFFER_DATA_TYPE_##name) { \
338
+ return atomic_cas_##impl_name(self, offset, expected_value, desired_value) ? Qtrue : Qfalse; \
339
+ }
340
+
341
+ ATOMIC_CAS_CASE(U8, u8)
342
+ ATOMIC_CAS_CASE(S8, i8)
343
+ ATOMIC_CAS_CASE(u16, u16)
344
+ ATOMIC_CAS_CASE(U16, u16)
345
+ ATOMIC_CAS_CASE(s16, i16)
346
+ ATOMIC_CAS_CASE(S16, i16)
347
+ ATOMIC_CAS_CASE(u32, u32)
348
+ ATOMIC_CAS_CASE(U32, u32)
349
+ ATOMIC_CAS_CASE(s32, i32)
350
+ ATOMIC_CAS_CASE(S32, i32)
351
+ ATOMIC_CAS_CASE(u64, u64)
352
+ ATOMIC_CAS_CASE(U64, u64)
353
+ ATOMIC_CAS_CASE(s64, i64)
354
+ ATOMIC_CAS_CASE(S64, i64)
355
+ #undef ATOMIC_CAS_CASE
356
+
357
+ rb_raise(rb_eArgError, "Unsupported type for atomic operations: %"PRIsVALUE, rb_id2str(type_identifier));
358
+ return Qfalse;
359
+ }
360
+
361
+ #endif // HAVE_STDATOMIC_H
362
+
363
+ #else
364
+ #error "Typed atomic operations not available"
365
+ #endif // HAVE_TYPED_ATOMICS
366
+
367
+ // Ruby method wrappers
368
+ static VALUE atomic_increment(int argc, VALUE *argv, VALUE self) {
369
+ VALUE type_symbol, offset_value, value_value;
370
+
371
+ rb_scan_args(argc, argv, "21", &type_symbol, &offset_value, &value_value);
372
+
373
+ if (value_value == Qnil) {
374
+ value_value = INT2NUM(1);
375
+ }
376
+
377
+ ID type_identifier = get_type_id(type_symbol);
378
+ size_t offset = NUM2SIZET(offset_value);
379
+ long value = NUM2LONG(value_value);
380
+
381
+ return atomic_increment_impl(self, type_identifier, offset, value);
382
+ }
383
+
384
+ static VALUE atomic_decrement(int argc, VALUE *argv, VALUE self) {
385
+ VALUE type_symbol, offset_value, value_value;
386
+
387
+ rb_scan_args(argc, argv, "21", &type_symbol, &offset_value, &value_value);
388
+
389
+ if (value_value == Qnil) {
390
+ value_value = INT2NUM(1);
391
+ }
392
+
393
+ ID type_identifier = get_type_id(type_symbol);
394
+ size_t offset = NUM2SIZET(offset_value);
395
+ long value = NUM2LONG(value_value);
396
+
397
+ return atomic_increment_impl(self, type_identifier, offset, -value);
398
+ }
399
+
400
+ static VALUE atomic_add(VALUE self, VALUE type_symbol, VALUE offset_value, VALUE value_value) {
401
+ ID type_identifier = get_type_id(type_symbol);
402
+ size_t offset = NUM2SIZET(offset_value);
403
+ long value = NUM2LONG(value_value);
404
+
405
+ return atomic_increment_impl(self, type_identifier, offset, value);
406
+ }
407
+
408
+ static VALUE atomic_subtract(VALUE self, VALUE type_symbol, VALUE offset_value, VALUE value_value) {
409
+ ID type_identifier = get_type_id(type_symbol);
410
+ size_t offset = NUM2SIZET(offset_value);
411
+ long value = NUM2LONG(value_value);
412
+
413
+ return atomic_increment_impl(self, type_identifier, offset, -value);
414
+ }
415
+
416
+ static VALUE atomic_and(VALUE self, VALUE type_symbol, VALUE offset_value, VALUE value_value) {
417
+ ID type_identifier = get_type_id(type_symbol);
418
+ size_t offset = NUM2SIZET(offset_value);
419
+ long value = NUM2LONG(value_value);
420
+
421
+ return atomic_and_impl(self, type_identifier, offset, value);
422
+ }
423
+
424
+ static VALUE atomic_or(VALUE self, VALUE type_symbol, VALUE offset_value, VALUE value_value) {
425
+ ID type_identifier = get_type_id(type_symbol);
426
+ size_t offset = NUM2SIZET(offset_value);
427
+ long value = NUM2LONG(value_value);
428
+
429
+ return atomic_or_impl(self, type_identifier, offset, value);
430
+ }
431
+
432
+ static VALUE atomic_xor(VALUE self, VALUE type_symbol, VALUE offset_value, VALUE value_value) {
433
+ ID type_identifier = get_type_id(type_symbol);
434
+ size_t offset = NUM2SIZET(offset_value);
435
+ long value = NUM2LONG(value_value);
436
+
437
+ return atomic_xor_impl(self, type_identifier, offset, value);
438
+ }
439
+
440
+ static VALUE atomic_load_method(VALUE self, VALUE type_symbol, VALUE offset_value) {
441
+ ID type_identifier = get_type_id(type_symbol);
442
+ size_t offset = NUM2SIZET(offset_value);
443
+
444
+ return atomic_load_impl(self, type_identifier, offset);
445
+ }
446
+
447
+ static VALUE atomic_store_method(VALUE self, VALUE type_symbol, VALUE offset_value, VALUE value) {
448
+ ID type_identifier = get_type_id(type_symbol);
449
+ size_t offset = NUM2SIZET(offset_value);
450
+
451
+ return atomic_store_impl(self, type_identifier, offset, value);
452
+ }
453
+
454
+ static VALUE atomic_compare_and_swap(VALUE self, VALUE type_symbol, VALUE offset_value, VALUE expected_value, VALUE desired_value) {
455
+ ID type_identifier = get_type_id(type_symbol);
456
+ size_t offset = NUM2SIZET(offset_value);
457
+
458
+ return atomic_compare_and_swap_impl(self, type_identifier, offset, expected_value, desired_value);
459
+ }
460
+
461
+ // Helper macro to define method only if it doesn't exist
462
+ // Uses rb_method_boundp to check if method is already defined (feature detection)
463
+ #define DEFINE_METHOD_IF_NOT_EXISTS(klass, name, func, argc) \
464
+ do { \
465
+ ID method_identifier = rb_intern(name); \
466
+ if (!rb_method_boundp(klass, method_identifier, 0)) { \
467
+ rb_define_method(klass, name, func, argc); \
468
+ } \
469
+ } while (0)
470
+
471
+ void Init_IO_Buffer_Atomic(void) {
472
+ #ifdef HAVE_RB_EXT_RACTOR_SAFE
473
+ rb_ext_ractor_safe(true);
474
+ #endif
475
+
476
+ VALUE IO_Buffer = rb_const_get(rb_cIO, rb_intern("Buffer"));
477
+
478
+ // Initialize type IDs (support both forms for compatibility, but atomic operations work on raw bytes in host endian)
479
+ #define IO_BUFFER_DEFINE_DATA_TYPE(name) RB_IO_BUFFER_DATA_TYPE_##name = rb_intern_const(#name)
480
+ IO_BUFFER_DEFINE_DATA_TYPE(U8);
481
+ IO_BUFFER_DEFINE_DATA_TYPE(S8);
482
+ IO_BUFFER_DEFINE_DATA_TYPE(u16);
483
+ IO_BUFFER_DEFINE_DATA_TYPE(U16);
484
+ IO_BUFFER_DEFINE_DATA_TYPE(s16);
485
+ IO_BUFFER_DEFINE_DATA_TYPE(S16);
486
+ IO_BUFFER_DEFINE_DATA_TYPE(u32);
487
+ IO_BUFFER_DEFINE_DATA_TYPE(U32);
488
+ IO_BUFFER_DEFINE_DATA_TYPE(s32);
489
+ IO_BUFFER_DEFINE_DATA_TYPE(S32);
490
+ IO_BUFFER_DEFINE_DATA_TYPE(u64);
491
+ IO_BUFFER_DEFINE_DATA_TYPE(U64);
492
+ IO_BUFFER_DEFINE_DATA_TYPE(s64);
493
+ IO_BUFFER_DEFINE_DATA_TYPE(S64);
494
+ #undef IO_BUFFER_DEFINE_DATA_TYPE
495
+
496
+ DEFINE_METHOD_IF_NOT_EXISTS(IO_Buffer, "atomic_increment", atomic_increment, -1);
497
+ DEFINE_METHOD_IF_NOT_EXISTS(IO_Buffer, "atomic_decrement", atomic_decrement, -1);
498
+ DEFINE_METHOD_IF_NOT_EXISTS(IO_Buffer, "atomic_add", atomic_add, 3);
499
+ DEFINE_METHOD_IF_NOT_EXISTS(IO_Buffer, "atomic_subtract", atomic_subtract, 3);
500
+ DEFINE_METHOD_IF_NOT_EXISTS(IO_Buffer, "atomic_and", atomic_and, 3);
501
+ DEFINE_METHOD_IF_NOT_EXISTS(IO_Buffer, "atomic_or", atomic_or, 3);
502
+ DEFINE_METHOD_IF_NOT_EXISTS(IO_Buffer, "atomic_xor", atomic_xor, 3);
503
+ DEFINE_METHOD_IF_NOT_EXISTS(IO_Buffer, "atomic_load", atomic_load_method, 2);
504
+ DEFINE_METHOD_IF_NOT_EXISTS(IO_Buffer, "atomic_store", atomic_store_method, 3);
505
+ DEFINE_METHOD_IF_NOT_EXISTS(IO_Buffer, "atomic_compare_and_swap", atomic_compare_and_swap, 4);
506
+ }
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Released under the MIT License.
4
+ # Copyright, 2026, by Samuel Williams.
5
+
6
+ # @namespace
7
+ class IO
8
+ # @namespace
9
+ class Buffer
10
+ # @namespace
11
+ module Atomic
12
+ VERSION = "0.1.0"
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Released under the MIT License.
4
+ # Copyright, 2026, by Samuel Williams.
5
+
6
+ require_relative "atomic/version"
7
+
8
+ # Check if Ruby itself provides atomic operations
9
+ if IO.const_defined?(:Buffer) && IO::Buffer.instance_methods.include?(:atomic_increment)
10
+ # Ruby provides atomic operations, use them
11
+ else
12
+ # Load native extension (required for shared memory support)
13
+ begin
14
+ require "IO_Buffer_Atomic"
15
+ rescue LoadError => error
16
+ warn "Could not load native atomic operations: #{error.message}"
17
+ warn "Atomic operations will not be available."
18
+ end
19
+ end
data/lib/io/buffer.rb ADDED
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Released under the MIT License.
4
+ # Copyright, 2026, by Samuel Williams.
5
+
6
+ require_relative "buffer/atomic"
data/license.md ADDED
@@ -0,0 +1,21 @@
1
+ # MIT License
2
+
3
+ Copyright, 2026, by Samuel Williams.
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/readme.md ADDED
@@ -0,0 +1,41 @@
1
+ # IO::Buffer::Atomic
2
+
3
+ Provides atomic operations for `IO::Buffer` instances, including increment, decrement, add, subtract, and bitwise operations.
4
+
5
+ [![Development Status](https://github.com/socketry/io-buffer-atomic/workflows/Test/badge.svg)](https://github.com/socketry/io-buffer-atomic/actions?workflow=Test)
6
+
7
+ ## Features
8
+
9
+ - **Atomic Operations**: Thread-safe atomic operations on `IO::Buffer` values.
10
+ - **Multiple Types**: Supports `:u8`, `:u16`, `:u32`, `:u64`, `:i8`, `:i16`, `:i32`, `:i64`.
11
+ - **Native Extension**: Uses C11 atomic operations when available for maximum performance.
12
+
13
+ ## Usage
14
+
15
+ Please see the [project documentation](https://socketry.github.io/io-buffer-atomic/) for more details.
16
+
17
+ - [Getting Started](https://socketry.github.io/io-buffer-atomic/guides/getting-started/index) - This guide explains how to use `io-buffer-atomic` for atomic operations on `IO::Buffer` instances.
18
+
19
+ ## Releases
20
+
21
+ Please see the [project releases](https://socketry.github.io/io-buffer-atomic/releases/index) for all releases.
22
+
23
+ ### v0.1.0
24
+
25
+ ## Contributing
26
+
27
+ We welcome contributions to this project.
28
+
29
+ 1. Fork it.
30
+ 2. Create your feature branch (`git checkout -b my-new-feature`).
31
+ 3. Commit your changes (`git commit -am 'Add some feature'`).
32
+ 4. Push to the branch (`git push origin my-new-feature`).
33
+ 5. Create new Pull Request.
34
+
35
+ ### Developer Certificate of Origin
36
+
37
+ In order to protect users of this project, we require all contributors to comply with the [Developer Certificate of Origin](https://developercertificate.org/). This ensures that all contributions are properly licensed and attributed.
38
+
39
+ ### Community Guidelines
40
+
41
+ This project is best served by a collaborative and respectful environment. Treat each other professionally, respect differing viewpoints, and engage constructively. Harassment, discrimination, or harmful behavior is not tolerated. Communicate clearly, listen actively, and support one another. If any issues arise, please inform the project maintainers.
data/releases.md ADDED
@@ -0,0 +1,3 @@
1
+ # Releases
2
+
3
+ ## v0.1.0
metadata ADDED
@@ -0,0 +1,50 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: io-buffer-atomic
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Samuel Williams
8
+ bindir: bin
9
+ cert_chain: []
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
+ dependencies: []
12
+ executables: []
13
+ extensions:
14
+ - ext/extconf.rb
15
+ extra_rdoc_files: []
16
+ files:
17
+ - agents.md
18
+ - ext/extconf.rb
19
+ - ext/io/buffer/atomic.c
20
+ - lib/io/buffer.rb
21
+ - lib/io/buffer/atomic.rb
22
+ - lib/io/buffer/atomic/version.rb
23
+ - license.md
24
+ - readme.md
25
+ - releases.md
26
+ homepage: https://github.com/socketry/io-buffer-atomic
27
+ licenses:
28
+ - MIT
29
+ metadata:
30
+ documentation_uri: https://socketry.github.io/io-buffer-atomic/
31
+ funding_uri: https://github.com/sponsors/ioquatix
32
+ source_code_uri: https://github.com/socketry/io-buffer-atomic.git
33
+ rdoc_options: []
34
+ require_paths:
35
+ - lib
36
+ required_ruby_version: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '3.2'
41
+ required_rubygems_version: !ruby/object:Gem::Requirement
42
+ requirements:
43
+ - - ">="
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ requirements: []
47
+ rubygems_version: 4.0.3
48
+ specification_version: 4
49
+ summary: Atomic operations for IO::Buffer instances.
50
+ test_files: []