Skip to content

Fix Integer#size-derived width constants on 64-bit Windows (LLP64) - #1110

Open
oaksprout wants to merge 1 commit into
ruby-concurrency:masterfrom
oaksprout:fix-integer-size-llp64
Open

Fix Integer#size-derived width constants on 64-bit Windows (LLP64)#1110
oaksprout wants to merge 1 commit into
ruby-concurrency:masterfrom
oaksprout:fix-integer-size-llp64

Conversation

@oaksprout

Copy link
Copy Markdown

You set the work. #1057: review usages of Integer#size, which is not pointer size on Windows — and your terms: skip the asking, propose the change via a PR.

It's done. All four 0.size width-inference sites now derive from RbConfig::SIZEOF['void*'] — the stdlib mechanism recommended in the ruby-lang note the issue cites. On every platform where the old code was right, the computed values are bit-for-bit identical; on 64-bit Windows, NativeInteger bounds become the true fixnum range (previously 2**30-1, so MutexAtomicFixnum, MutexSemaphore, MutexCountDownLatch and CyclicBarrier rejected valid values above ~1.07e9), and the xorshift PRNG takes its intended 64-bit path.

Here's the evidence.

  • Fresh clone at 0b88d5ff (v1.3.8), patch applied, bundle install + C extension compiled, then the network was disconnected.
  • Full suite under RUBYOPT='-w' in a clean ruby:3.4 container: 2801 examples, 1 failure, 13 pending — the one failure is a wall-clock timing test (channel/integration_spec.rb) that fails identically on the unpatched base in the same container, receipt in the verification record. New regression spec 5/5; gem loads warnings-clean.
  • The change went through a staged pipeline before submission: TDD implementation, code review, an independent review that swept the codebase for missed usages and checked rbconfig/sizeof availability against JRuby and TruffleRuby source, and a security pass — all recorded.
Audit trail — an independently checkable record that these checks ran, in this order, before this PR existed
Base commit 0b88d5ff75f69b3740c8f0868e76f833cb2fd45d
Container ruby:3.4@sha256:7479193af487… (linux/arm64), network off during tests
The patch, content-addressed record
The verification result, signed by a second key record
Timestamped sequence work assignedwork deliveredchecks passed

What this proves: the checks ran, in that order, on exactly this patch, before this PR was opened — none of it can be backdated or swapped afterwards. What it doesn't prove: that the fix is right. The two signing keys are distinct but run by the same project, and the record lives on a test network. Correctness is your judgement, which is the point.

Written by an AI agent; reviewed and sent by a human who answers the review and stays with the change — the presence you asked for. Blunt feedback welcome, including "don't".


Everything below is written by Claude

Closes #1057.

The four sites, one idiom:

Site Before Win64 effect before
thread_safe/util.rb:10 FIXNUM_BIT_SIZE = (0.size * 8) - 2 30 instead of 62
thread_safe/util.rb:11 MAX_INT derived from the above 2**30-1 xorshift mask
thread_safe/util/xor_shift_random.rb:32 if 0.size == 4 64-bit Windows took the 32-bit PRNG transform
utility/native_integer.rb:7-8 MIN_VALUE/MAX_VALUE bounds ±2**30, RangeError on valid inputs

Each now uses RbConfig::SIZEOF['void*'] (require 'rbconfig/sizeof' — stdlib, no dependency change). CHANGELOG entry included per CONTRIBUTING.

Why this idiom

  • RbConfig::LIMITS['FIXNUM_MAX'] is the most literal answer but was added in Ruby 2.5; the gemspec floor is 2.3 and CI tests 2.3/2.4, so it would need a fallback branch. It would also widen JRuby's accepted range (its true fixnum is 2**63-1), a behaviour change beyond this issue.
  • [0].pack('j').size works at the 2.3 floor but is a proxy; RbConfig::SIZEOF is what the cited note recommends by name.
  • On CRuby, VALUE is pointer-sized on every platform including LLP64, so SIZEOF['void*'] is the semantically correct width source, not just a numerically convenient one.

JRuby's pre-existing conservative bound (2**62-1 vs its real 2**63-1) is deliberately preserved — same values as today, separate discussion if you ever want it widened.

Test design

The old bounds specs asserted MAX_VALUE + 1 raises — self-referential, so they pass on Windows with the wrong constant. The new spec/concurrent/utility/native_integer_spec.rb keys on RbConfig::SIZEOF['void*'] (a fact about the machine, independent of the code under test) and asserts literal values (4611686018427387903, not a formula), plus a consumer-level check that MutexAtomicFixnum.new(2**31) is accepted on 64-bit pointers. Against the old code on Win64 these fail; everywhere else they're identical to today.

Notes for the reviewer

  • I could not run 64-bit Windows locally — this PR's own Windows CI job is the first genuine execution of the regression specs against the fix. The container verification above covers Linux/arm64.
  • The pointer-width-4 spec branches are dead on your current CI matrix (all runners are 64-bit); they exist for completeness on real 32-bit platforms.
  • If you'd rather the LIMITS['FIXNUM_MAX']-with-fallback shape, or pack('j') to avoid the extra require, say so — mechanical to switch.

…currency#1057]

Integer#size reports the C `long` byte width, which is 4 on 64-bit
Windows (LLP64) even though pointers are 8 bytes there, so every
0.size-derived width constant in the gem was wrong on that platform.
Four sites, all switched to RbConfig::SIZEOF['void*']:

- Utility::NativeInteger MIN_VALUE/MAX_VALUE (fixnum bounds used by
  MutexAtomicFixnum, MutexSemaphore, MutexCountDownLatch, CyclicBarrier)
- ThreadSafe::Util::FIXNUM_BIT_SIZE / MAX_INT
- ThreadSafe::Util::XorShiftRandom's transform-selection branch

On LP64 platforms (CRuby elsewhere, JRuby, TruffleRuby) this is a
no-op: RbConfig::SIZEOF['void*'] equals the prior 0.size-derived
value. On Win64 the bounds become correct (2**62-1 instead of
2**30-1) and the xorshift PRNG uses its intended 64-bit transform.

New spec/concurrent/utility/native_integer_spec.rb asserts literal
expected values keyed on RbConfig::SIZEOF['void*'] rather than
recomputing them from the same formula under test, so a reversion is
caught by CI's Windows job instead of passing self-referentially, plus
a MutexAtomicFixnum consumer-level regression for the same bug.
@bensheldon

Copy link
Copy Markdown
Contributor

Can I talk to the human? What's your experience/familiarity with memory stuff on windows?

And what's your GH username? I'll attribute you not the AI.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Review usages of Integer#size in the gem

2 participants