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

Add File::try_clone #31069

Merged
merged 1 commit into from
Feb 4, 2016
Merged

Add File::try_clone #31069

merged 1 commit into from
Feb 4, 2016

Conversation

sfackler
Copy link
Member

I have it set as stable right now under the rationale that it's extending an existing, stable API to another type in the "obvious" way.

r? @alexcrichton

cc @reem

@reem
Copy link
Contributor

reem commented Jan 21, 2016

Looks great; looking forward to removing my much more fragile platform specific code.

@alexcrichton
Copy link
Member

This seems reasonable to me, although I will cc @rust-lang/libs and tag with it to make sure we're all good with the insta-stablilization.

@alexcrichton alexcrichton added the T-libs-api Relevant to the library API team, which will review and decide on the PR/issue. label Jan 21, 2016
@oli-obk
Copy link
Contributor

oli-obk commented Jan 21, 2016

I know this can't cause memory unsafety, but isn't it very unexpected to be able to modify a file from two threads simultaneously (without additional synchronization, writing to the file will be very amusing at best)

@alexcrichton
Copy link
Member

@oli-obk I suspect so, yes, but there are uses now and then which are nice if the standard library supports them. That being said I can't particularly think of a concrete reason to have this, but perhaps @sfackler or @reem had one in mind to motivate this?

@sfackler
Copy link
Member Author

@oli-obk I hate to be the bearer of unexpected news, but that is already the case: Read and Write are implemented for &File, and all of the state mutation methods on it take &self. This just makes it more ergonomic to work with.

@alexcrichton I implemented this at @reem's request, so he'd be able to better speak to the use cases.

@oli-obk
Copy link
Contributor

oli-obk commented Jan 21, 2016

Ugh, ugly. Still, there you are at least bound to an instance. So what's the difference between Rc<File> right now and File in this PR, except that the "sharedness" is hidden?

@sfackler
Copy link
Member Author

It's pretty annoying to access the impls for &File. Calling foo.read(buf) where foo is an Arc<File> will try to select the implementation for File and fail to compile: http://is.gd/RnmzQZ. Since the impl is for &File and not Arc<File>, you can't use it in contexts that want a 'static Reader.

@reem
Copy link
Contributor

reem commented Jan 22, 2016

Hmm, I actually could get by in my case with an Arc<File> (I have a case where I would normally option-dance around the File, but I would rather avoid keeping the File in an Option for all the other cases), but why implement my own reference counting on top of the underlying os's reference count?

EDIT: For clarity, the case is moving from one enum variant which contains a File to another enum variant which should contain the same File with an &mut T where T is the enum.

@oli-obk
Copy link
Contributor

oli-obk commented Jan 22, 2016

I don't know how powerful impl specialization would be, but could it create a special impl for Rc<File> and Arc<File> that uses the filehandle reference counting instead moving the File object to the heap?

@retep998
Copy link
Member

@oli-obk That seems overly complicated and would be a lot of complexity when simply duplicating a file handle can be done now fairly easily with just a single public method.

@oli-obk
Copy link
Contributor

oli-obk commented Jan 22, 2016

I'm aware of that. But it's much clearer that Rc<File> or Arc<File> are shared and File is owned. Without using hypothetical impl specialization it would still work although with a little overhead.


Since the borrowing semantics of File are broken already, we might just go all the way. Then we'd also need a way to obtain the reference count so we can figure out if we truly own the handle. Also some more documentation about this deviation from ownership semantics would be good.

@retep998
Copy link
Member

Even if you duplicate a File both Files still own their handles, the handles just happen to have some shared state. Also, as far as I can tell there is no way to get the reference count of a handle on Windows.

@oli-obk
Copy link
Contributor

oli-obk commented Jan 22, 2016

Yes, but shared ownership is represented through Rc or Arc in Rust. At least I don't know of any other shared ownership types.

Also, as far as I can tell there is no way to get the reference count of a handle on Windows.

It's probably not important anyway. We can always get true ownership back through a newtype that doesn't give access to the internal value. Still a sad deviation, but one that's already happened, now we gotta live with it.

@alexcrichton
Copy link
Member

@oli-obk I at least would consider us to not perform that sort of specialization or optimization in the future. There are some use cases where you want an entirely separate handle and/or file descriptor to pass to a library which it itself calls close on, for example. That means that there's a semantic difference between an OS-reference-counted File and Rc<File>. The former case has whole new file descriptors allocate where the latter all shares the same file descriptor.

@bluss
Copy link
Member

bluss commented Jan 22, 2016

insta-stabilizing seems wrong (why is this PR special)? If any time is right for it, it's this week though; it will still need 11 weeks to reach the stable release.

@sfackler
Copy link
Member Author

Fair enough, I'm fine with landing it unstable. I'll update it tonight.

@sfackler
Copy link
Member Author

Updated to be unstable.

@aturon
Copy link
Member

aturon commented Jan 27, 2016

I'm fine with this functionality, under the rubric of exposing cross-platform IO APIs that apply to our various IO objects (part of the vision of IO reform).

The name isn't great, though. If you succeed here, you haven't really cloned, you've created a new alias. (And we also avoid try for Result-returning operations). How about duplicate_handle? Verbose, but I don't think this will be called very often in Rust.

@alexcrichton
Copy link
Member

@aturon note, however, that we have already stabilized TcpStream::try_clone, so I believe this is just mirroring that

@BurntSushi
Copy link
Member

@aturon To bikeshed a bit more, clone on Arc/Rc also means "created a new alias," so there is some precedent!

@aturon
Copy link
Member

aturon commented Jan 27, 2016

@BurntSushi

Yeah, I thought about that, though I'd argue it's mildly more obvious what's going on in those cases.

@alexcrichton

D'oh! I'm surprised that got through, though I imagine I signed off on it at some point :-)

Given the try_clone precedent, we should probably stick with the name here. Not worth the effort of renaming the stable method, even if it's not great.

@sfackler
Copy link
Member Author

Agreed that try_clone is not a great name; I filed #31239 to track renaming in the future.

@alexcrichton
Copy link
Member

The libs team discussed this during triage today and the conclusion was to merge (mirrors existing APIs, seems like good functionality to have).

@sfackler could you open a tracking issue and update the pointer here? Other than that r=me

@alexcrichton alexcrichton removed the T-libs-api Relevant to the library API team, which will review and decide on the PR/issue. label Feb 4, 2016
@sfackler
Copy link
Member Author

sfackler commented Feb 4, 2016

@bors r=alexcrichton

@bors
Copy link
Contributor

bors commented Feb 4, 2016

📌 Commit a414b61 has been approved by alexcrichton

@bors
Copy link
Contributor

bors commented Feb 4, 2016

⌛ Testing commit a414b61 with merge 7b9d6d3...

bors added a commit that referenced this pull request Feb 4, 2016
I have it set as stable right now under the rationale that it's extending an existing, stable API to another type in the "obvious" way.

r? @alexcrichton

cc @reem
@bors bors merged commit a414b61 into rust-lang:master Feb 4, 2016
@sfackler sfackler deleted the file-try-clone branch November 26, 2016 04:42
@@ -338,6 +338,12 @@ impl File {
Ok(newpos as u64)
}

pub fn duplicate(&self) -> io::Result<File> {
Ok(File {
handle: try!(self.handle.duplicate(0, true, c::DUPLICATE_SAME_ACCESS)),
Copy link
Contributor

@oconnor663 oconnor663 Oct 11, 2019

Choose a reason for hiding this comment

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

Asking 3.5 years after the fact: Does anyone know why we set inheritable = true here? I've run into a bug (because duct was abusing File::try_clone somewhat) where pipes get inherited by processes that aren't supposed to inherit them, when spawning happens on multiple threads at once. Normally the CREATE_PROCESS_LOCK prevents this, but this line here creates inheritable handles outside of that lock. duct is going to work around this by making all the appropriate system calls explicitly. But this inheritable flag might be cause some programs in the wild to keep files open longer than they should (even without a multi-threaded race condition like mine), and on Windows in particular that could cause bugs by making them un-deletable.

Centril pushed a commit to Centril/rust that referenced this pull request Oct 16, 2019
File handles shouldn't be inheritable in general.
`std::process::Command` takes care of making them inheritable when child
processes are spawned, and the `CREATE_PROCESS_LOCK` protects against
races in that section on Windows. But `File::try_clone` has been
creating inheritable file descriptors outside of that lock, which could
be leaking into other child processes unintentionally.

See also rust-lang#31069 (comment).
Centril added a commit to Centril/rust that referenced this pull request Oct 16, 2019
…ichton

make File::try_clone produce non-inheritable handles on Windows

~**NOT READY FOR REVIEW.** This PR is currently mainly to trigger CI so that I can see what happens. (Is there a better way to trigger CI?) I don't know whether this change makes sense yet.~ (Edit: @Mark-Simulacrum clarified that CI doesn't currently run on Windows.)

---

File handles shouldn't be inheritable in general.
`std::process::Command` takes care of making them inheritable when child
processes are spawned, and the `CREATE_PROCESS_LOCK` protects against
races in that section on Windows. But `File::try_clone` has been
creating inheritable file descriptors outside of that lock, which could
be leaking into other child processes unintentionally.

See also rust-lang#31069 (comment).
Centril added a commit to Centril/rust that referenced this pull request Oct 17, 2019
…ichton

make File::try_clone produce non-inheritable handles on Windows

~**NOT READY FOR REVIEW.** This PR is currently mainly to trigger CI so that I can see what happens. (Is there a better way to trigger CI?) I don't know whether this change makes sense yet.~ (Edit: @Mark-Simulacrum clarified that CI doesn't currently run on Windows.)

---

File handles shouldn't be inheritable in general.
`std::process::Command` takes care of making them inheritable when child
processes are spawned, and the `CREATE_PROCESS_LOCK` protects against
races in that section on Windows. But `File::try_clone` has been
creating inheritable file descriptors outside of that lock, which could
be leaking into other child processes unintentionally.

See also rust-lang#31069 (comment).
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.

10 participants