mirror of
https://github.com/openai/codex.git
synced 2026-03-23 16:46:32 +03:00
Compare commits
7 Commits
starr/exec
...
codex/tmp-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e182331dc7 | ||
|
|
ece9757260 | ||
|
|
c136143be9 | ||
|
|
52db1ffcb3 | ||
|
|
e312e613ba | ||
|
|
f81ccfa2ee | ||
|
|
69dfa11ba9 |
1
codex-rs/Cargo.lock
generated
1
codex-rs/Cargo.lock
generated
@@ -2505,6 +2505,7 @@ dependencies = [
|
||||
"core-foundation 0.9.4",
|
||||
"libc",
|
||||
"tracing",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
||||
@@ -626,7 +626,11 @@ pub const FEATURES: &[FeatureSpec] = &[
|
||||
FeatureSpec {
|
||||
id: Feature::PreventIdleSleep,
|
||||
key: "prevent_idle_sleep",
|
||||
stage: if cfg!(target_os = "macos") {
|
||||
stage: if cfg!(any(
|
||||
target_os = "macos",
|
||||
target_os = "linux",
|
||||
target_os = "windows"
|
||||
)) {
|
||||
Stage::Experimental {
|
||||
name: "Prevent sleep while running",
|
||||
menu_description: "Keep your computer awake while Codex is running a thread.",
|
||||
|
||||
@@ -11,3 +11,15 @@ workspace = true
|
||||
core-foundation = "0.9"
|
||||
libc = { workspace = true }
|
||||
tracing = { workspace = true }
|
||||
|
||||
[target.'cfg(target_os = "linux")'.dependencies]
|
||||
tracing = { workspace = true }
|
||||
|
||||
[target.'cfg(target_os = "windows")'.dependencies]
|
||||
tracing = { workspace = true }
|
||||
windows-sys = { version = "0.52", features = [
|
||||
"Win32_Foundation",
|
||||
"Win32_System_Power",
|
||||
"Win32_System_SystemServices",
|
||||
"Win32_System_Threading",
|
||||
] }
|
||||
|
||||
@@ -3,10 +3,14 @@
|
||||
//! On macOS this uses native IOKit power assertions instead of spawning
|
||||
//! `caffeinate`, so assertion lifecycle is tied directly to Rust object lifetime.
|
||||
|
||||
#[cfg(not(target_os = "macos"))]
|
||||
#[cfg(not(any(target_os = "linux", target_os = "macos", target_os = "windows")))]
|
||||
mod dummy;
|
||||
#[cfg(target_os = "linux")]
|
||||
mod linux_inhibitor;
|
||||
#[cfg(target_os = "macos")]
|
||||
mod macos_inhibitor;
|
||||
#[cfg(target_os = "windows")]
|
||||
mod windows_inhibitor;
|
||||
|
||||
use std::fmt::Debug;
|
||||
|
||||
@@ -27,7 +31,13 @@ impl SleepInhibitor {
|
||||
#[cfg(target_os = "macos")]
|
||||
let platform: Box<dyn PlatformSleepInhibitor> =
|
||||
Box::new(macos_inhibitor::MacOsSleepInhibitor::new());
|
||||
#[cfg(not(target_os = "macos"))]
|
||||
#[cfg(target_os = "linux")]
|
||||
let platform: Box<dyn PlatformSleepInhibitor> =
|
||||
Box::new(linux_inhibitor::LinuxSleepInhibitor::new());
|
||||
#[cfg(target_os = "windows")]
|
||||
let platform: Box<dyn PlatformSleepInhibitor> =
|
||||
Box::new(windows_inhibitor::WindowsSleepInhibitor::new());
|
||||
#[cfg(not(any(target_os = "linux", target_os = "macos", target_os = "windows")))]
|
||||
let platform: Box<dyn PlatformSleepInhibitor> = Box::new(dummy::DummySleepInhibitor::new());
|
||||
|
||||
Self { enabled, platform }
|
||||
|
||||
182
codex-rs/utils/sleep-inhibitor/src/linux_inhibitor.rs
Normal file
182
codex-rs/utils/sleep-inhibitor/src/linux_inhibitor.rs
Normal file
@@ -0,0 +1,182 @@
|
||||
use crate::PlatformSleepInhibitor;
|
||||
use std::process::Child;
|
||||
use std::process::Command;
|
||||
use tracing::warn;
|
||||
|
||||
const ASSERTION_REASON: &str = "Codex is running an active turn";
|
||||
const APP_ID: &str = "codex";
|
||||
// Keep the blocker process alive "long enough" without needing restarts.
|
||||
// This is `i32::MAX` seconds, which is accepted by common `sleep` implementations.
|
||||
const BLOCKER_SLEEP_SECONDS: &str = "2147483647";
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub(crate) struct LinuxSleepInhibitor {
|
||||
state: InhibitState,
|
||||
preferred_backend: Option<LinuxBackend>,
|
||||
missing_backend_logged: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
enum InhibitState {
|
||||
#[default]
|
||||
Inactive,
|
||||
Active {
|
||||
backend: LinuxBackend,
|
||||
child: Child,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
enum LinuxBackend {
|
||||
SystemdInhibit,
|
||||
GnomeSessionInhibit,
|
||||
}
|
||||
|
||||
impl LinuxSleepInhibitor {
|
||||
pub(crate) fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
}
|
||||
|
||||
impl PlatformSleepInhibitor for LinuxSleepInhibitor {
|
||||
fn acquire(&mut self) {
|
||||
if let InhibitState::Active { backend, child } = &mut self.state {
|
||||
match child.try_wait() {
|
||||
Ok(None) => return,
|
||||
Ok(Some(status)) => {
|
||||
warn!(
|
||||
?backend,
|
||||
?status,
|
||||
"Linux sleep inhibitor backend exited unexpectedly; attempting fallback"
|
||||
);
|
||||
}
|
||||
Err(error) => {
|
||||
warn!(
|
||||
?backend,
|
||||
reason = %error,
|
||||
"Failed to query Linux sleep inhibitor backend status"
|
||||
);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.state = InhibitState::Inactive;
|
||||
let should_log_backend_failures = !self.missing_backend_logged;
|
||||
let backends = match self.preferred_backend {
|
||||
Some(LinuxBackend::SystemdInhibit) => [
|
||||
LinuxBackend::SystemdInhibit,
|
||||
LinuxBackend::GnomeSessionInhibit,
|
||||
],
|
||||
Some(LinuxBackend::GnomeSessionInhibit) => [
|
||||
LinuxBackend::GnomeSessionInhibit,
|
||||
LinuxBackend::SystemdInhibit,
|
||||
],
|
||||
None => [
|
||||
LinuxBackend::SystemdInhibit,
|
||||
LinuxBackend::GnomeSessionInhibit,
|
||||
],
|
||||
};
|
||||
|
||||
for backend in backends {
|
||||
match spawn_backend(backend) {
|
||||
Ok(mut child) => match child.try_wait() {
|
||||
Ok(None) => {
|
||||
self.state = InhibitState::Active { backend, child };
|
||||
self.preferred_backend = Some(backend);
|
||||
self.missing_backend_logged = false;
|
||||
return;
|
||||
}
|
||||
Ok(Some(status)) => {
|
||||
if should_log_backend_failures {
|
||||
warn!(
|
||||
?backend,
|
||||
?status,
|
||||
"Linux sleep inhibitor backend exited immediately"
|
||||
);
|
||||
}
|
||||
}
|
||||
Err(error) => {
|
||||
if should_log_backend_failures {
|
||||
warn!(
|
||||
?backend,
|
||||
reason = %error,
|
||||
"Failed to query Linux sleep inhibitor backend status after spawn"
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
Err(error) => {
|
||||
if should_log_backend_failures && error.kind() != std::io::ErrorKind::NotFound {
|
||||
warn!(
|
||||
?backend,
|
||||
reason = %error,
|
||||
"Failed to start Linux sleep inhibitor backend"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if should_log_backend_failures {
|
||||
warn!("No Linux sleep inhibitor backend is available");
|
||||
self.missing_backend_logged = true;
|
||||
}
|
||||
}
|
||||
|
||||
fn release(&mut self) {
|
||||
match std::mem::take(&mut self.state) {
|
||||
InhibitState::Inactive => {}
|
||||
InhibitState::Active { backend, mut child } => {
|
||||
if let Err(error) = child.kill()
|
||||
&& !child_exited(&error)
|
||||
{
|
||||
warn!(?backend, reason = %error, "Failed to stop Linux sleep inhibitor backend");
|
||||
}
|
||||
if let Err(error) = child.wait()
|
||||
&& !child_exited(&error)
|
||||
{
|
||||
warn!(?backend, reason = %error, "Failed to reap Linux sleep inhibitor backend");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for LinuxSleepInhibitor {
|
||||
fn drop(&mut self) {
|
||||
self.release();
|
||||
}
|
||||
}
|
||||
|
||||
fn spawn_backend(backend: LinuxBackend) -> Result<Child, std::io::Error> {
|
||||
match backend {
|
||||
LinuxBackend::SystemdInhibit => Command::new("systemd-inhibit")
|
||||
.args([
|
||||
"--what=idle",
|
||||
"--mode=block",
|
||||
"--who",
|
||||
APP_ID,
|
||||
"--why",
|
||||
ASSERTION_REASON,
|
||||
"--",
|
||||
"sleep",
|
||||
BLOCKER_SLEEP_SECONDS,
|
||||
])
|
||||
.spawn(),
|
||||
LinuxBackend::GnomeSessionInhibit => Command::new("gnome-session-inhibit")
|
||||
.args([
|
||||
"--inhibit",
|
||||
"idle",
|
||||
"--reason",
|
||||
ASSERTION_REASON,
|
||||
"sleep",
|
||||
BLOCKER_SLEEP_SECONDS,
|
||||
])
|
||||
.spawn(),
|
||||
}
|
||||
}
|
||||
|
||||
fn child_exited(error: &std::io::Error) -> bool {
|
||||
matches!(error.kind(), std::io::ErrorKind::InvalidInput)
|
||||
}
|
||||
95
codex-rs/utils/sleep-inhibitor/src/windows_inhibitor.rs
Normal file
95
codex-rs/utils/sleep-inhibitor/src/windows_inhibitor.rs
Normal file
@@ -0,0 +1,95 @@
|
||||
use crate::PlatformSleepInhibitor;
|
||||
use std::ffi::OsStr;
|
||||
use std::iter::once;
|
||||
use std::os::windows::ffi::OsStrExt;
|
||||
use tracing::warn;
|
||||
use windows_sys::Win32::Foundation::CloseHandle;
|
||||
use windows_sys::Win32::System::Power::POWER_REQUEST_TYPE;
|
||||
use windows_sys::Win32::System::Power::PowerClearRequest;
|
||||
use windows_sys::Win32::System::Power::PowerCreateRequest;
|
||||
use windows_sys::Win32::System::Power::PowerRequestExecutionRequired;
|
||||
use windows_sys::Win32::System::Power::PowerSetRequest;
|
||||
use windows_sys::Win32::System::SystemServices::POWER_REQUEST_CONTEXT_VERSION;
|
||||
use windows_sys::Win32::System::Threading::POWER_REQUEST_CONTEXT_SIMPLE_STRING;
|
||||
use windows_sys::Win32::System::Threading::REASON_CONTEXT;
|
||||
use windows_sys::Win32::System::Threading::REASON_CONTEXT_0;
|
||||
|
||||
const ASSERTION_REASON: &str = "Codex is running an active turn";
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub(crate) struct WindowsSleepInhibitor {
|
||||
request: Option<PowerRequest>,
|
||||
}
|
||||
|
||||
impl WindowsSleepInhibitor {
|
||||
pub(crate) fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
}
|
||||
|
||||
impl PlatformSleepInhibitor for WindowsSleepInhibitor {
|
||||
fn acquire(&mut self) {
|
||||
if self.request.is_some() {
|
||||
return;
|
||||
}
|
||||
|
||||
match PowerRequest::new_execution_required(ASSERTION_REASON) {
|
||||
Ok(request) => {
|
||||
self.request = Some(request);
|
||||
}
|
||||
Err(error) => {
|
||||
warn!(
|
||||
reason = %error,
|
||||
"Failed to acquire Windows sleep-prevention request"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn release(&mut self) {
|
||||
self.request = None;
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct PowerRequest {
|
||||
handle: windows_sys::Win32::Foundation::HANDLE,
|
||||
request_type: POWER_REQUEST_TYPE,
|
||||
}
|
||||
|
||||
impl PowerRequest {
|
||||
fn new_execution_required(reason: &str) -> Result<Self, String> {
|
||||
let mut wide_reason: Vec<u16> = OsStr::new(reason).encode_wide().chain(once(0)).collect();
|
||||
let context = REASON_CONTEXT {
|
||||
Version: POWER_REQUEST_CONTEXT_VERSION,
|
||||
Flags: POWER_REQUEST_CONTEXT_SIMPLE_STRING,
|
||||
Reason: REASON_CONTEXT_0 {
|
||||
SimpleReasonString: wide_reason.as_mut_ptr(),
|
||||
},
|
||||
};
|
||||
let handle = unsafe { PowerCreateRequest(&context) };
|
||||
if handle == 0 {
|
||||
let error = std::io::Error::last_os_error();
|
||||
return Err(format!("PowerCreateRequest failed: {error}"));
|
||||
}
|
||||
|
||||
let request_type = PowerRequestExecutionRequired;
|
||||
if unsafe { PowerSetRequest(handle, request_type) } == 0 {
|
||||
let error = std::io::Error::last_os_error();
|
||||
let _ = unsafe { CloseHandle(handle) };
|
||||
return Err(format!("PowerSetRequest failed: {error}"));
|
||||
}
|
||||
|
||||
Ok(Self {
|
||||
handle,
|
||||
request_type,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for PowerRequest {
|
||||
fn drop(&mut self) {
|
||||
let _ = unsafe { PowerClearRequest(self.handle, self.request_type) };
|
||||
let _ = unsafe { CloseHandle(self.handle) };
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user