mirror of
https://github.com/neondatabase/neon.git
synced 2026-01-13 16:32:56 +00:00
## Problem `cargo +nightly doc` is giving a lot of warnings: broken links, naked URLs, etc. ## Summary of changes * update the `proc-macro2` dependency so that it can compile on latest Rust nightly, see https://github.com/dtolnay/proc-macro2/pull/391 and https://github.com/dtolnay/proc-macro2/issues/398 * allow the `private_intra_doc_links` lint, as linking to something that's private is always more useful than just mentioning it without a link: if the link breaks in the future, at least there is a warning due to that. Also, one might enable [`--document-private-items`](https://doc.rust-lang.org/cargo/commands/cargo-doc.html#documentation-options) in the future and make these links work in general. * fix all the remaining warnings given by `cargo +nightly doc` * make it possible to run `cargo doc` on stable Rust by updating `opentelemetry` and associated crates to version 0.19, pulling in a fix that previously broke `cargo doc` on stable: https://github.com/open-telemetry/opentelemetry-rust/pull/904 * Add `cargo doc` to CI to ensure that it won't get broken in the future. Fixes #2557 ## Future work * Potentially, it might make sense, for development purposes, to publish the generated rustdocs somewhere, like for example [how the rust compiler does it](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_driver/index.html). I will file an issue for discussion.
135 lines
4.4 KiB
Rust
135 lines
4.4 KiB
Rust
//! A module to create and read lock files.
|
|
//!
|
|
//! File locking is done using [`fcntl::flock`] exclusive locks.
|
|
//! The only consumer of this module is currently
|
|
//! [`pid_file`](crate::pid_file). See the module-level comment
|
|
//! there for potential pitfalls with lock files that are used
|
|
//! to store PIDs (pidfiles).
|
|
|
|
use std::{
|
|
fs,
|
|
io::{Read, Write},
|
|
ops::Deref,
|
|
os::unix::prelude::AsRawFd,
|
|
path::{Path, PathBuf},
|
|
};
|
|
|
|
use anyhow::Context;
|
|
use nix::{errno::Errno::EAGAIN, fcntl};
|
|
|
|
use crate::crashsafe;
|
|
|
|
/// A handle to an open and unlocked, but not-yet-written lock file.
|
|
/// Returned by [`create_exclusive`].
|
|
#[must_use]
|
|
pub struct UnwrittenLockFile {
|
|
path: PathBuf,
|
|
file: fs::File,
|
|
}
|
|
|
|
/// Returned by [`UnwrittenLockFile::write_content`].
|
|
#[must_use]
|
|
pub struct LockFileGuard(fs::File);
|
|
|
|
impl Deref for LockFileGuard {
|
|
type Target = fs::File;
|
|
|
|
fn deref(&self) -> &Self::Target {
|
|
&self.0
|
|
}
|
|
}
|
|
|
|
impl UnwrittenLockFile {
|
|
/// Replace the content of this lock file with the byte representation of `contents`.
|
|
pub fn write_content(mut self, contents: String) -> anyhow::Result<LockFileGuard> {
|
|
self.file
|
|
.set_len(0)
|
|
.context("Failed to truncate lockfile")?;
|
|
self.file
|
|
.write_all(contents.as_bytes())
|
|
.with_context(|| format!("Failed to write '{contents}' contents into lockfile"))?;
|
|
crashsafe::fsync_file_and_parent(&self.path).context("fsync lockfile")?;
|
|
Ok(LockFileGuard(self.file))
|
|
}
|
|
}
|
|
|
|
/// Creates and opens a lock file in the path, grabs an exclusive flock on it, and returns
|
|
/// a handle that allows overwriting the locked file's content.
|
|
///
|
|
/// The exclusive lock is released when dropping the returned handle.
|
|
///
|
|
/// It is not an error if the file already exists.
|
|
/// It is an error if the file is already locked.
|
|
pub fn create_exclusive(lock_file_path: &Path) -> anyhow::Result<UnwrittenLockFile> {
|
|
let lock_file = fs::OpenOptions::new()
|
|
.create(true) // O_CREAT
|
|
.write(true)
|
|
.open(lock_file_path)
|
|
.context("open lock file")?;
|
|
|
|
let res = fcntl::flock(
|
|
lock_file.as_raw_fd(),
|
|
fcntl::FlockArg::LockExclusiveNonblock,
|
|
);
|
|
match res {
|
|
Ok(()) => Ok(UnwrittenLockFile {
|
|
path: lock_file_path.to_owned(),
|
|
file: lock_file,
|
|
}),
|
|
Err(EAGAIN) => anyhow::bail!("file is already locked"),
|
|
Err(e) => Err(e).context("flock error"),
|
|
}
|
|
}
|
|
|
|
/// Returned by [`read_and_hold_lock_file`].
|
|
/// Check out the [`pid_file`](crate::pid_file) module for what the variants mean
|
|
/// and potential caveats if the lock files that are used to store PIDs.
|
|
pub enum LockFileRead {
|
|
/// No file exists at the given path.
|
|
NotExist,
|
|
/// No other process held the lock file, so we grabbed an flock
|
|
/// on it and read its contents.
|
|
/// Release the flock by dropping the [`LockFileGuard`].
|
|
NotHeldByAnyProcess(LockFileGuard, String),
|
|
/// The file exists but another process was holding an flock on it.
|
|
LockedByOtherProcess {
|
|
not_locked_file: fs::File,
|
|
content: String,
|
|
},
|
|
}
|
|
|
|
/// Open & try to lock the lock file at the given `path`, returning a [handle][`LockFileRead`] to
|
|
/// inspect its content. It is not an `Err(...)` if the file does not exist or is already locked.
|
|
/// Check the [`LockFileRead`] variants for details.
|
|
pub fn read_and_hold_lock_file(path: &Path) -> anyhow::Result<LockFileRead> {
|
|
let res = fs::OpenOptions::new().read(true).open(path);
|
|
let mut lock_file = match res {
|
|
Ok(f) => f,
|
|
Err(e) => match e.kind() {
|
|
std::io::ErrorKind::NotFound => return Ok(LockFileRead::NotExist),
|
|
_ => return Err(e).context("open lock file"),
|
|
},
|
|
};
|
|
let res = fcntl::flock(
|
|
lock_file.as_raw_fd(),
|
|
fcntl::FlockArg::LockExclusiveNonblock,
|
|
);
|
|
// We need the content regardless of lock success / failure.
|
|
// But, read it after flock so that, if it succeeded, the content is consistent.
|
|
let mut content = String::new();
|
|
lock_file
|
|
.read_to_string(&mut content)
|
|
.context("read lock file")?;
|
|
match res {
|
|
Ok(()) => Ok(LockFileRead::NotHeldByAnyProcess(
|
|
LockFileGuard(lock_file),
|
|
content,
|
|
)),
|
|
Err(EAGAIN) => Ok(LockFileRead::LockedByOtherProcess {
|
|
not_locked_file: lock_file,
|
|
content,
|
|
}),
|
|
Err(e) => Err(e).context("flock error"),
|
|
}
|
|
}
|