arbiter_machines 0.1.5-aarch64-linux

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: 3ec1cc02077cd0a3484e47dec1d2bfe3139db4b295f2a083322404bb476c2462
4
+ data.tar.gz: d364fa500c077c33a03f80615e857fb45e75b9e8cfec8a814a24783c6d1de895
5
+ SHA512:
6
+ metadata.gz: 7b7ad6b35d3e98b4e9bbf712334186db2642e129d5c31be9b819ea672f4b21784d3ac1d387af65895a1a1a5700e3ebae03814de42eb1df2dbdc052c73955a850
7
+ data.tar.gz: 484ab97b811be4da0aca64ec75fcf920a722bac9c18e611759fe437aa69a1fe880eca425c95b852e027718c1a4fd198c5f5e5895e8740de1e38c3d0964452011
data/CHANGELOG.md ADDED
@@ -0,0 +1,36 @@
1
+ # Changelog
2
+
3
+ ## [0.1.5](https://github.com/seuros/arbiter_machines/compare/arbiter_machines/v0.1.4...arbiter_machines/v0.1.5) (2026-11-06)
4
+
5
+
6
+ ### Bug Fixes
7
+
8
+ * share RArray count conversion in arbiter ffi ([0a026aa](https://github.com/seuros/arbiter_machines/commit/0a026aac2e17efecc2546bb8b9532b89f516e7f0))
9
+
10
+ ## [0.1.4](https://github.com/seuros/arbiter_machines/compare/arbiter_machines/v0.1.3...arbiter_machines/v0.1.4) (2026-11-06)
11
+
12
+
13
+ ### Bug Fixes
14
+
15
+ * crates.io keywords and categories for the core ([10c8cad](https://github.com/seuros/arbiter_machines/commit/10c8cad0f35cb6c44dbe0e5573275f2267afc306))
16
+
17
+ ## [0.1.3](https://github.com/seuros/arbiter_machines/compare/arbiter_machines/v0.1.2...arbiter_machines/v0.1.3) (2026-11-06)
18
+
19
+
20
+ ### Bug Fixes
21
+
22
+ * keep test sources out of the packaged gem and crate ([513f507](https://github.com/seuros/arbiter_machines/commit/513f5074a2058f23742ee47b2289cd8b8872d6d3))
23
+
24
+ ## [0.1.2](https://github.com/seuros/arbiter_machines/compare/arbiter_machines/v0.1.1...arbiter_machines/v0.1.2) (2026-11-06)
25
+
26
+
27
+ ### Bug Fixes
28
+
29
+ * cross builds no longer compile for the host ([feeb4f0](https://github.com/seuros/arbiter_machines/commit/feeb4f09e5d0b20b81a3fffb35562594b80e2e0a))
30
+
31
+ ## [0.1.1](https://github.com/seuros/arbiter_machines/compare/arbiter_machines-v0.1.0...arbiter_machines/v0.1.1) (2026-11-06)
32
+
33
+
34
+ ### Features
35
+
36
+ * initial extraction ([0ce828b](https://github.com/seuros/arbiter_machines/commit/0ce828bfacd5c9ef7482136b73d7be18339e19b9))
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Abdelkader Boudih
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,221 @@
1
+ # ArbiterMachines
2
+
3
+ > When the slow job holds the key the urgent job needs, more workers won't help. 🛰️
4
+
5
+ A priority arbiter for Ruby. You declare your tasks with their priorities and your resources with their ceilings. The `Arbiter` tells you who may take what, who has to wait, who just inherited someone else's urgency, and who is about to deadlock. Your code still takes and releases the actual locks. The arbiter only ever answers one question: is this a good idea right now?
6
+
7
+ ## Quick Start
8
+
9
+ ```bash
10
+ gem 'arbiter_machines'
11
+ ```
12
+
13
+ ```ruby
14
+ # Two tasks: a janitor at priority 1, a payment at priority 10.
15
+ # One resource: the ledger lock, ceiling 10.
16
+ arbiter = ArbiterMachines::Arbiter.new(
17
+ priorities: [1, 10],
18
+ ceilings: [10],
19
+ protocol: :inheritance
20
+ )
21
+
22
+ arbiter.acquire(task: 0, resource: 0) # => [:granted, nil] janitor takes the ledger
23
+ arbiter.acquire(task: 1, resource: 0) # => [:blocked, 0] payment waits on resource 0
24
+ arbiter.effective_priorities # => [10, 10] janitor now runs as urgent
25
+
26
+ arbiter.release(task: 0, resource: 0) # => true janitor done, wait cleared
27
+ arbiter.acquire(task: 1, resource: 0) # => [:granted, nil] payment retries and gets in
28
+ ```
29
+
30
+ Tasks and resources are positional indices into the arrays you passed in. Larger number means more urgent.
31
+
32
+ ## A Message to the Resistance
33
+
34
+ So you gave the nightly cleanup job priority 1, the payment processor priority 10, and a pile of report builders priority 5. Sensible. Then one night the cleanup job took the ledger lock for a quick vacuum, the payment job arrived and blocked on it, and twelve report builders showed up and ran. All of them. Before the cleanup job got another slice of CPU.
35
+
36
+ The payment job, your most urgent work, sat behind priority-5 reports because a priority-1 job was holding its lock and nobody told the scheduler that the priority-1 job was suddenly very important.
37
+
38
+ That's priority inversion. It put a rover on Mars into a reboot loop in 1997, and it's been quietly doing the same thing to job queues ever since. The fix has been known just as long: while the janitor holds something the payment job needs, the janitor *is* the payment job. It inherits the urgency, finishes fast, hands the lock over.
39
+
40
+ This gem does the accounting for that. Who holds what, who waits on whom, what everybody's priority really is right now, and whether the next acquire closes a loop nobody can escape from.
41
+
42
+ ## Chapter 1: Stardate 2025.186, the AMS Dijkstra
43
+
44
+ > **AMS Dijkstra**, NCC-4201, *Dijkstra*-class traffic-control vessel.
45
+ > One airlock. Two waiting crews. No circular holding pattern.
46
+
47
+ The vessel doesn't open airlocks. It keeps the chart: which crew is in which lock, which crew is waiting at which door, and which crew's urgency has been lent to the crew blocking it. Before any crew is cleared to wait, the chart is checked for a loop. A loop is never charted, because a charted loop is a ship nobody leaves.
48
+
49
+ Somewhere on the bridge, a junior officer suggests:
50
+
51
+ ```ruby
52
+ def process_payment
53
+ ledger_lock.synchronize do # the janitor has this, and the janitor is slow
54
+ charge_card
55
+ end
56
+ end
57
+ ```
58
+
59
+ "That," says the traffic controller, "is how urgent work waits for the floor to be mopped."
60
+
61
+ ## The Flight Plan
62
+
63
+ Every resource is either free or held. Every task is either running, holding, or waiting at exactly one door.
64
+
65
+ ```mermaid
66
+ stateDiagram-v2
67
+ [*] --> free
68
+ free --> held: acquire, granted
69
+ held --> held: holder acquires again, already_owned
70
+ held --> held: other task acquires, blocked (holder inherits urgency)
71
+ held --> free: holder releases, waiters cleared to retry
72
+ held --> held: acquire would close a wait loop, deadlock (nothing changes)
73
+ free --> [*]
74
+ ```
75
+
76
+ And the night the janitor held the ledger:
77
+
78
+ ```mermaid
79
+ sequenceDiagram
80
+ participant J as Janitor (task 0, prio 1)
81
+ participant A as Arbiter
82
+ participant P as Payment (task 1, prio 10)
83
+
84
+ J->>A: acquire(task 0, resource 0)
85
+ A-->>J: granted
86
+ Note over J: starts vacuuming the ledger
87
+
88
+ P->>A: acquire(task 1, resource 0)
89
+ A-->>P: blocked on resource 0
90
+ Note over A: effective priorities now [10, 10]
91
+ Note over J: runs as priority 10, no report builder gets ahead
92
+
93
+ J->>A: release(task 0, resource 0)
94
+ A-->>J: true
95
+ Note over A: effective priorities back to [1, 10], wait cleared
96
+
97
+ P->>A: acquire(task 1, resource 0)
98
+ A-->>P: granted
99
+ ```
100
+
101
+ ## How Arbitration Works
102
+
103
+ ### Acquire
104
+
105
+ `acquire` returns a two-element array: a status and, when blocked, the resource index you're waiting on.
106
+
107
+ ```ruby
108
+ arbiter = ArbiterMachines::Arbiter.new(
109
+ priorities: [1, 5, 10], ceilings: [10, 10], protocol: :inheritance
110
+ )
111
+
112
+ arbiter.acquire(task: 0, resource: 0) # => [:granted, nil]
113
+ arbiter.acquire(task: 0, resource: 0) # => [:already_owned, nil]
114
+ arbiter.acquire(task: 1, resource: 1) # => [:granted, nil]
115
+ arbiter.acquire(task: 1, resource: 0) # => [:blocked, 0]
116
+ ```
117
+
118
+ Task 1 now holds resource 1 and is waiting on resource 0, which task 0 holds.
119
+
120
+ ### Inheritance is transitive
121
+
122
+ Urgency flows up the chain. If a third task waits on task 1, task 1 gets its priority, and so does task 0 behind it.
123
+
124
+ ```ruby
125
+ arbiter.acquire(task: 2, resource: 1) # => [:blocked, 1]
126
+ arbiter.effective_priorities # => [10, 10, 10]
127
+ ```
128
+
129
+ Task 2, priority 10, waits on task 1. Task 1 waits on task 0. Everybody in the chain runs at 10 until the chain clears.
130
+
131
+ ### Release and retry
132
+
133
+ Releasing clears the waits on that resource. It does not hand the resource to anyone. The waiters call `acquire` again, and the arbiter decides fresh.
134
+
135
+ ```ruby
136
+ arbiter.release(task: 0, resource: 0) # => true
137
+ arbiter.effective_priorities # => [1, 10, 10] task 1 still blocks task 2
138
+ arbiter.acquire(task: 1, resource: 0) # => [:granted, nil]
139
+ ```
140
+
141
+ Release by a task that doesn't hold the resource returns `false` and changes nothing.
142
+
143
+ ### Stop waiting
144
+
145
+ A task that gives up on a door calls `cancel_wait`. Its held resources stay held. Only the wait is dropped, and the urgency it was lending goes with it.
146
+
147
+ ```ruby
148
+ arbiter = ArbiterMachines::Arbiter.new(
149
+ priorities: [1, 5, 10], ceilings: [10, 10], protocol: :inheritance
150
+ )
151
+ arbiter.acquire(task: 0, resource: 0)
152
+ arbiter.acquire(task: 2, resource: 0) # => [:blocked, 0]
153
+ arbiter.effective_priorities # => [10, 5, 10]
154
+
155
+ arbiter.cancel_wait(task: 2) # => true
156
+ arbiter.effective_priorities # => [1, 5, 10]
157
+ ```
158
+
159
+ ### Deadlock is refused, not reported after the fact
160
+
161
+ Two tasks each holding what the other wants is a loop. The arbiter sees it before charting the second wait and returns `:deadlock`. Nothing about the current state changes, so you can back out cleanly.
162
+
163
+ ```ruby
164
+ arbiter = ArbiterMachines::Arbiter.new(
165
+ priorities: [1, 5, 10], ceilings: [10, 10], protocol: :inheritance
166
+ )
167
+ arbiter.acquire(task: 0, resource: 0)
168
+ arbiter.acquire(task: 1, resource: 1)
169
+ arbiter.acquire(task: 0, resource: 1) # => [:blocked, 1]
170
+ arbiter.acquire(task: 1, resource: 0) # => [:deadlock, nil]
171
+ arbiter.effective_priorities # => [1, 5, 10] untouched
172
+
173
+ arbiter.cancel_wait(task: 0) # => true, task 0 backs off
174
+ arbiter.acquire(task: 1, resource: 0) # => [:blocked, 0] now it's a plain wait
175
+ ```
176
+
177
+ ### The other protocol: immediate ceiling
178
+
179
+ With `:immediate_ceiling`, a task jumps to the resource's ceiling the moment it takes the resource, before anyone has to wait. Nobody with a priority at or below the ceiling can get in behind it.
180
+
181
+ ```ruby
182
+ arbiter = ArbiterMachines::Arbiter.new(
183
+ priorities: [1, 5, 10], ceilings: [8, 10], protocol: :immediate_ceiling
184
+ )
185
+ arbiter.acquire(task: 0, resource: 0) # => [:granted, nil]
186
+ arbiter.effective_priorities # => [8, 5, 10] task 0 raised to the ceiling
187
+ arbiter.acquire(task: 1, resource: 1) # => [:blocked, 0] priority 5 can't clear a ceiling of 8
188
+ arbiter.acquire(task: 2, resource: 1) # => [:granted, nil] priority 10 can
189
+ ```
190
+
191
+ A ceiling has to be at least as high as every task that will ever take the resource. Task 2 at priority 10 asking for resource 0 with ceiling 8 is a misdeclaration, and the arbiter says so:
192
+
193
+ ```ruby
194
+ arbiter.acquire(task: 2, resource: 0) # => [:invalid, nil]
195
+ ```
196
+
197
+ ## The Rules of the Vessel
198
+
199
+ - **Up to 8 tasks and 8 resources**, chosen independently by the lengths of `priorities:` and `ceilings:`. Empty or longer arrays raise `ArgumentError`.
200
+ - **Priorities, ceilings, and indices are non-negative Integers** below `2**32`. Floats and strings raise `TypeError`, out-of-range raises `RangeError`.
201
+ - **Out-of-range task or resource indices** in `acquire` return `[:invalid, nil]`. In `cancel_wait` they return `false`. Nothing raises for a bad index, only for a bad type.
202
+ - **`effective_priorities` is a fresh Array** every call. Mutate it all you like, the arbiter doesn't care.
203
+ - **Frozen means frozen.** `freeze` the arbiter and `acquire`, `release`, and `cancel_wait` raise `FrozenError`. Reads still work.
204
+ - **Ceilings are always required**, even under `:inheritance`, where they're not consulted.
205
+
206
+ ## What You Think You're Doing vs Reality
207
+
208
+ ### You Think: "Priority 10 means it runs first."
209
+ ### Reality: Priority 10 means it waits first, behind whoever holds its lock.
210
+
211
+ A priority is a promise about scheduling, not about locks. The moment a high-priority task blocks on a lock held by a low-priority one, every medium task in the system is effectively more urgent than your most urgent work. Inheritance is what makes the number mean something again.
212
+
213
+ ## What's Yours
214
+
215
+ The arbiter never touches a mutex, a thread, or a scheduler. When it says `:granted`, you take the lock. When it says `:blocked`, you wait however you wait, then ask again after the holder releases. When it reports an effective priority, you're the one who has to tell your scheduler. Keep one arbiter per group of resources, and keep its calls serialized.
216
+
217
+ ## License
218
+
219
+ MIT License. See [LICENSE.txt](LICENSE.txt) for details.
220
+
221
+ **Remember: Nobody can hear the payment job scream while the janitor mops at priority 1.**
@@ -0,0 +1,22 @@
1
+ # Changelog
2
+
3
+ ## [0.2.2](https://github.com/seuros/arbiter_machines/compare/arbiter-machines-v0.2.1...arbiter-machines-v0.2.2) (2026-11-06)
4
+
5
+
6
+ ### Bug Fixes
7
+
8
+ * crates.io keywords and categories for the core ([10c8cad](https://github.com/seuros/arbiter_machines/commit/10c8cad0f35cb6c44dbe0e5573275f2267afc306))
9
+
10
+ ## [0.2.1](https://github.com/seuros/arbiter_machines/compare/arbiter-machines-v0.2.0...arbiter-machines-v0.2.1) (2026-11-06)
11
+
12
+
13
+ ### Bug Fixes
14
+
15
+ * keep test sources out of the packaged gem and crate ([513f507](https://github.com/seuros/arbiter_machines/commit/513f5074a2058f23742ee47b2289cd8b8872d6d3))
16
+
17
+ ## [0.2.0](https://github.com/seuros/arbiter_machines/compare/arbiter-machines-v0.1.0...arbiter-machines-v0.2.0) (2026-11-06)
18
+
19
+
20
+ ### Features
21
+
22
+ * initial extraction ([0ce828b](https://github.com/seuros/arbiter_machines/commit/0ce828bfacd5c9ef7482136b73d7be18339e19b9))
@@ -0,0 +1,20 @@
1
+ # arbiter-machines
2
+
3
+ > **AMS Dijkstra**, NCC-4201, *Dijkstra*-class traffic-control vessel.
4
+
5
+ Engine-room flight manual: coordinate airlock ownership without a circular wait.
6
+
7
+ Allocation-free `no_std` ownership and wait-for decisions for fixed tasks/resources.
8
+ Larger numeric priority means higher priority. One outstanding wait per task;
9
+ nonrecursive ownership. Releases wake affected waiters to retry but never silently
10
+ transfer ownership.
11
+
12
+ Inheritance propagates priorities transitively to owners. Immediate-ceiling mode
13
+ raises owners to held-resource ceilings and blocks acquisition unless effective
14
+ priority exceeds every ceiling held by another task. Each configured resource
15
+ ceiling must bound all tasks permitted to use it; requests exceeding it reject.
16
+ The core detects prospective wait-for cycles before inserting a wait.
17
+
18
+ These decisions require an executor honoring priorities and ownership. No OS
19
+ priority manipulation, lock implementation, waiter-queue fairness, preemption
20
+ driver, multi-resource atomic acquisition, or Ruby scheduling guarantee.
@@ -0,0 +1,3 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'ffi/extconf'
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ArbiterMachines
4
+ VERSION = '0.1.5'
5
+ end
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'arbiter_machines/version'
4
+ require 'arbiter_machines_native/arbiter_machines_native'
@@ -0,0 +1,12 @@
1
+ module ArbiterMachines
2
+ VERSION: String
3
+ class Arbiter
4
+ def self.new: (priorities: Array[Integer], ceilings: Array[Integer], protocol: :inheritance | :immediate_ceiling) -> instance
5
+ def acquire: (task: Integer, resource: Integer) -> [Symbol, Integer?]
6
+ def release: (task: Integer, resource: Integer) -> bool
7
+ def cancel_wait: (task: Integer) -> bool
8
+ def effective_priorities: () -> Array[Integer]
9
+ def task_count: () -> Integer
10
+ def resource_count: () -> Integer
11
+ end
12
+ end
metadata ADDED
@@ -0,0 +1,62 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: arbiter_machines
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.5
5
+ platform: aarch64-linux
6
+ authors:
7
+ - Abdelkader Boudih
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2026-11-06 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: Thin Ruby adapter to the standalone allocation-free arbiter-machines
14
+ Rust core. Initial unstable local implementation; caller enforces all decisions.
15
+ email:
16
+ - oss@seuros.com
17
+ executables: []
18
+ extensions: []
19
+ extra_rdoc_files: []
20
+ files:
21
+ - CHANGELOG.md
22
+ - LICENSE.txt
23
+ - README.md
24
+ - ext/arbiter_machines_native/core/CHANGELOG.md
25
+ - ext/arbiter_machines_native/core/README.md
26
+ - ext/arbiter_machines_native/extconf.rb
27
+ - lib/arbiter_machines.rb
28
+ - lib/arbiter_machines/version.rb
29
+ - lib/arbiter_machines_native/arbiter_machines_native.so
30
+ - sig/arbiter_machines.rbs
31
+ homepage: https://github.com/seuros/arbiter_machines
32
+ licenses:
33
+ - MIT
34
+ metadata:
35
+ source_code_uri: https://github.com/seuros/arbiter_machines
36
+ bug_tracker_uri: https://github.com/seuros/arbiter_machines/issues
37
+ rubygems_mfa_required: 'true'
38
+ cargo_crate_name: arbiter_machines_native
39
+ cargo_manifest_path: ext/arbiter_machines_native/ffi/Cargo.toml
40
+ post_install_message:
41
+ rdoc_options: []
42
+ require_paths:
43
+ - lib
44
+ required_ruby_version: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - ">="
47
+ - !ruby/object:Gem::Version
48
+ version: '4.0'
49
+ - - "<"
50
+ - !ruby/object:Gem::Version
51
+ version: 4.1.dev
52
+ required_rubygems_version: !ruby/object:Gem::Requirement
53
+ requirements:
54
+ - - ">="
55
+ - !ruby/object:Gem::Version
56
+ version: '0'
57
+ requirements: []
58
+ rubygems_version: 3.5.23
59
+ signing_key:
60
+ specification_version: 4
61
+ summary: Native bounded priority inheritance and resource ceiling decisions
62
+ test_files: []