ripgrep: ripgrep doesn't stop when its pipe is closed

For example, the following two rg commands take the same amount of time, but the second one should be much shorter:

[andrew@Cheetah subtitles] ls -lh OpenSubtitles2016.raw.en 
-rw-r--r-- 1 andrew users 9.3G Sep 10 11:51 OpenSubtitles2016.raw.en
[andrew@Cheetah subtitles] time rg 'Sherlock Holmes' OpenSubtitles2016.raw.en | wc -l
5107

real    0m1.602s
user    0m1.250s
sys     0m0.350s
[andrew@Cheetah subtitles] time rg 'Sherlock Holmes' OpenSubtitles2016.raw.en | head -n1
You read Sherlock Holmes to deduce that?

real    0m1.626s
user    0m1.247s
sys     0m0.377s

About this issue

  • Original URL
  • State: closed
  • Created 8 years ago
  • Reactions: 5
  • Comments: 33 (15 by maintainers)

Commits related to this issue

Most upvoted comments

@junegunn Sure, I can do that. Hopefully soon.

grep is faster than rg! This issue is the proof! All hail grep! grep, grep, grep!

Confirmed fixed in 0.7.1 on macOS. Thanks.

@BurntSushi Hi, any plans for a patch release including the fix? Many users, myself included, would want to install ripgrep using Homebrew/Linuxbrew, and use it with a secondary filter like fzf.

@BurntSushi @junegunn

This means that pipe errors are only detected once an actual write occurs. At that point, the pipe error is reported “in band” instead of as a signal.

It is trivial to stop ignoring SIGPIPE. This would make ripgrep behave like a “normal” C UNIX application. SIGPIPE gets sent to the process, and since there is no signal handler for it, the process (by default) will terminate immediately. This likely achieves the behavior you want.

Having looked into this a bit more, I’m not convinced that a change to SIGPIPE handling would have any impact on the behavior I’m seeing. IIUC, SIGPIPE isn’t even generated until the writer process attempts to write to the closed pipe, which is too late. And I’m not even sure there’s a way for a writer process to check for a closed pipe without attempting to write. I’ve tried using select(), fstat(), write() of 0 bytes, etc., and the only thing that gave any indication that stdout had closed was an attempt to write actual data to it. I would have expected there would be some way - even if it involved a relatively costly system call - for a writer process to check the status of a pipe without writing unwanted data if it happens to be open.

I’m not sure exactly how the fzf Vim plugin creates the rg|fzf pipeline, but I doubt it’s possible for the plugin to force early termination, since it would have know way of knowing when the user’s selection has been made. The fzf executable knows when the selection has been made, but may not have a clean way to kill the process at the write end of its pipeline. Ah well, perhaps the solution is to ensure that everything I want to search with rg|fzf has a good .ignore file…

@oconnor663 Graciously submitted a PR implementing the libc::signal idea, which means this is fixed for now on Unix. I’m going to leave this open to track a proper fix.

Could we just libc::signal(libc::SIGPIPE, libc::SIG_DFL) somewhere early in main(), if we wanted an easy workaround for the short term? Not sure what the Windows equivalent is though. Maybe the more portable thing would be to std::process::exit on write errors? Gross in library code, but anyway just until the error plumbing is there.