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

Overhaul the API #42

Merged
merged 2 commits into from May 22, 2017

Conversation

Projects
None yet
3 participants
@bugaevc
Copy link
Contributor

bugaevc commented May 10, 2017

Overhaul the API as requested in #33. In particular,

  • Remove MmapView and MmapViewSync types
  • Define two types - Mmap and MmapMut for readable and readable/writable mappings respectively. They implement Deref<Target = [u8]>/DerefMut as appropriate.
    • Mmap has no flush methods.
    • Alongside "raw" set_protection(), provide make_mut() and make_read_only() to safely convert between Mmap and MmapMut.
  • Get rid of multiple open, open_path, anonymous_with_options, ... methods. Instead, provide two OpenOptions-style builders, one for file-backed and one for anonymous mappings.
    • Remove support for opening files by their path. Always use std::fs::File.
    • On Unix, memmap::file() is unsafe. This seems to be the best approach, although it is always safe to use it with ReadCopy.
    • To improve ergonomics, automatically pick a suitable Protection unless specified explicitly.
      • This makes it ergonomic enough so there's no need for additional convenience constructors.
  • Convert the tests and the cat example to use the new API.
    • use super as memmap doesn't seem to work, rustc says that super is not found because it's the root module, WTF? Yet, use super::* works. Check out the workaround I came up with.
@bugaevc

This comment has been minimized.

Copy link
Contributor Author

bugaevc commented May 10, 2017

The build is failing on Windows because of unnecessary unsafes (should I just #[allow] them?) and on Rust 1.8 because I used newer features such as the ? operator.

@jonas-schievink

This comment has been minimized.

Copy link
Contributor

jonas-schievink commented May 10, 2017

What happens if I create a MmapMut and call set_protection(Protection::Read)? Will a write just segfault?

@bugaevc

This comment has been minimized.

Copy link
Contributor Author

bugaevc commented May 10, 2017

What happens if I create a MmapMut and call set_protection(Protection::Read)? Will a write just segfault?

No, set_protection() will return Err appropriately.

An alternative approach would be to split Protection into separate enums for mutable and immutable options. That would allow for things like this to be enforced statically, but it seems to make APIs less ergonomic.

@jonas-schievink

This comment has been minimized.

Copy link
Contributor

jonas-schievink commented May 10, 2017

Ah, no, that's fine. I didn't see it in the diff because it wasn't getting rendered.

@danburkert
Copy link
Owner

danburkert left a comment

Hey @bugaevc, thanks for putting in the time to do this. I left some detailed nits, but I want to reiterate on a couple of higher level points:

  • I'd prefer if we kept the normal user facing API completely the same for Windows and Unix, even if that means overshooting with marking things unsafe on Windows. Having a consistent cross-platform API has always been the goal of this crate. We are planning on introducing platform-specific extension traits in the future, but that will be for exposing functionality that fundamentally isn't supported across platforms.

  • I think this would be easier to review if you split out the removal of the view APIs into a separate commit.

  • We probably need to think about what to do about Protections. I think it's pretty clear that they aren't the right abstraction given the split in Mmap and MmapMut. Anyone have thoughts for a better way forward?

CC #33, @sfackler, @BurntSushi, I'd appreciate you alls thoughts if you have any

src/lib.rs Outdated
/// then chain call methods to configure additional options, finally, call [`map()`](#method.map)
/// or [`map_mut()`](#method.map_mut).
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
pub struct AnonymousMappingOptions {

This comment has been minimized.

@danburkert

danburkert May 11, 2017

Owner

I think AnonymousMmapOptions is a better name for this, so it lines up with the type it's building.

src/lib.rs Outdated

/// Actually map this anonymous mapping into the address space.
///
/// This methos returns an immutable mapping, see [`map_mut()`](#method.map_mut)

This comment has been minimized.

@danburkert

danburkert May 11, 2017

Owner

typo: method

///
/// This method returns `Err` when the underlying system call fails, which can happen for
/// a variety of reasons, such as when you don't have the necessary permissions for the file.
pub fn map(&self) -> Result<Mmap> {

This comment has been minimized.

@danburkert

danburkert May 11, 2017

Owner

Does this constructor actually make sense? What's the usecase for creating a read-only anonymous memory map?

This comment has been minimized.

@bugaevc

bugaevc May 11, 2017

Author Contributor

Yeah, I thought about this too, but decided to leave it here for consistency. I should however document that this is not what you want most of the time.

This comment has been minimized.

@danburkert

danburkert May 12, 2017

Owner

I think we should go ahead and remove it unless there's a compelling reason to keep it at this point. We can always add it back later if a case can be made.

src/lib.rs Outdated
/// [`Protection::ReadCopy`](enum.Protection.html#variant.ReadCopy)
/// is always safe.
#[cfg(unix)]
pub unsafe fn file(file: &File) -> FileMappingOptions {

This comment has been minimized.

@danburkert

danburkert May 11, 2017

Owner

I would strongly prefer if this API were the same across all platforms, even if that means marking it unsafe on Windows.

This comment has been minimized.

@bugaevc

bugaevc May 11, 2017

Author Contributor

Okay.

src/lib.rs Outdated
/// In particular, it is **undefined behavior** in Rust for the memory to be
/// modified by some other code while there's a reference to it.
///
/// Note, however, that using this with

This comment has been minimized.

@danburkert

danburkert May 11, 2017

Owner

This has never really been clear to me. The Linux mmap manpage says:

It is unspecified whether changes made to the file after the mmap() call are visible in the mapped region.

That doesn't seem like it will play well with Rust's safety semantics.

This comment has been minimized.

@bugaevc

bugaevc May 11, 2017

Author Contributor

Ah, right. I'll change the message to say it's unsafe even with cow.

///
/// This method *also* returns `Err` with `ErrorKind` set to `InvalidInput` if the specified
/// protection does not allow the mapping to be mutable.
pub fn set_protection(&mut self, protection: Protection) -> Result<()> {

This comment has been minimized.

@danburkert

danburkert May 11, 2017

Owner

So this is just for switching from ReadWrite to ReadCopy and vice versa? That seems kinda strange - we should probably document what that even means. I'm surprised the OS allows it.

src/lib.rs Outdated
len: self.len,
}
/// This method will **not** return `Err` if the passed `protection` is mutable.
pub fn make_read_only(mut self, protection: Protection) -> Result<Mmap> {

This comment has been minimized.

@danburkert

danburkert May 11, 2017

Owner

Perhaps this could be make_read_only, and make_read_execute? Are there any other valid outcomes from this?

src/lib.rs Outdated
#![allow(unused_unsafe)]

mod memmap {
pub use super::super::*;

This comment has been minimized.

@danburkert

danburkert May 11, 2017

Owner

Is it not possible to do a use super::*; here without the extra mod? I've never seen anything like this.

This comment has been minimized.

@bugaevc

bugaevc May 11, 2017

Author Contributor

It is, I just wanted the tests code to say memmap::file instead of just file, etc. As I said above, use super as memmap doesn't work, this is either a bug or me misunderstanding how the module system works.

@@ -78,13 +77,7 @@ impl MmapInner {
}
}

/// Check if a file needs to be writable for copy-on-write mode.
#[inline]
pub fn needs_write_for_copy() -> bool {

This comment has been minimized.

@danburkert

danburkert May 11, 2017

Owner

Bad rebase?

This comment has been minimized.

@bugaevc

bugaevc May 11, 2017

Author Contributor

No, actually, this method isn't used anymore, because we require for the file to be opened in advance.

This comment has been minimized.

@danburkert

danburkert May 12, 2017

Owner

ah, I see

ErrorKind::InvalidInput,
"Invalid protection for a mutable mapping",
)),
Protection::ReadWrite | Protection::ReadCopy => Ok(

This comment has been minimized.

@danburkert

danburkert May 11, 2017

Owner

Does ReadCopy even make sense for an Anonymous mapping? I'm having a hard time reasoning through some of these semantics.

This comment has been minimized.

@bugaevc

bugaevc May 11, 2017

Author Contributor

No, not really; again, it's here for consistency. Do you think we should work out what combinations are actually useful and only allow those?

This comment has been minimized.

@danburkert

danburkert May 12, 2017

Owner

Yah, I think eventually we should, but we can punt for now and do it in a follow-up commit.

@bugaevc

This comment has been minimized.

Copy link
Contributor Author

bugaevc commented May 11, 2017

We could get rid of protection in public API entirely, instead exposing a bunch of methods: {map, make}_read_{only, write, execute, copy_on_write} for initial mapping and conversions where they make sense.

@bugaevc bugaevc force-pushed the bugaevc:overhaul-api branch 2 times, most recently from 1a768dc to 6384afc May 11, 2017

@bugaevc

This comment has been minimized.

Copy link
Contributor Author

bugaevc commented May 11, 2017

Rebased to separate MmapView removal and addressed a couple of pointed out issues: naming (Mapping -> Mmap in Options), a typo ("methos") and API difference between Unix and Windows (it's now always unsafe to mmap a file).

Please let me know what you think about my suggestion above.

@danburkert
Copy link
Owner

danburkert left a comment

Looks good, just a few comments. Could you switch the ? calls to try! so CI can pass?

///
/// This method returns `Err` when the underlying system call fails, which can happen for
/// a variety of reasons, such as when you don't have the necessary permissions for the file.
pub fn map(&self) -> Result<Mmap> {

This comment has been minimized.

@danburkert

danburkert May 12, 2017

Owner

I think we should go ahead and remove it unless there's a compelling reason to keep it at this point. We can always add it back later if a case can be made.

ErrorKind::InvalidInput,
"Invalid protection for a mutable mapping",
)),
Protection::ReadWrite | Protection::ReadCopy => Ok(

This comment has been minimized.

@danburkert

danburkert May 12, 2017

Owner

Yah, I think eventually we should, but we can punt for now and do it in a follow-up commit.

@@ -78,13 +77,7 @@ impl MmapInner {
}
}

/// Check if a file needs to be writable for copy-on-write mode.
#[inline]
pub fn needs_write_for_copy() -> bool {

This comment has been minimized.

@danburkert

danburkert May 12, 2017

Owner

ah, I see

@danburkert

This comment has been minimized.

Copy link
Owner

danburkert commented May 12, 2017

I merged the view removal commit, so you may need to rebase over that.

@bugaevc bugaevc force-pushed the bugaevc:overhaul-api branch from 6384afc to 3ffc31b May 12, 2017

@bugaevc

This comment has been minimized.

Copy link
Contributor Author

bugaevc commented May 12, 2017

  • Removed AnonymousMmapOptions::map() and the corresponding test.
  • Made it compile under 1.8.0 (why do you need that?)
@danburkert

This comment has been minimized.

Copy link
Owner

danburkert commented May 12, 2017

Awesome, thanks. As for Rust 1.8, there is no hard and fast requirement except that that's the status quo, so if it's not too onerous to maintain I'd prefer to. This crate is likely to be a widely used dependency, and if it forces a certain rustic version, then it's forced on everyone who uses it as well. Basically the same reason that libc maintains long backwards compat windows.

Will take a look and likely merge tonight or over the weekend. Thanks again for the help!

@bugaevc

This comment has been minimized.

Copy link
Contributor Author

bugaevc commented May 12, 2017

Great! So, if you think this:

We could get rid of protection in public API entirely, instead exposing a bunch of methods: {map, make}_read_{only, write, execute, copy_on_write} for initial mapping and conversions where they make sense.

is worth a try! 😄 , I'll make a separate PR, is that OK?

@bugaevc

This comment has been minimized.

Copy link
Contributor Author

bugaevc commented May 12, 2017

Also note that I haven't put much work into docs here, that's left for #32 and #34

@bugaevc

This comment has been minimized.

Copy link
Contributor Author

bugaevc commented May 20, 2017

Will take a look and likely merge tonight or over the weekend.

Any news?

@danburkert danburkert merged commit da7000b into danburkert:master May 22, 2017

2 checks passed

continuous-integration/appveyor/pr AppVeyor build succeeded
Details
continuous-integration/travis-ci/pr The Travis CI build passed
Details
@danburkert

This comment has been minimized.

Copy link
Owner

danburkert commented May 22, 2017

Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.