Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix offset in acquire, inside() method #498

Merged
merged 1 commit into from Mar 30, 2023
Merged

Conversation

nicktindall
Copy link
Contributor

@nicktindall nicktindall commented Mar 21, 2023

Fixes #481

Hide whitespace when reviewing, one file had an extra indent through the whole file that I removed

else
acquireNextByteStore0(offset + adding - 1, false);
if (adding > 0 && !bytesStore.inside(offset, checkSize0(adding))) {
acquireNextByteStore0(offset, false);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The old else is the cause of #452, this bug is repeated throughout the bytes implementations. The basic error is

if (!bytesStore.inside(offset, length)) {
     bytesStore.acquire(offset + length);
}

This works most of the time when you're writing forwards sequentially because if what you're writing won't fit in currentStore + offset, it'll usually fit in the store that gets acquired (nextStore) because that will start at currentStore + 1

The error shows up in the forward direction when you jump ahead a cycle (see what I added to the test)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

e.g.

for a mapped file with chunksize 100, overlap 20

the sequential case

writeLong(50, some-long);   // positioned in the first chunk
writeLong(119, some-long);
  -> if (!bytesStore.inside(119, 8)) {     // false
          bytesStore.acquire(127);         // loads second store, which happens to contain 119->127, so OK

the non-sequential case (broken)

writeLong(50, some-long);   // positioned in the first chunk
writeLong(199, some-long);
  -> if (!bytesStore.inside(199, 8)) {     // false
          bytesStore.acquire(207);         // loads third store, which does not contain 199->207, so FAIL

the backwards traversal case (broken)

writeLong(150, some-long);   // positioned in the second chunk
writeLong(99, some-long);
  -> if (!bytesStore.inside(99, 8)) {     // false
          bytesStore.acquire(107);         // loads second store, which does not contain 99->107, so FAIL

if (!bytesStore.inside(offset, 1)) {
bytesStore = acquireNextByteStore0(offset + 1, false);
if (!bytesStore.inside(offset, 2)) {
bytesStore = acquireNextByteStore0(offset, false);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

example of the bug

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed, I saw that on Monday.

if (!bytesStore.inside(offset, 3)) {
bytesStore = acquireNextByteStore0(offset + 3, false);
if (!bytesStore.inside(offset, 4)) {
bytesStore = acquireNextByteStore0(offset, false);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

and again

if (!bytesStore.inside(offset, 7)) {
bytesStore = acquireNextByteStore0(offset + 7, false);
if (!bytesStore.inside(offset, 8)) {
bytesStore = acquireNextByteStore0(offset, false);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

and again

if (!bytesStore.inside(readPosition, 3)) {
bytesStore = acquireNextByteStore0(readPosition + 3, true);
if (!bytesStore.inside(readPosition, 4)) {
bytesStore = acquireNextByteStore0(readPosition, true);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

and again

public boolean inside(@NonNegative long offset, @NonNegative long buffer) {
return false;
public boolean inside(@NonNegative long offset, @NonNegative long bufferSize) {
return offset == 0 && bufferSize == 0;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

an array of zero length at position zero is inside the empty store

// go back to the first store
checkWritePosition(bytes, CHUNK_SIZE - 1, 0);
// write to just before the start of the third bytes store (i.e. not in the 1st plus overlap or 2nd plus overlap)
checkWritePosition(bytes, 3 * CHUNK_SIZE - size + 1, 2 * CHUNK_SIZE);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This reproduces the bug. If you pop the last commit (with the fix in it) off, you will see it blow.

* as the 1st byte is written at offset, and the last at offset+n-1
* <p>
* Use this test to determine if an offset is considered safe to write to. Note that it checks we are
* inside the BytesStore limits *including* the overlap
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I still feel strongly that we make this intuitive rather than writing a convoluted comment as to why it's not and how to use it "correctly".

@nicktindall nicktindall force-pushed the temp/overlap-vs-chunk branch 4 times, most recently from 10aedc9 to d47b069 Compare March 23, 2023 07:27
@nicktindall nicktindall changed the title For discussion, not merging DO NOT MERGE Fix offset in acquire, inside() method Mar 23, 2023
@@ -213,21 +212,21 @@ public long start() {
@Override
public boolean compareAndSwapInt(@NonNegative long offset, int expected, int value)
throws BufferOverflowException, IllegalStateException {
writeCheckOffset(offset, 4);
writeCheckOffset(offset, Integer.BYTES);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice

@nicktindall nicktindall force-pushed the temp/overlap-vs-chunk branch 2 times, most recently from 7317da2 to 80530a3 Compare March 30, 2023 06:43
…onstants, ensure overlapSize <= chunkSize, remove minimum overlapSize. Fixes #481
@sonarcloud
Copy link

sonarcloud bot commented Mar 30, 2023

Kudos, SonarCloud Quality Gate passed!    Quality Gate passed

Bug B 1 Bug
Vulnerability A 0 Vulnerabilities
Security Hotspot A 0 Security Hotspots
Code Smell A 0 Code Smells

75.2% 75.2% Coverage
2.6% 2.6% Duplication

@nicktindall nicktindall merged commit 9532b87 into develop Mar 30, 2023
1 check passed
@nicktindall nicktindall deleted the temp/overlap-vs-chunk branch March 30, 2023 06:59
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.

None yet

3 participants