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 external storage range #511

Merged
merged 8 commits into from
Aug 25, 2020
Merged

Fix external storage range #511

merged 8 commits into from
Aug 25, 2020

Conversation

ethanfrey
Copy link
Member

@ethanfrey ethanfrey commented Aug 25, 2020

Closes #508
Builds on test case reported by #509 and develops a solution.

All discussion was in #509, but then wanted to change the branch name properly.

// thus they are not inside a block
let start = start.map(|s| build_region(s));
let start_ptr = match start {
Some(reg) => &*reg as *const Region as u32,
Copy link
Contributor

Choose a reason for hiding this comment

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

Just out of curiosity, I wonder why these fail in the vm context.

Copy link
Member Author

Choose a reason for hiding this comment

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

They return something, but likely a pointer to stack, which is overwritten, not a pointer to heap.

Copy link
Contributor

Choose a reason for hiding this comment

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

I see. The db_scan is out of scope!

Copy link
Contributor

Choose a reason for hiding this comment

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

what do you mean by out of scope?
Looking at this diff it's not clear to me what the difference is between the two versions.

Copy link
Member Author

Choose a reason for hiding this comment

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

Me either. But the compiler does something so they work very different.

Check out the WIP: lots of debug println commit and run it, and then switch out what is commented out. This is odd behavior, but we are grabbing a result that was created in a closure and dereferenced inside an Option, and I am assuming something happens a bit odd, so we get a stack pointer not a heap pointer.

Copy link
Contributor

@maurolacy maurolacy Aug 25, 2020

Choose a reason for hiding this comment

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

I guess there are no safe (in the sense of compiler warnings and such) ways to do these mappings between languages / frameworks.

Copy link
Contributor

@maurolacy maurolacy Aug 25, 2020

Choose a reason for hiding this comment

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

OK, I was looking into this, and the alternative way to do it, if you cannot avoid using a raw pointer, is to use Box::into_raw. The advantage is that the returned pointer is to the original value on the heap. So, it avoids copying of the value to the stack, and the scoping issues.

The code also ends up being somewhat clearer:

        let start_ptr = match start {
            Some(reg) => Box::into_raw(reg) as u32,
            None => 0,
        };
        let end = end.map(|e| build_region(e));
        let end_ptr = match end {
            Some(reg) => Box::into_raw(reg) as u32,
            None => 0,
        };

Now, after into_raw you're dealing with a raw (therefore unmanaged) pointer. Being on the heap, it's your responsibility to release it. It has to be manually released with something like:

        if start_ptr != 0 {
            unsafe { drop(Box::from_raw(start_ptr as *mut Region)); }
        }
        if end_ptr != 0 {
            unsafe { drop(Box::from_raw(end_ptr as *mut Region)); }
        }

This can still probably be improved a bit by avoiding the cast tu u32in the match (that is, casting in the db_scan parameters), in order to avoid recasting to *Region.

Copy link
Contributor

@maurolacy maurolacy Aug 25, 2020

Choose a reason for hiding this comment

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

Something like:

        let start = start.map(|s| build_region(s));
        let start_ptr = match start {
            Some(reg) => Box::into_raw(reg),
            None => null_mut(),
        };
        let end = end.map(|e| build_region(e));
        let end_ptr = match end {
            Some(reg) => Box::into_raw(reg),
            None => null_mut(),
        };
        let iterator_id = unsafe { db_scan(start_ptr as u32, end_ptr as u32, order as i32) };
        let iter = ExternalIterator { iterator_id };
        if start_ptr != null_mut() {
            unsafe { Box::from_raw(start_ptr); }
        }
        if end_ptr != null_mut() {
            unsafe { Box::from_raw(end_ptr); }
        }
        Box::new(iter)

Copy link
Member Author

Choose a reason for hiding this comment

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

Nice suggestion. Do you want to make a branch with those changes and see if it passes tests?

I actually kind of like the current way as it is more explicit and fewer if statments. But the Box::into_raw is a nice addition as well.

You can make a PR proposing this as a cleaner logic and we can iterate on it and see if anyone has better ideas. I really wanted to get the thing fixed first of all, so iterators work in the contracts, but happy for a refined version in the future. Whatever is easiest to read really.

Copy link
Contributor

@maurolacy maurolacy Aug 26, 2020

Choose a reason for hiding this comment

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

Sure. I tested it, and it works. I'll make a PR.

@ethanfrey
Copy link
Member Author

Given Simon is on vacation and this is an important bugfix that doesn't affect other sections, I will merge this.

@ethanfrey ethanfrey merged commit 50b4918 into master Aug 25, 2020
@ethanfrey ethanfrey deleted the fix-external-storage-range branch August 25, 2020 17:47
Copy link
Member

@webmaster128 webmaster128 left a comment

Choose a reason for hiding this comment

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

Nice one

.collect();
let early: Vec<u32> = deps
.storage
.range(None, Some(b"\x20a"), Order::Ascending)
Copy link
Member

Choose a reason for hiding this comment

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

Why is there an additional a byte in this limit?

Copy link
Member Author

Choose a reason for hiding this comment

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

End is exclusive I believe and I wanted to ensure we included it.
(I know start is inclusive)

Copy link
Member

Choose a reason for hiding this comment

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

Ah, I see. Any reason for having the contract's API flipped compared to the storage access?

Storage: start included; end excluded
API: start excluded (in late); end included (in early)

If we change the API to

pub struct ListResponse {
    /// List an empty range, both bounded
    pub empty: Vec<u32>,
    /// List all IDs lower than 0x20
    pub early: Vec<u32>,
    /// List all IDs starting from 0x20
    pub late: Vec<u32>,
}

this gets simpler

Copy link
Member Author

Choose a reason for hiding this comment

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

Sure, sounds proper. Please make an issue or a PR for this. I'll be doing orga for the next day or two and not get to coding very soon.

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.

Integration tests fail with "VM error"
4 participants