Skip to content

Commit 107896c

Browse files
committed
Auto merge of #83121 - the8472:env-rwlock-2, r=joshtriplett
use RWlock when accessing os::env (take 2) This reverts commit acdca31 (#82877) i.e. redoes #81850 since the invalid unlock attempts in the child process have been fixed in #82949 r? `@joshtriplett`
2 parents 2ccf063 + e22143c commit 107896c

File tree

3 files changed

+72
-12
lines changed

3 files changed

+72
-12
lines changed

library/std/src/sys/unix/os.rs

+10-9
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ use crate::str;
2222
use crate::sys::cvt;
2323
use crate::sys::fd;
2424
use crate::sys_common::mutex::{StaticMutex, StaticMutexGuard};
25+
use crate::sys_common::rwlock::{RWLockReadGuard, StaticRWLock};
2526
use crate::vec;
2627

2728
use libc::{c_char, c_int, c_void};
@@ -490,20 +491,20 @@ pub unsafe fn environ() -> *mut *const *const c_char {
490491
extern "C" {
491492
static mut environ: *const *const c_char;
492493
}
493-
&mut environ
494+
ptr::addr_of_mut!(environ)
494495
}
495496

496-
pub unsafe fn env_lock() -> StaticMutexGuard {
497-
// It is UB to attempt to acquire this mutex reentrantly!
498-
static ENV_LOCK: StaticMutex = StaticMutex::new();
499-
ENV_LOCK.lock()
497+
static ENV_LOCK: StaticRWLock = StaticRWLock::new();
498+
499+
pub fn env_read_lock() -> RWLockReadGuard {
500+
ENV_LOCK.read_with_guard()
500501
}
501502

502503
/// Returns a vector of (variable, value) byte-vector pairs for all the
503504
/// environment variables of the current process.
504505
pub fn env() -> Env {
505506
unsafe {
506-
let _guard = env_lock();
507+
let _guard = env_read_lock();
507508
let mut environ = *environ();
508509
let mut result = Vec::new();
509510
if !environ.is_null() {
@@ -540,7 +541,7 @@ pub fn getenv(k: &OsStr) -> io::Result<Option<OsString>> {
540541
// always None as well
541542
let k = CString::new(k.as_bytes())?;
542543
unsafe {
543-
let _guard = env_lock();
544+
let _guard = env_read_lock();
544545
let s = libc::getenv(k.as_ptr()) as *const libc::c_char;
545546
let ret = if s.is_null() {
546547
None
@@ -556,7 +557,7 @@ pub fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> {
556557
let v = CString::new(v.as_bytes())?;
557558

558559
unsafe {
559-
let _guard = env_lock();
560+
let _guard = ENV_LOCK.write_with_guard();
560561
cvt(libc::setenv(k.as_ptr(), v.as_ptr(), 1)).map(drop)
561562
}
562563
}
@@ -565,7 +566,7 @@ pub fn unsetenv(n: &OsStr) -> io::Result<()> {
565566
let nbuf = CString::new(n.as_bytes())?;
566567

567568
unsafe {
568-
let _guard = env_lock();
569+
let _guard = ENV_LOCK.write_with_guard();
569570
cvt(libc::unsetenv(nbuf.as_ptr())).map(drop)
570571
}
571572
}

library/std/src/sys/unix/process/process_unix.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ impl Command {
4848
// a lock any more because the parent won't do anything and the child is
4949
// in its own process. Thus the parent drops the lock guard while the child
5050
// forgets it to avoid unlocking it on a new thread, which would be invalid.
51-
let (env_lock, result) = unsafe { (sys::os::env_lock(), cvt(libc::fork())?) };
51+
let (env_lock, result) = unsafe { (sys::os::env_read_lock(), cvt(libc::fork())?) };
5252

5353
let pid = unsafe {
5454
match result {
@@ -127,7 +127,7 @@ impl Command {
127127
// Similar to when forking, we want to ensure that access to
128128
// the environment is synchronized, so make sure to grab the
129129
// environment lock before we try to exec.
130-
let _lock = sys::os::env_lock();
130+
let _lock = sys::os::env_read_lock();
131131

132132
let Err(e) = self.do_exec(theirs, envp.as_ref());
133133
e
@@ -407,7 +407,7 @@ impl Command {
407407
cvt_nz(libc::posix_spawnattr_setflags(attrs.0.as_mut_ptr(), flags as _))?;
408408

409409
// Make sure we synchronize access to the global `environ` resource
410-
let _env_lock = sys::os::env_lock();
410+
let _env_lock = sys::os::env_read_lock();
411411
let envp = envp.map(|c| c.as_ptr()).unwrap_or_else(|| *sys::os::environ() as *const _);
412412
cvt_nz(libc::posix_spawnp(
413413
&mut p.pid,

library/std/src/sys_common/rwlock.rs

+59
Original file line numberDiff line numberDiff line change
@@ -86,3 +86,62 @@ impl RWLock {
8686
self.0.destroy()
8787
}
8888
}
89+
90+
// the cfg annotations only exist due to dead code warnings. the code itself is portable
91+
#[cfg(unix)]
92+
pub struct StaticRWLock(RWLock);
93+
94+
#[cfg(unix)]
95+
impl StaticRWLock {
96+
pub const fn new() -> StaticRWLock {
97+
StaticRWLock(RWLock::new())
98+
}
99+
100+
/// Acquires shared access to the underlying lock, blocking the current
101+
/// thread to do so.
102+
///
103+
/// The lock is automatically unlocked when the returned guard is dropped.
104+
#[inline]
105+
pub fn read_with_guard(&'static self) -> RWLockReadGuard {
106+
// SAFETY: All methods require static references, therefore self
107+
// cannot be moved between invocations.
108+
unsafe {
109+
self.0.read();
110+
}
111+
RWLockReadGuard(&self.0)
112+
}
113+
114+
/// Acquires write access to the underlying lock, blocking the current thread
115+
/// to do so.
116+
///
117+
/// The lock is automatically unlocked when the returned guard is dropped.
118+
#[inline]
119+
pub fn write_with_guard(&'static self) -> RWLockWriteGuard {
120+
// SAFETY: All methods require static references, therefore self
121+
// cannot be moved between invocations.
122+
unsafe {
123+
self.0.write();
124+
}
125+
RWLockWriteGuard(&self.0)
126+
}
127+
}
128+
129+
#[cfg(unix)]
130+
pub struct RWLockReadGuard(&'static RWLock);
131+
132+
#[cfg(unix)]
133+
impl Drop for RWLockReadGuard {
134+
fn drop(&mut self) {
135+
unsafe { self.0.read_unlock() }
136+
}
137+
}
138+
139+
#[cfg(unix)]
140+
pub struct RWLockWriteGuard(&'static RWLock);
141+
142+
#[cfg(unix)]
143+
impl Drop for RWLockWriteGuard {
144+
fn drop(&mut self) {
145+
unsafe { self.0.write_unlock() }
146+
}
147+
}

0 commit comments

Comments
 (0)