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

Don't unconditionally set CLOEXEC twice on every fd we open on Linux #50638

Merged
merged 1 commit into from
May 17, 2018

Conversation

tbu-
Copy link
Contributor

@tbu- tbu- commented May 10, 2018

Previously, every open64 was accompanied by a ioctl(…, FIOCLEX),
because some old Linux version would ignore the O_CLOEXEC flag we pass
to the open64 function.

Now, we check whether the CLOEXEC flag is set on the first file we
open – if it is, we won't do extra syscalls for every opened file. If it
is not set, we fall back to the old behavior of unconditionally calling
ioctl(…, FIOCLEX) on newly opened files.

On old Linuxes, this amounts to one extra syscall per process, namely
the fcntl(…, F_GETFD) call to check the CLOEXEC flag.

On new Linuxes, this reduces the number of syscalls per opened file by
one, except for the first file, where it does the same number of
syscalls as before (fcntl(…, F_GETFD) to check the flag instead of
ioctl(…, FIOCLEX) to set it).

@rust-highfive
Copy link
Collaborator

r? @dtolnay

(rust_highfive has picked a reviewer for you, use r? to override)

@rust-highfive rust-highfive added the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. label May 10, 2018
@rust-highfive
Copy link
Collaborator

The job x86_64-gnu-llvm-3.9 of your PR failed on Travis (raw log). Through arcane magic we have determined that the following fragments from the build log may contain information about the problem.

Click to expand the log.
[00:03:42]    Compiling panic_unwind v0.0.0 (file:///checkout/src/libpanic_unwind)
[00:03:47] error[E0308]: mismatched types
[00:03:47]    --> libstd/sys/unix/fs.rs:473:28
[00:03:47]     |
[00:03:47] 473 |               if need_to_set {
[00:03:47]     |  ____________________________^
[00:03:47] 474 | |                 fd.set_cloexec()?;
[00:03:47] 475 | |             }
[00:03:47]     | |_____________^ expected enum `core::result::Result`, found ()
[00:03:47]     |
[00:03:47]     = note: expected type `core::result::Result<(), io::error::Error>`
[00:03:47]                found type `()`
[00:03:48] error: aborting due to previous error
[00:03:48] 
[00:03:48] For more information about this error, try `rustc --explain E0308`.
[00:03:48] error: Could not compile `std`.
[00:03:48] error: Could not compile `std`.
[00:03:48] 
[00:03:48] Caused by:
[00:03:48]   process didn't exit successfully: `/checkout/obj/build/bootstrap/debug/rustc --crate-name std libstd/lib.rs --color always --error-format json --crate-type dylib --crate-type rlib --emit=dep-info,link -C prefer-dynamic -C opt-level=3 --cfg feature="alloc_jemalloc" --cfg feature="backtrace" --cfg feature="jemalloc" --cfg feature="panic-unwind" --cfg feature="panic_unwind" -C metadata=761fbae3650d6e55 -C extra-filename=-761fbae3650d6e55 --out-dir /checkout/obj/build/x86_64-unknown-linux-gnu/stage0-std/x86_64-unknown-linux-gnu/release/deps --target x86_64-unknown-linux-gnu -L dependency=/checkout/obj/build/x86_64-unknown-linux-gnu/stage0-std/x86_64-unknown-linux-gnu/release/deps -L dependency=/checkout/obj/build/x86_64-unknown-linux-gnu/stage0-std/release/deps --extern rustc_asan=/checkout/obj/build/x86_64-unknown-linux-gnu/stage0-std/x86_64-unknown-linux-gnu/release/deps/librustc_asan-1d7f9c9946b2c2bc.rlib --extern std_unicode=/checkout/obj/build/x86_64-unknown-linux-gnu/stage0-std/x86_64-unknown-linux-gnu/release/deps/libstd_unicode-48544fd5cf3dcf07.rlib --extern rustc_tsan=/checkout/obj/build/x86_64-unknown-linux-gnu/stage0-std/x86_64-unknown-linux-gnu/release/deps/librustc_tsan-aa82747b5f7140a1.rlib --extern alloc_jemalloc=/checkout/obj/build/x86_64-unknown-linux-gnu/stage0-std/x86_64-unknown-linux-gnu/release/deps/liballoc_jemalloc-5657f35033ebd4c8.rlib --extern core=/checkout/obj/build/x86_64-unknown-linux-gnu/stage0-std/x86_64-unknown-linux-gnu/release/deps/libcore-fb1e36473ec4786e.rlib --extern panic_unwind=/checkout/obj/build/x86_64-unknown-linux-gnu/stage0-std/x86_64-unknown-linux-gnu/release/deps/libpanic_unwind-3742d05cb3a356c6.rlib --extern unwind=/checkout/obj/build/x86_64-unknown-linux-gnu/stage0-std/x86_64-unknown-linux-gnu/release/deps/libunwind-aa228a67f9f2430d.rlib --extern libc=/checkout/obj/build/x86_64-unknown-linux-gnu/stage0-std/x86_64-unknown-linux-gnu/release/deps/liblibc-f9142bcb925af734.rlib --extern rustc_msan=/checkout/obj/build/x86_64-unknown-linux-gnu/stage0-std/x86_64-unknown-linux-gnu/release/deps/librustc_msan-e6ced10bca67cf14.rlib --extern alloc=/checkout/obj/build/x86_64-unknown-linux-gnu/stage0-std/x86_64-unknown-linux-gnu/release/deps/liballoc-1ba2d219736013ac.rlib --extern compiler_builtins=/checkout/obj/build/x86_64-unknown-linux-gnu/stage0-std/x86_64-unknown-linux-gnu/release/deps/libcompiler_builtins-96668d527ecf7799.rlib --extern panic_abort=/checkout/obj/build/x86_64-unknown-linux-gnu/stage0-std/x86_64-unknown-linux-gnu/release/deps/libpanic_abort-600098a993bc3037.rlib --extern alloc_system=/checkout/obj/build/x86_64-unknown-linux-gnu/stage0-std/x86_64-unknown-linux-gnu/release/deps/liballoc_system-8c40d3ae621dd1f7.rlib --extern rustc_lsan=/checkout/obj/build/x86_64-unknown-linux-gnu/stage0-std/x86_64-unknown-linux-gnu/release/deps/librustc_lsan-1e788572dd7deb7a.rlib -L native=/checkout/obj/build/x86_64-unknown-linux-gnu/native/libbacktrace/.libs -l static=backtrace -l dl -l rt -l pthread -L native=/checkout/obj/build/x86_64-unknown-linux-gnu/stage0-std/x86_64-unknown-linux-gnu/release/build/compiler_builtins-54b62960ded3ce36/out -L native=/checkout/obj/build/x86_64-unknown-linux-gnu/native/jemalloc/lib` (exit code: 101)
[00:03:48] command did not execute successfully: "/checkout/obj/build/x86_64-unknown-linux-gnu/stage0/bin/cargo" "build" "--target" "x86_64-unknown-linux-gnu" "--release" "--locked" "--color" "always" "--features" "panic-unwind jemalloc backtrace" "--manifest-path" "/checkout/src/libstd/Cargo.toml" "--message-format" "json"
[00:03:48] expected success, got: exit code: 101
[00:03:48] thread 'main' panicked at 'cargo must succeed', bootstrap/compile.rs:1091:9
[00:03:48] travis_fold:end:stage0-std

[00:03:48] travis_time:end:stage0-std:start=1525996514396008291,finish=1525996558888250756,duration=44492242465


[00:03:48] failed to run: /checkout/obj/build/bootstrap/debug/bootstrap test src/tools/tidy
[00:03:48] Build completed unsuccessfully in 0:00:46
[00:03:48] make: *** [tidy] Error 1
[00:03:48] Makefile:79: recipe for target 'tidy' failed

The command "stamp sh -x -c "$RUN_SCRIPT"" exited with 2.
travis_time:start:2df32971
$ date && (curl -fs --head https://google.com | grep ^Date: | sed 's/Date: //g' || true)

I'm a bot! I can only do what humans tell me to, so if this was not helpful or you have suggestions for improvements, please ping or otherwise contact @TimNN. (Feature Requests)

@tbu-
Copy link
Contributor Author

tbu- commented May 11, 2018

Source:

use std::fs::File;

fn main() {
    let _x = File::create("x").unwrap();
    let _y = File::create("y").unwrap();
}

Before:

openat(AT_FDCWD, "x", O_WRONLY|O_CREAT|O_TRUNC|O_CLOEXEC, 0666) = 3
ioctl(3, FIOCLEX)                       = 0
openat(AT_FDCWD, "y", O_WRONLY|O_CREAT|O_TRUNC|O_CLOEXEC, 0666) = 4
ioctl(4, FIOCLEX)                       = 0
close(4)                                = 0
close(3)                                = 0

After:

openat(AT_FDCWD, "x", O_WRONLY|O_CREAT|O_TRUNC|O_CLOEXEC, 0666) = 3
fcntl(3, F_GETFD)                       = 0x1 (flags FD_CLOEXEC)
openat(AT_FDCWD, "y", O_WRONLY|O_CREAT|O_TRUNC|O_CLOEXEC, 0666) = 4
close(4)                                = 0
close(3)                                = 0

@tbu- tbu- changed the title [WIP] Don't unconditionally set CLOEXEC twice on every fd we open on Linux Don't unconditionally set CLOEXEC twice on every fd we open on Linux May 11, 2018
@nagisa
Copy link
Member

nagisa commented May 12, 2018

@bors r+

@bors
Copy link
Contributor

bors commented May 12, 2018

📌 Commit 0a6b1a8 has been approved by nagisa

@bors bors added S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels May 12, 2018
@kennytm
Copy link
Member

kennytm commented May 13, 2018

@bors r-

Failed in rollup #50704 (comment).

@bors bors added S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. and removed S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. labels May 13, 2018
@@ -18,6 +18,7 @@ use mem;
use path::{Path, PathBuf};
use ptr;
use sync::Arc;
use sync::atomic::{AtomicUsize, Ordering};
Copy link
Member

Choose a reason for hiding this comment

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

[00:24:14] error: unused imports: `AtomicUsize`, `Ordering`
[00:24:14]   --> libstd/sys/unix/fs.rs:21:20
[00:24:14]    |
[00:24:14] 21 | use sync::atomic::{AtomicUsize, Ordering};
[00:24:14]    |                    ^^^^^^^^^^^  ^^^^^^^^
[00:24:14]    = note: `-D unused-imports` implied by `-D warnings`
[00:24:14] 
[00:24:17] error: aborting due to previous error

@tbu-
Copy link
Contributor Author

tbu- commented May 13, 2018

@kennytm Sorry for the compilation failure. Second one in a row due to only checking the Linux build. :(

I moved the imports into the function.

@dtolnay
Copy link
Member

dtolnay commented May 13, 2018

@bors r+

@bors
Copy link
Contributor

bors commented May 13, 2018

📌 Commit f3956e9 has been approved by dtolnay

@bors bors added S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. and removed S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. labels May 13, 2018
@bors
Copy link
Contributor

bors commented May 14, 2018

⌛ Testing commit f3956e9ff16dc78031dc93c0cf11c275e6cbd1e1 with merge bbd6f38235858820ce7b1fdfe78acf7d064560d0...

@bors
Copy link
Contributor

bors commented May 14, 2018

💔 Test failed - status-travis

@bors bors added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. and removed S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. labels May 14, 2018
@rust-highfive
Copy link
Collaborator

The job dist-i686-freebsd of your PR failed on Travis (raw log). Through arcane magic we have determined that the following fragments from the build log may contain information about the problem.

Click to expand the log.
[00:26:00] [RUSTC-TIMING] panic_unwind test:false 0.276
[00:26:05] error: unused variable: `fd`
[00:26:05]    --> libstd/sys/unix/fs.rs:481:27
[00:26:05]     |
[00:26:05] 481 |         fn ensure_cloexec(fd: &FileDesc) -> io::Result<()> {
[00:26:05]     |                           ^^ help: consider using `_fd` instead
[00:26:05]     = note: `-D unused-variables` implied by `-D warnings`
[00:26:05] 
[00:26:05] error: aborting due to previous error
[00:26:05] 
[00:26:05] 
[00:26:05] [RUSTC-TIMING] std test:false 4.449
[00:26:05] error: Could not compile `std`.
[00:26:05] 
[00:26:05] Caused by:
[00:26:05]   process didn't exit successfully: `/checkout/obj/build/bootstrap/debug/rustc --crate-name std libstd/lib.rs --color always --error-format json --crate-type dylib --crate-type rlib --emit=dep-info,link -C prefer-dynamic -C opt-level=3 --cfg feature="alloc_jemalloc" --cfg feature="backtrace" --cfg feature="jemalloc" --cfg feature="panic-unwind" --cfg feature="panic_unwind" -C metadata=8ebd5be427249cd3 -C extra-filename=-8ebd5be427249cd3 --out-dir /checkout/obj/build/x86_64-unknown-linux-gnu/stage1-std/i686-unknown-freebsd/release/deps --target i686-unknown-freebsd -L dependency=/checkout/obj/build/x86_64-unknown-linux-gnu/stage1-std/i686-unknown-freebsd/release/deps -L dependency=/checkout/obj/build/x86_64-unknown-linux-gnu/stage1-std/release/deps --extern panic_abort=/checkout/obj/build/x86_64-unknown-linux-gnu/stage1-std/i686-unknown-freebsd/release/deps/libpanic_abort-f88a00d162e3144e.rlib --extern unwind=/checkout/obj/build/x86_64-unknown-linux-gnu/stage1-std/i686-unknown-freebsd/release/deps/libunwind-aa83f963a589d5c7.rlib --extern libc=/checkout/obj/build/x86_64-unknown-linux-gnu/stage1-std/i686-unknown-freebsd/release/deps/liblibc-69a491b68a83ff00.rlib --extern alloc_system=/checkout/obj/build/x86_64-unknown-linux-gnu/stage1-std/i686-unknown-freebsd/release/deps/liballoc_system-b06b948ecc04fcd6.rlib --extern std_unicode=/checkout/obj/build/x86_64-unknown-linux-gnu/stage1-std/i686-unknown-freebsd/release/deps/libstd_unicode-cb230abdcba680b1.rlib --extern alloc=/checkout/obj/build/x86_64-unknown-linux-gnu/stage1-std/i686-unknown-freebsd/release/deps/liballoc-8d719fed8238a551.rlib --extern alloc_jemalloc=/checkout/obj/build/x86_64-unknown-linux-gnu/stage1-std/i686-unknown-freebsd/release/deps/liballoc_jemalloc-4cb9ec8db81fe30b.rlib --extern panic_unwind=/checkout/obj/build/x86_64-unknown-linux-gnu/stage1-std/i686-unknown-freebsd/release/deps/libpanic_unwind-f134dd64f219bf1b.rlib --extern core=/checkout/obj/build/x86_64-unknown-linux-gnu/stage1-std/i686-unknown-freebsd/release/deps/libcore-6eb1a77d793ab07f.rlib --extern compiler_builtins=/checkout/obj/build/x86_64-unknown-linux-gnu/stage1-std/i686-unknown-freebsd/release/deps/libcompiler_builtins-27a9e89a44272f11.rlib -L native=/checkout/obj/build/i686-unknown-freebsd/native/libbacktrace/.libs -l static=backtrace -l execinfo -l pthread -L native=/checkout/obj/build/x86_64-unknown-linux-gnu/stage1-std/i686-unknown-freebsd/release/build/compiler_builtins-5785bd67f4678604/out -L native=/checkout/obj/build/i686-unknown-freebsd/native/jemalloc/lib` (exit code: 101)
[00:26:05] command did not execute successfully: "/checkout/obj/build/x86_64-unknown-linux-gnu/stage0/bin/cargo" "build" "--target" "i686-unknown-freebsd" "-j" "4" "--release" "--locked" "--color" "always" "--features" "panic-unwind jemalloc backtrace" "--manifest-path" "/checkout/src/libstd/Cargo.toml" "--message-format" "json"
[00:26:05] expected success, got: exit code: 101
[00:26:05] thread 'main' panicked at 'cargo must succeed', bootstrap/compile.rs:1091:9
[00:26:05] travis_fold:end:stage1-std

[00:26:05] travis_time:end:stage1-std:start=1526258460915195427,finish=1526258522546259710,duration=61631064283

I'm a bot! I can only do what humans tell me to, so if this was not helpful or you have suggestions for improvements, please ping or otherwise contact @TimNN. (Feature Requests)

@kennytm kennytm added S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels May 14, 2018
@rust-lang rust-lang deleted a comment from rust-highfive May 14, 2018
}

#[cfg(not(target_os = "linux"))]
fn ensure_cloexec(fd: &FileDesc) -> io::Result<()> {
Copy link
Member

Choose a reason for hiding this comment

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

Replace the fd by _ to fix the warning 🤷

Previously, every `open64` was accompanied by a `ioctl(…, FIOCLEX)`,
because some old Linux version would ignore the `O_CLOEXEC` flag we pass
to the `open64` function.

Now, we check whether the `CLOEXEC` flag is set on the first file we
open – if it is, we won't do extra syscalls for every opened file. If it
is not set, we fall back to the old behavior of unconditionally calling
`ioctl(…, FIOCLEX)` on newly opened files.

On old Linuxes, this amounts to one extra syscall per process, namely
the `fcntl(…, F_GETFD)` call to check the `CLOEXEC` flag.

On new Linuxes, this reduces the number of syscalls per opened file by
one, except for the first file, where it does the same number of
syscalls as before (`fcntl(…, F_GETFD)` to check the flag instead of
`ioctl(…, FIOCLEX)` to set it).
@tbu-
Copy link
Contributor Author

tbu- commented May 14, 2018

:(

Fixed.

@nagisa
Copy link
Member

nagisa commented May 14, 2018

@bors r+

@bors
Copy link
Contributor

bors commented May 14, 2018

📌 Commit 6d1da82 has been approved by nagisa

@bors bors added S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. and removed S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. labels May 14, 2018
kennytm added a commit to kennytm/rust that referenced this pull request May 14, 2018
Don't unconditionally set CLOEXEC twice on every fd we open on Linux

Previously, every `open64` was accompanied by a `ioctl(…, FIOCLEX)`,
because some old Linux version would ignore the `O_CLOEXEC` flag we pass
to the `open64` function.

Now, we check whether the `CLOEXEC` flag is set on the first file we
open – if it is, we won't do extra syscalls for every opened file. If it
is not set, we fall back to the old behavior of unconditionally calling
`ioctl(…, FIOCLEX)` on newly opened files.

On old Linuxes, this amounts to one extra syscall per process, namely
the `fcntl(…, F_GETFD)` call to check the `CLOEXEC` flag.

On new Linuxes, this reduces the number of syscalls per opened file by
one, except for the first file, where it does the same number of
syscalls as before (`fcntl(…, F_GETFD)` to check the flag instead of
`ioctl(…, FIOCLEX)` to set it).
kennytm added a commit to kennytm/rust that referenced this pull request May 15, 2018
Don't unconditionally set CLOEXEC twice on every fd we open on Linux

Previously, every `open64` was accompanied by a `ioctl(…, FIOCLEX)`,
because some old Linux version would ignore the `O_CLOEXEC` flag we pass
to the `open64` function.

Now, we check whether the `CLOEXEC` flag is set on the first file we
open – if it is, we won't do extra syscalls for every opened file. If it
is not set, we fall back to the old behavior of unconditionally calling
`ioctl(…, FIOCLEX)` on newly opened files.

On old Linuxes, this amounts to one extra syscall per process, namely
the `fcntl(…, F_GETFD)` call to check the `CLOEXEC` flag.

On new Linuxes, this reduces the number of syscalls per opened file by
one, except for the first file, where it does the same number of
syscalls as before (`fcntl(…, F_GETFD)` to check the flag instead of
`ioctl(…, FIOCLEX)` to set it).
kennytm added a commit to kennytm/rust that referenced this pull request May 16, 2018
Don't unconditionally set CLOEXEC twice on every fd we open on Linux

Previously, every `open64` was accompanied by a `ioctl(…, FIOCLEX)`,
because some old Linux version would ignore the `O_CLOEXEC` flag we pass
to the `open64` function.

Now, we check whether the `CLOEXEC` flag is set on the first file we
open – if it is, we won't do extra syscalls for every opened file. If it
is not set, we fall back to the old behavior of unconditionally calling
`ioctl(…, FIOCLEX)` on newly opened files.

On old Linuxes, this amounts to one extra syscall per process, namely
the `fcntl(…, F_GETFD)` call to check the `CLOEXEC` flag.

On new Linuxes, this reduces the number of syscalls per opened file by
one, except for the first file, where it does the same number of
syscalls as before (`fcntl(…, F_GETFD)` to check the flag instead of
`ioctl(…, FIOCLEX)` to set it).
bors added a commit that referenced this pull request May 17, 2018
Rollup of 17 pull requests

Successful merges:

 - #50170 (Implement From for more types on Cow)
 - #50638 (Don't unconditionally set CLOEXEC twice on every fd we open on Linux)
 - #50656 (Fix `fn main() -> impl Trait` for non-`Termination` trait)
 - #50669 (rustdoc: deprecate `#![doc(passes, plugins, no_default_passes)]`)
 - #50726 (read2: Use inner function instead of closure)
 - #50728 (Fix rustdoc panic with `impl Trait` in type parameters)
 - #50736 (env: remove unwrap in examples in favor of try op)
 - #50740 (Remove LazyBTreeMap.)
 - #50752 (Add missing error codes in libsyntax-ext asm)
 - #50779 (Make mutable_noalias and arg_align_attributes be tracked)
 - #50787 (Fix run-make wasm tests)
 - #50788 (Fix an ICE when casting a nonexistent const)
 - #50789 (Ensure libraries built in stage0 have unique metadata)
 - #50793 (tidy: Add a check for empty UI test files)
 - #50797 (fix a typo in signed-integer::from_str_radix())
 - #50808 (Stabilize num::NonZeroU*)
 - #50809 (GitHub: Stop treating Cargo.lock as a generated file.)

Failed merges:
@bors bors merged commit 6d1da82 into rust-lang:master May 17, 2018
@tamird
Copy link
Contributor

tamird commented May 24, 2018

Just wondering: how come this isn't implemented using sync::Once?

@tbu-
Copy link
Contributor Author

tbu- commented May 24, 2018

From the documentation of Once::call_once:

This method will block the calling thread if another initialization routine is currently running.

The implementation in this PR never blocks, that means that opening of files never blocks on this cached value.

As an effect of this, the initialization routine might be called multiple times, but this is no problem.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants