in src/concurrent.rs [91:128]
fn drop(&mut self) {
debug!("Closing ShareableFile with fd {}", self.fd);
// We are about to touch file handles used by other threads. If those threads are blocked
// on a select call, the call may return an error on some systems due to the closed file
// descriptors. If those threads are about to issue a read after a select, the read will
// fail with a bad file descriptor. Prepare them about these potential failure conditions
// before we actually touch anything.
self.closed.store(true, Ordering::SeqCst);
for watcher in &self.watchers {
if let Err(e) = unistd::write(*watcher, &[0]) {
// This write to a pipe we control really should not have failed. If it did there
// is not much we can do other than log an error. We may get stuck threads on exit
// though...
//
// TODO(jmmv): We expect the write to fail if we had any ShareableFileReader that
// has already been dropped, as that drop would have closed the read end of the
// pipe. Note that this means that, if we create/drop ShareableFileReaders non-stop
// for this ShareableFile, we'll exhaust the open file descriptors because we are
// leaking the write ends of these pipes. This should be fixed, but it's not a big
// deal because we don't do this in sandboxfs.
if e.as_errno().expect("Must have been a system error") != Errno::EPIPE {
warn!("Failed to tell ShareableFileReader with handle {} of close: {}",
*watcher, e)
}
}
if let Err(e) = unistd::close(*watcher) {
// Closing should really not have failed, but if it did, it does not hurt and there
// is nothing we can do anyway.
warn!("Failed to close pipe write end with handle {}: {}", *watcher, e)
}
}
if let Err(e) = unistd::close(self.fd) {
warn!("Failed to close fd {}: {}", self.fd, e);
}
}