-
Notifications
You must be signed in to change notification settings - Fork 329
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
Conversation
// 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, |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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!
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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 u32
in the match (that is, casting in the db_scan
parameters), in order to avoid recasting to *Region
.
There was a problem hiding this comment.
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)
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
Given Simon is on vacation and this is an important bugfix that doesn't affect other sections, I will merge this. |
There was a problem hiding this 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) |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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)
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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.
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.