-
Notifications
You must be signed in to change notification settings - Fork 64
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
define wrapper for C FAM structs #4
define wrapper for C FAM structs #4
Conversation
src/fam_struct_wrapper.rs
Outdated
let mut mem_allocator = Vec::with_capacity(required_mem_allocator_capacity); | ||
for _ in 0..required_mem_allocator_capacity { | ||
mem_allocator.push(T::default()) | ||
} |
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.
Should we initialize memory other than first element to zero instead of T::default()? T::default() may generate some non-zero content.
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.
Good catch. Done.
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.
Well, then we should revert to zero-initialized instead of default-initialized in the method doc comment above.
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.
Done.
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.
FYI I can't get coverage with cargo kcov
, could you?
test fam_struct_wrapper::tests::test_reserve ... ok
kcov: Process exited with signal 4 (SIGILL) at 0x7ffff6ce00e3
kcov: Illegal instructions are sometimes caused by some GCC versions
kcov: miscompiling C++ headers. If the problem is persistent, try running
kcov: with --verify. For more information, see
kcov: http://github.com/SimonKagstrom/kcov/issues/18
src/fam_struct_wrapper.rs
Outdated
|
||
use std::mem::size_of; | ||
|
||
/// Errors associated with the FamStructWrapper struct. |
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.
/// Errors associated with the FamStructWrapper struct. | |
/// Errors associated with the [`FamStructWrapper`](struct.FamStructWrapper.html) struct. |
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.
Fixed
src/fam_struct_wrapper.rs
Outdated
use std::mem::size_of; | ||
|
||
/// Errors associated with the FamStructWrapper struct. | ||
#[derive(Debug, Clone)] |
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.
nit: order derives alphabetically
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.
Done
src/fam_struct_wrapper.rs
Outdated
type Entry: PartialEq + Copy; | ||
|
||
/// Get the array length | ||
/// |
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.
nit: I don't think the extra newline improves readability here.
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. Fixed.
src/fam_struct_wrapper.rs
Outdated
fn as_mut_slice(&mut self) -> &mut [Self::Entry]; | ||
} | ||
|
||
/// A wrapper that helps in treating a `FamStruct` similarly to an actual `Vec`. |
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.
/// A wrapper that helps in treating a `FamStruct` similarly to an actual `Vec`. | |
/// A wrapper that helps in treating a [`FamStruct`](trait.FamStruct.html) similarly to an actual `Vec`. |
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.
Fixed
src/fam_struct_wrapper.rs
Outdated
/// # Arguments | ||
/// | ||
/// * `num_elements` - The number of default-initialized elements of type | ||
/// `FamStruct::Entry` in the initial `FamStructWrapper` |
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.
/// `FamStruct::Entry` in the initial `FamStructWrapper` | |
/// [`FamStruct::Entry`](trait.FamStruct.html#associatedtype.Entry) in the initial [`FamStructWrapper`](struct.FamStructWrapper.html) |
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.
Fixed
src/fam_struct_wrapper.rs
Outdated
} | ||
} | ||
|
||
/// Creates a new `FamStructWrapper` structure based on a supplied slice |
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.
/// Creates a new `FamStructWrapper` structure based on a supplied slice | |
/// Creates a new [`FamStructWrapper`](struct.FamStructWrapper.html) structure based on a supplied slice |
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.
Fixed
src/fam_struct_wrapper.rs
Outdated
} | ||
|
||
/// Creates a new `FamStructWrapper` structure based on a supplied slice | ||
/// of `FamStruct::Entry`. |
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.
/// of `FamStruct::Entry`. | |
/// of [`FamStruct::Entry`](trait.FamStruct.html#associatedtype.Entry). |
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.
Fixed
src/fam_struct_wrapper.rs
Outdated
/// | ||
/// # Arguments | ||
/// | ||
/// * `entries` - The vector of `FamStruct::Entry` entries. |
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.
/// * `entries` - The vector of `FamStruct::Entry` entries. | |
/// * `entries` - The vector of [`FamStruct::Entry`](trait.FamStruct.html#associatedtype.Entry) entries. |
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.
Fixed
src/fam_struct_wrapper.rs
Outdated
adapter | ||
} | ||
|
||
/// Get a reference to the actual `FamStruct` instance. |
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.
/// Get a reference to the actual `FamStruct` instance. | |
/// Get a reference to the actual [`FamStruct`](trait.FamStruct.html) instance. |
This applies to all the following mentions too, I'll stop with the suggestions 😄
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.
Fixed
@alexandruag I know you had some comments on this PR. Can you please add them here in case they weren't already fixed? I will also check if the comments I had were resolved. |
Yes. 98.9% coverage for |
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.
Sorry about the large number of rename related comments. I think they can make the code easier to use/understand, but I realize this is also a subjective perspective. Looking forward to other ppl's opinion as well.
src/lib.rs
Outdated
@@ -10,6 +10,7 @@ pub mod ioctl; | |||
|
|||
pub mod errno; | |||
pub mod eventfd; | |||
pub mod fam_struct_wrapper; |
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 think about calling the new module simply fam
? fam_struct_wrapper
looks a bit ugly to me :(
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.
Sounds good. Done.
src/fam_struct_wrapper.rs
Outdated
/// ``` | ||
/// | ||
#[allow(clippy::len_without_is_empty)] | ||
pub trait FamStruct { |
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 ppl think about renaming this to Fam
and the wrapper struct to FamWrapper
? The shorter names seem a lot nicer to me :(
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.
Using Fam
here wouldn't be completely accurate.
FAM = Flexible array member. This is the field in the structure. Not the structure itself.
I would keep it as it is.
src/fam_struct_wrapper.rs
Outdated
// which must be contiguous. Since the entries are of type `FamStruct::Entry` we must | ||
// be careful to convert the desired capacity of the `FamStructWrapper` | ||
// from `FamStruct::Entry` to `T` when reserving or releasing memory. | ||
mem_allocator: Vec<T>, |
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.
How about renaming this to simply allocator
? (applying this to other mem_allocator
strings/substrings in the rest of the file as well)
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 would keep it as it is. I don't see a great benefit in doing this. It would decrease the length of some method names but it would also make the names less suggestive.
src/fam_struct_wrapper.rs
Outdated
/// Get the capacity required by mem_allocator in order to hold | ||
/// the provided number of [`FamStruct::Entry`](trait.FamStruct.html#associatedtype.Entry) | ||
/// | ||
fn mem_allocator_len(wrapper_len: usize) -> usize { |
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 think reading this function becomes easier if we change the name of the parameter from wrapper_len
to something like num_entries
.
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 renamed it to fam_len
src/fam_struct_wrapper.rs
Outdated
/// [`FamStruct::Entry`](trait.FamStruct.html#associatedtype.Entry) elements. | ||
/// If the capacity is already reserved, this method doesn't do anything. | ||
/// | ||
fn reserve(&mut self, additional: usize) { |
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.
It might be worth noting in the docs that calls to reserve
(and other methods based on it, for example set_let
) eventually lead to Vec::reserve
being called, which may end up doing a realloc
of the underlying buffer, and that invalidates all previous pointers obtained via methods such as as_ptr
, etc.
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 added comments related to this.
src/fam_struct_wrapper.rs
Outdated
/// This is needed after `self.as_ptr()` is supplied as param to an FFI call | ||
/// which may change it's state. | ||
/// | ||
pub fn sync(&mut self) -> Result<(), Error> { |
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.
Does this function make sense when new_len > self.capacity
? It looks like that should be an error condition, because it means someone effectively wrote past the end of our allocator.
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.
True. Fixed.
src/fam_struct_wrapper.rs
Outdated
|
||
impl<T: Default + FamStruct> PartialEq for FamStructWrapper<T> { | ||
fn eq(&self, other: &FamStructWrapper<T>) -> bool { | ||
self.len == other.len && self.as_slice() == other.as_slice() |
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.
Sorry I know I asked this regarding the old PR as well, but I don't remember the conclusion :( Is it a valid occurence to have self.as_slice() == other.as_slice()
, but self.len != other.len
?
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.
Yes, if sync()
isn't called.
It's possible to have for example:
cpuid_1 = CpuId::new(100);
cpuid_2 = CpuId::new(110);
// here the inner fam_struct of cpuid_1 will be modified by kvm. Let's say that it will have len = 50
call_to_kvm(cpuid_1.as_mut_fam_struct_ptr());
// here the inner fam_struct of cpuid_2 will be modified by kvm. Let's say that it will have len = 50
call_to_kvm(cpuid_2.as_mut_fam_struct_ptr());
After this cpuid_1.as_slice() == cpuid_2.as_slice() .
But cpuid_1.len() = 100 while cpuid_2.len = 110.
src/fam_struct_wrapper.rs
Outdated
/// | ||
/// # Error: When len is greater than the max possible len it returns Error::SizeLimitExceeded | ||
/// | ||
fn set_len(&mut self, len: usize) -> Result<(), Error> { |
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.
It seems that when set_len
causes the length to increase, the newly added elements are based on basically random memory contents (I don't think reserving capacity also initializes the memory to a particular value). If that's true, is this a part of the set_len
method contract, or we should try to zero-initialize the new elements like we do for new
?
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.
For the moment set_len is used to increase the lenght of the array only when pushing. So it should be safe as it is. I don't know if in the future there will be any case where we would need to zero-initialize the memory.
src/fam_struct_wrapper.rs
Outdated
/// ``` | ||
/// | ||
#[allow(clippy::len_without_is_empty)] | ||
pub trait FamStruct { |
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.
Also, I think we should mark this trait as unsafe
, just so the user gives an explicit sign-off that the implementation adheres to the conventions established by the trait, including that any type implementing this must be valid to initialize from any random bits. Without this assumption,we can't be sure that zero-initializing the memory in new
and using Vec::set_len
are actually safe to do in the implementation of FamStructWrapper
. What do you ppl think?
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.
Makes sense. Done.
src/fam.rs
Outdated
/// | ||
/// This method might trigger reallocations of the underlying buffer. | ||
/// | ||
/// # Error: When len is greater than the max possible len it returns Error::SizeLimitExceeded |
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.
This will look a bit weird in markdown. You basically have a heading title that is very very long.
Please use as heading title Errors
as this is the standard defined in the Rust documentation guidelines (even if the function can return just on type of error). The explanation for the error needs to be in a paragraph.
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.
Done
What's the plan with this PR? I saw this pattern has been repeated in several crates, so it may help to reduce common code. |
We have to discuss the final details related to this comment. I'll try to sync with @alexandruag about this as soon as possible. |
I've had another look and for now there are no other pressing concerns from my point of view. I was worried before there were dangerous corner cases when the size of |
/// ``` | ||
/// | ||
#[allow(clippy::len_without_is_empty)] | ||
pub unsafe trait FamStruct { |
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.
Could you please add some documentation on this trait? Some references here
Line 72 in 9014b7a
pub unsafe trait Terminal { |
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 added just a couple of lines. I couldn't think of any other details that would need to be specified. Please let me know if anything is not clear.
|
||
/// A wrapper that helps in treating a [`FamStruct`](trait.FamStruct.html) similarly to | ||
/// an actual `Vec`. | ||
/// |
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.
Blank line is useless if no other description.
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.
Could you please kindly separate the documentation into a short summary line of its functionality, and more explanation in a new paragraph? Ditto for others below. EventFd structure in eventfd.rs is a reference for you.
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.
Done
src/fam.rs
Outdated
/// * `num_elements` - The number of zero-initialized elements of type | ||
/// [`FamStruct::Entry`](trait.FamStruct.html#associatedtype.Entry) | ||
/// in the initial [`FamStructWrapper`](struct.FamStructWrapper.html) | ||
/// |
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.
Yep, good to see the detailed #Arguments description. :)
src/fam.rs
Outdated
desired_len = 5; | ||
assert!(adapter.set_len(desired_len).is_ok()); | ||
assert_eq!(adapter.len(), desired_len); | ||
// check that the capacity |
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.
Useless comment?
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.
fixed
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.
Some comments as mentioned.
In C an array of unknown size may appear within a struct definition as the last member (as long as there is at least one other named member). This is a known as flexible array member. Pre C99, the same behavior could be achieved using zero length arrays. Flexible Array Members are the go-to choice for working with large amounts of data prefixed by header values. KVM uses this type of structures (e.g., kvm_cpuid2, kvm_msr_list, etc.) FamStructWrapper provides a common abstraction for this type of structures and helps in treating them similarly to Rust Vec. Signed-off-by: Serban Iorga <seriorga@amazon.com>
/// pub unsafe fn as_mut_slice(&mut self, len: usize) -> &mut [T] { | ||
/// ::std::slice::from_raw_parts_mut(self.as_mut_ptr(), len) | ||
/// } | ||
/// } |
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.
Could the __IncompleteArrayField struct be exported as a pub generic type, seems it only contains generic code.
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.
This struct is generated automatically by bindgen. You can find it for example in the kvm-bindings project. I needed to copy it just for the unit tests and the example. I don't know if it adds any value exporting it.
In C an array of unknown size may appear within a struct definition
as the last member (as long as there is at least one other named member).
This is a known as flexible array member.
Pre C99, the same behavior could be achieved using zero length arrays.
Flexible Array Members are the go-to choice for working with large
amounts of data prefixed by header values.
KVM uses this type of structures (e.g., kvm_cpuid2, kvm_msr_list, etc.)
FamStructWrapper provides a common abstraction for this type of
structures and helps in treating them similarly to Rust Vec.
Signed-off-by: Serban Iorga seriorga@amazon.com