Skip to content

Commit 2a2a608

Browse files
Desktop: Use the OS temp directory for CEF caches (#4030)
* Desktop: Use the OS temp dir * Review
1 parent 60f16d7 commit 2a2a608

8 files changed

Lines changed: 95 additions & 54 deletions

File tree

desktop/src/cef.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@ use crate::wrapper::{WgpuContext, deserialize_editor_message};
2727

2828
mod consts;
2929
mod context;
30-
mod dirs;
3130
mod input;
3231
mod internal;
3332
mod ipc;

desktop/src/cef/context/builder.rs

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,15 @@ use cef::{
44
App, BrowserSettings, CefString, Client, DictionaryValue, ImplCommandLine, ImplRequestContext, LogSeverity, RequestContextSettings, SchemeHandlerFactory, Settings, WindowInfo, api_hash,
55
browser_host_create_browser_sync, execute_process,
66
};
7-
use std::path::{Path, PathBuf};
7+
use std::path::Path;
88

99
use super::CefContext;
1010
use super::singlethreaded::SingleThreadedCefContext;
1111
use crate::cef::CefEventHandler;
1212
use crate::cef::consts::{RESOURCE_DOMAIN, RESOURCE_SCHEME};
13-
use crate::cef::dirs::{create_instance_dir, delete_instance_dirs};
1413
use crate::cef::input::InputState;
1514
use crate::cef::internal::{BrowserProcessAppImpl, BrowserProcessClientImpl, RenderProcessAppImpl, SchemeHandlerFactoryImpl};
15+
use crate::dirs::TempDir;
1616

1717
pub(crate) struct CefContextBuilder<H: CefEventHandler> {
1818
pub(crate) args: Args,
@@ -97,8 +97,7 @@ impl<H: CefEventHandler> CefContextBuilder<H> {
9797

9898
#[cfg(target_os = "macos")]
9999
pub(crate) fn initialize(self, event_handler: H, disable_gpu_acceleration: bool) -> Result<impl CefContext, InitError> {
100-
delete_instance_dirs();
101-
let instance_dir = create_instance_dir();
100+
let instance_dir = TempDir::new().expect("Failed to create temporary directory for CEF instance");
102101

103102
let exe = std::env::current_exe().expect("cannot get current exe path");
104103
let app_root = exe.parent().and_then(|p| p.parent()).expect("bad path structure").parent().expect("bad path structure");
@@ -108,7 +107,7 @@ impl<H: CefEventHandler> CefContextBuilder<H> {
108107
multi_threaded_message_loop: 0,
109108
external_message_pump: 1,
110109
no_sandbox: 1, // GPU helper crashes when running with sandbox
111-
..Self::common_settings(&instance_dir)
110+
..Self::common_settings(instance_dir.as_ref())
112111
};
113112

114113
self.initialize_inner(&event_handler, settings)?;
@@ -118,14 +117,13 @@ impl<H: CefEventHandler> CefContextBuilder<H> {
118117

119118
#[cfg(not(target_os = "macos"))]
120119
pub(crate) fn initialize(self, event_handler: H, disable_gpu_acceleration: bool) -> Result<impl CefContext, InitError> {
121-
delete_instance_dirs();
122-
let instance_dir = create_instance_dir();
120+
let instance_dir = TempDir::new().expect("Failed to create temporary directory for CEF instance");
123121

124122
let settings = Settings {
125123
multi_threaded_message_loop: 1,
126124
#[cfg(target_os = "linux")]
127125
no_sandbox: 1,
128-
..Self::common_settings(&instance_dir)
126+
..Self::common_settings(instance_dir.as_ref())
129127
};
130128

131129
self.initialize_inner(&event_handler, settings)?;
@@ -157,7 +155,7 @@ impl<H: CefEventHandler> CefContextBuilder<H> {
157155
}
158156
}
159157

160-
fn create_browser<H: CefEventHandler>(event_handler: H, instance_dir: PathBuf, disable_gpu_acceleration: bool) -> Result<SingleThreadedCefContext, InitError> {
158+
fn create_browser<H: CefEventHandler>(event_handler: H, instance_dir: TempDir, disable_gpu_acceleration: bool) -> Result<SingleThreadedCefContext, InitError> {
161159
let mut client = Client::new(BrowserProcessClientImpl::new(&event_handler));
162160

163161
#[cfg(feature = "accelerated_paint")]
@@ -211,7 +209,7 @@ fn create_browser<H: CefEventHandler>(event_handler: H, instance_dir: PathBuf, d
211209
event_handler: Box::new(event_handler),
212210
browser,
213211
input_state: InputState::default(),
214-
instance_dir,
212+
_instance_dir: instance_dir,
215213
})
216214
} else {
217215
tracing::error!("Failed to create browser");

desktop/src/cef/context/multithreaded.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,12 @@ impl CefContext for MultiThreadedCefContextProxy {
5454
impl Drop for MultiThreadedCefContextProxy {
5555
fn drop(&mut self) {
5656
// Force dropping underlying context on the UI thread
57-
run_on_ui_thread(move || drop(CONTEXT.take()));
57+
let (sync_drop_tx, sync_drop_rx) = std::sync::mpsc::channel();
58+
run_on_ui_thread(move || {
59+
drop(CONTEXT.take());
60+
let _ = sync_drop_tx.send(());
61+
});
62+
let _ = sync_drop_rx.recv();
5863
}
5964
}
6065

desktop/src/cef/context/singlethreaded.rs

Lines changed: 2 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,15 @@ use winit::event::WindowEvent;
44
use crate::cef::input::InputState;
55
use crate::cef::ipc::{MessageType, SendMessage};
66
use crate::cef::{CefEventHandler, input};
7+
use crate::dirs::TempDir;
78

89
use super::CefContext;
910

1011
pub(super) struct SingleThreadedCefContext {
1112
pub(super) event_handler: Box<dyn CefEventHandler>,
1213
pub(super) browser: Browser,
1314
pub(super) input_state: InputState,
14-
pub(super) instance_dir: std::path::PathBuf,
15+
pub(super) _instance_dir: TempDir,
1516
}
1617

1718
impl CefContext for SingleThreadedCefContext {
@@ -46,19 +47,6 @@ impl Drop for SingleThreadedCefContext {
4647
// CEF wants us to close the browser before shutting down, otherwise it may run longer that necessary.
4748
self.browser.host().unwrap().close_browser(1);
4849
cef::shutdown();
49-
50-
// Sometimes some CEF processes still linger at this point and hold file handles to the cache directory.
51-
// To mitigate this, we try to remove the directory multiple times with some delay.
52-
// TODO: find a better solution if possible.
53-
for _ in 0..30 {
54-
match std::fs::remove_dir_all(&self.instance_dir) {
55-
Ok(_) => break,
56-
Err(e) => {
57-
tracing::warn!("Failed to remove CEF cache directory, retrying...: {e}");
58-
std::thread::sleep(std::time::Duration::from_millis(100));
59-
}
60-
}
61-
}
6250
}
6351
}
6452

@@ -68,7 +56,6 @@ impl SendMessage for SingleThreadedCefContext {
6856
tracing::error!("Main frame is not available, cannot send message");
6957
return;
7058
};
71-
7259
frame.send_message(message_type, message);
7360
}
7461
}

desktop/src/cef/dirs.rs

Lines changed: 0 additions & 24 deletions
This file was deleted.

desktop/src/dirs.rs

Lines changed: 67 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,31 @@
1-
use std::fs::create_dir_all;
2-
use std::path::PathBuf;
1+
use std::fs;
2+
use std::io;
3+
use std::path::{Path, PathBuf};
34

45
use crate::consts::{APP_DIRECTORY_NAME, APP_DOCUMENTS_DIRECTORY_NAME};
56

67
pub(crate) fn ensure_dir_exists(path: &PathBuf) {
78
if !path.exists() {
8-
create_dir_all(path).unwrap_or_else(|_| panic!("Failed to create directory at {path:?}"));
9+
fs::create_dir_all(path).unwrap_or_else(|_| panic!("Failed to create directory at {path:?}"));
10+
}
11+
}
12+
13+
fn clear_dir(path: &PathBuf) {
14+
let Ok(entries) = fs::read_dir(path) else {
15+
tracing::error!("Failed to read directory at {path:?}");
16+
return;
17+
};
18+
for entry in entries.flatten() {
19+
let entry_path = entry.path();
20+
if entry_path.is_dir() {
21+
if let Err(e) = fs::remove_dir_all(&entry_path) {
22+
tracing::error!("Failed to remove directory at {:?}: {}", entry_path, e);
23+
}
24+
} else if entry_path.is_file() {
25+
if let Err(e) = fs::remove_file(&entry_path) {
26+
tracing::error!("Failed to remove file at {:?}: {}", entry_path, e);
27+
}
28+
}
929
}
1030
}
1131

@@ -15,8 +35,52 @@ pub(crate) fn app_data_dir() -> PathBuf {
1535
path
1636
}
1737

38+
fn app_tmp_dir() -> PathBuf {
39+
let path = std::env::temp_dir().join(APP_DIRECTORY_NAME);
40+
ensure_dir_exists(&path);
41+
path
42+
}
43+
44+
pub(crate) fn app_tmp_dir_cleanup() {
45+
clear_dir(&app_tmp_dir());
46+
}
47+
1848
pub(crate) fn app_autosave_documents_dir() -> PathBuf {
1949
let path = app_data_dir().join(APP_DOCUMENTS_DIRECTORY_NAME);
2050
ensure_dir_exists(&path);
2151
path
2252
}
53+
54+
/// Temporary directory that is automatically deleted when dropped.
55+
pub struct TempDir {
56+
path: PathBuf,
57+
}
58+
59+
impl TempDir {
60+
pub fn new() -> io::Result<Self> {
61+
Self::new_with_parent(app_tmp_dir())
62+
}
63+
64+
pub fn new_with_parent(parent: impl AsRef<Path>) -> io::Result<Self> {
65+
let random_suffix = format!("{:032x}", rand::random::<u128>());
66+
let name = format!("{}_{}", std::process::id(), random_suffix);
67+
let path = parent.as_ref().join(name);
68+
fs::create_dir_all(&path)?;
69+
Ok(Self { path })
70+
}
71+
}
72+
73+
impl Drop for TempDir {
74+
fn drop(&mut self) {
75+
let result = fs::remove_dir_all(&self.path);
76+
if let Err(e) = result {
77+
tracing::error!("Failed to remove temporary directory at {:?}: {}", self.path, e);
78+
}
79+
}
80+
}
81+
82+
impl AsRef<Path> for TempDir {
83+
fn as_ref(&self) -> &Path {
84+
&self.path
85+
}
86+
}

desktop/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,8 @@ pub fn start() {
6363
}
6464
};
6565

66+
dirs::app_tmp_dir_cleanup();
67+
6668
let prefs = preferences::read();
6769

6870
// Must be called before event loop initialization or native window integrations will break

desktop/src/persist.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,8 @@ impl PersistentData {
9898
}
9999

100100
pub(crate) fn load_from_disk(&mut self) {
101+
delete_old_cef_browser_directory();
102+
101103
let path = Self::state_file_path();
102104
let data = match std::fs::read_to_string(&path) {
103105
Ok(d) => d,
@@ -157,3 +159,11 @@ impl PersistentData {
157159
path
158160
}
159161
}
162+
163+
// TODO: Eventually remove this cleanup code for the old "browser" CEF directory
164+
fn delete_old_cef_browser_directory() {
165+
let old_browser_dir = crate::dirs::app_data_dir().join("browser");
166+
if old_browser_dir.is_dir() {
167+
let _ = std::fs::remove_dir_all(&old_browser_dir);
168+
}
169+
}

0 commit comments

Comments
 (0)