Start every worker thread through the executors module
Thirty-nine spawn sites in dr-ui, and one in the Android entry point, called std::thread::spawn or a Builder of their own, and most of the threads they started were <unnamed> in a panic message or a profiler. Each now calls executors::spawn with its executor and a role, so the thread is named <executor>:<role> — net:sync, decode:thumbs, io:catalog-open — and knows which executor it is on. The three that already set a name (automation, import, prefetch) keep their name as the role. Behaviour is unchanged: each job still gets a thread of its own when it starts, and spawn panics where std::thread::spawn did. The module's documentation now says how a job is assigned: by what it spends its time on, so a sweep that fetches bytes and then decodes them is Decode, and a sidecar write that touches the catalog is Network. Left as they were: the segmentation and refine workers in masks_ui.rs, which another change is reworking, and test-only threads.
This commit is contained in:
@@ -300,7 +300,9 @@ fn install_bundled_models(app: slint::android::AndroidApp) {
|
||||
// on the worker because `AAssetManager` is thread-safe by contract and
|
||||
// reading the pointer takes only the app's read lock, which `poll_events`
|
||||
// also only ever holds shared.
|
||||
std::thread::spawn(move || unpack_bundled_models(&app));
|
||||
dr_ui::executors::spawn(dr_ui::executors::Executor::Io, "models", move || {
|
||||
unpack_bundled_models(&app)
|
||||
});
|
||||
}
|
||||
|
||||
/// The copy itself, on the worker [`install_bundled_models`] starts.
|
||||
|
||||
@@ -45,6 +45,7 @@
|
||||
//!
|
||||
//! Waiting is the client's job (poll `locate`), which keeps this stateless.
|
||||
|
||||
use crate::executors::{self, Executor};
|
||||
use std::io::{BufRead, BufReader, Write};
|
||||
use std::os::unix::net::{UnixListener, UnixStream};
|
||||
use std::sync::mpsc;
|
||||
@@ -71,15 +72,13 @@ pub(crate) fn attach(window: &AppWindow) {
|
||||
};
|
||||
log::info!("automation: listening on {path:?}");
|
||||
let weak = window.as_weak();
|
||||
std::thread::Builder::new()
|
||||
.name("automation".into())
|
||||
.spawn(move || {
|
||||
for stream in listener.incoming().flatten() {
|
||||
let weak = weak.clone();
|
||||
std::thread::spawn(move || serve(stream, weak));
|
||||
}
|
||||
})
|
||||
.ok();
|
||||
executors::try_spawn(Executor::Io, "automation", move || {
|
||||
for stream in listener.incoming().flatten() {
|
||||
let weak = weak.clone();
|
||||
executors::spawn(Executor::Io, "auto-conn", move || serve(stream, weak));
|
||||
}
|
||||
})
|
||||
.ok();
|
||||
}
|
||||
|
||||
fn serve(stream: UnixStream, weak: slint::Weak<AppWindow>) {
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
//! abandoned half way leaves the signatures it did compute — they are permanent
|
||||
//! and correct — and the previous grouping intact.
|
||||
|
||||
use crate::executors::{self, Executor};
|
||||
use std::cell::{Cell, RefCell};
|
||||
use std::collections::HashMap;
|
||||
use std::path::PathBuf;
|
||||
@@ -82,7 +83,7 @@ pub enum BurstMessage {
|
||||
pub fn spawn_grouping(catalog_path: PathBuf, thumbs_dir: PathBuf) -> Receiver<BurstMessage> {
|
||||
let (tx, rx) = std::sync::mpsc::channel();
|
||||
|
||||
std::thread::spawn(move || {
|
||||
executors::spawn(Executor::Decode, "bursts", move || {
|
||||
let catalog = match Catalog::open(&catalog_path) {
|
||||
Ok(c) => c,
|
||||
Err(e) => {
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
//! share boundary the account cannot write to. The scanner excludes it by the
|
||||
//! same mechanism that excludes the trash.
|
||||
|
||||
use crate::executors::{self, Executor};
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use dr_sync::{Connection, RemoteBackend, RemoteError, RemoteId, RemotePath};
|
||||
@@ -132,7 +133,7 @@ pub fn spawn_sync(
|
||||
) -> std::sync::mpsc::Receiver<SyncMessage> {
|
||||
let (tx, rx) = std::sync::mpsc::channel();
|
||||
|
||||
std::thread::spawn(move || {
|
||||
executors::spawn(Executor::Network, "sync", move || {
|
||||
// A current-thread runtime here is what made the library scan itself
|
||||
// rather than adopt the shards the server already had; see net_runtime.
|
||||
let rt = match crate::net_runtime::build() {
|
||||
@@ -1018,7 +1019,7 @@ pub fn spawn_place_fetch(
|
||||
) -> std::sync::mpsc::Receiver<Result<Option<dr_types::Place>, String>> {
|
||||
let (tx, rx) = std::sync::mpsc::channel();
|
||||
|
||||
std::thread::spawn(move || {
|
||||
executors::spawn(Executor::Network, "places", move || {
|
||||
let rt = match crate::net_runtime::build() {
|
||||
Ok(rt) => rt,
|
||||
Err(e) => {
|
||||
|
||||
@@ -38,6 +38,7 @@
|
||||
//! is a function of the image id, so the next review finds each such file
|
||||
//! there, and consolidating again treats it as already moved.
|
||||
|
||||
use crate::executors::{self, Executor};
|
||||
use std::path::PathBuf;
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::sync::mpsc::{Receiver, Sender};
|
||||
@@ -492,7 +493,7 @@ pub fn spawn_check(
|
||||
stop: Arc<AtomicBool>,
|
||||
) -> Receiver<DupMessage> {
|
||||
let (tx, rx) = std::sync::mpsc::channel();
|
||||
std::thread::spawn(move || {
|
||||
executors::spawn(Executor::Network, "dupes", move || {
|
||||
let stopped = run_check(&conn, &catalog_path, cache_dir, groups, &stop, &tx).err();
|
||||
let _ = tx.send(DupMessage::Finished { stopped });
|
||||
});
|
||||
@@ -758,7 +759,7 @@ pub fn spawn_consolidate(
|
||||
stop: Arc<AtomicBool>,
|
||||
) -> Receiver<DupMessage> {
|
||||
let (tx, rx) = std::sync::mpsc::channel();
|
||||
std::thread::spawn(move || {
|
||||
executors::spawn(Executor::Network, "consolidate", move || {
|
||||
let stopped = run_consolidate(&conn, &catalog_path, cache_dir, plans, &stop, &tx).err();
|
||||
let _ = tx.send(DupMessage::Finished { stopped });
|
||||
});
|
||||
|
||||
@@ -35,12 +35,15 @@
|
||||
//!
|
||||
//! # Which executor a job belongs to
|
||||
//!
|
||||
//! By what it spends its time waiting on. A worker that builds a network
|
||||
//! runtime and moves bytes to or from a server is [`Executor::Network`], even
|
||||
//! when it touches the catalog on the way; one that decodes images or runs a
|
||||
//! CPU pass over their pixels or embeddings is [`Executor::Decode`]; one that
|
||||
//! drives the GPU is [`Executor::GpuSubmit`]; the rest — catalog, files, local
|
||||
//! sockets, relaying a channel to the log — is [`Executor::Io`].
|
||||
//! By what it spends its time on. One that decodes images or runs a CPU pass
|
||||
//! over their pixels or embeddings — the thumbnail and metadata sweeps, face
|
||||
//! indexing, grouping, repairs — is [`Executor::Decode`], even when it fetches
|
||||
//! the bytes first. One that drives the GPU — batch export, a merge — is
|
||||
//! [`Executor::GpuSubmit`]. One whose work is moving files or metadata to or
|
||||
//! from a server — sync, sidecars, fetches, trash, login — is
|
||||
//! [`Executor::Network`], even when it touches the catalog on the way. The
|
||||
//! rest — opening the catalog, a backup, a local import, relaying a channel to
|
||||
//! the log — is [`Executor::Io`].
|
||||
|
||||
use std::cell::Cell;
|
||||
use std::thread::JoinHandle;
|
||||
|
||||
@@ -55,6 +55,7 @@
|
||||
//! same file from the grid does — subject, both times, to what the settings
|
||||
//! allow (FR-EXP-8).
|
||||
|
||||
use crate::executors::{self, Executor};
|
||||
use std::collections::HashSet;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::rc::Rc;
|
||||
@@ -414,7 +415,7 @@ pub fn spawn_upload(
|
||||
) -> std::sync::mpsc::Receiver<UploadMessage> {
|
||||
let (tx, rx) = std::sync::mpsc::channel();
|
||||
|
||||
std::thread::spawn(move || {
|
||||
executors::spawn(Executor::Network, "upload", move || {
|
||||
let rt = match crate::net_runtime::build() {
|
||||
Ok(rt) => rt,
|
||||
Err(e) => {
|
||||
@@ -665,7 +666,9 @@ pub enum BatchMessage {
|
||||
/// Export a selection on a thread of its own.
|
||||
pub fn spawn_batch(request: BatchRequest, cancel: Cancel) -> Receiver<BatchMessage> {
|
||||
let (tx, rx) = std::sync::mpsc::channel();
|
||||
std::thread::spawn(move || run(request, &cancel, &tx));
|
||||
executors::spawn(Executor::GpuSubmit, "export", move || {
|
||||
run(request, &cancel, &tx)
|
||||
});
|
||||
rx
|
||||
}
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
//! on every one. It therefore runs debounced, when detection has been idle and
|
||||
//! the face count has moved materially (catalog.md §10.2).
|
||||
|
||||
use crate::executors::{self, Executor};
|
||||
use std::path::PathBuf;
|
||||
use std::sync::mpsc::{Receiver, Sender};
|
||||
|
||||
@@ -816,7 +817,7 @@ pub fn spawn_store_face_sweep(
|
||||
) -> Receiver<FaceSweepMessage> {
|
||||
let (tx, rx) = std::sync::mpsc::channel();
|
||||
|
||||
std::thread::spawn(move || {
|
||||
executors::spawn(Executor::Decode, "faces", move || {
|
||||
let finish_empty = |tx: &Sender<FaceSweepMessage>| {
|
||||
let _ = tx.send(FaceSweepMessage::Finished {
|
||||
images: 0,
|
||||
@@ -1348,7 +1349,7 @@ pub fn spawn_grouping_preview(
|
||||
) -> Receiver<PreviewMessage> {
|
||||
let (tx, rx) = std::sync::mpsc::channel();
|
||||
|
||||
std::thread::spawn(move || {
|
||||
executors::spawn(Executor::Decode, "grouping", move || {
|
||||
let msg = match Catalog::open(&catalog_path) {
|
||||
Ok(catalog) => match preview_grouping(&catalog, &model_id, &grouping) {
|
||||
Ok(p) => PreviewMessage::Ready(p),
|
||||
@@ -1397,7 +1398,7 @@ pub fn spawn_recluster(
|
||||
) -> Receiver<ReclusterMessage> {
|
||||
let (tx, rx) = std::sync::mpsc::channel();
|
||||
|
||||
std::thread::spawn(move || {
|
||||
executors::spawn(Executor::Decode, "recluster", move || {
|
||||
let catalog = match Catalog::open(&catalog_path) {
|
||||
Ok(c) => c,
|
||||
Err(e) => {
|
||||
|
||||
@@ -48,6 +48,7 @@
|
||||
//! record afterwards is each file's digest (`dr_catalog::dedup`), which the
|
||||
//! scan cannot know because it never reads a whole file.
|
||||
|
||||
use crate::executors::{self, Executor};
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::sync::mpsc::{Receiver, Sender};
|
||||
@@ -228,14 +229,12 @@ pub struct Outcome {
|
||||
/// does, and the caller holds it.
|
||||
pub fn spawn(request: Request, cancel: Cancel) -> Receiver<Message> {
|
||||
let (tx, rx) = std::sync::mpsc::channel();
|
||||
std::thread::Builder::new()
|
||||
.name("import".into())
|
||||
.spawn(move || {
|
||||
if let Err(e) = run(request, &cancel, &tx) {
|
||||
let _ = tx.send(Message::Failed(e));
|
||||
}
|
||||
})
|
||||
.expect("spawning the import worker");
|
||||
executors::try_spawn(Executor::Io, "import", move || {
|
||||
if let Err(e) = run(request, &cancel, &tx) {
|
||||
let _ = tx.send(Message::Failed(e));
|
||||
}
|
||||
})
|
||||
.expect("spawning the import worker");
|
||||
rx
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
//! the develop window's wiring. The model holds the state machine and is
|
||||
//! tested headless; this module only moves values across the boundary.
|
||||
|
||||
use crate::executors::{self, Executor};
|
||||
use std::cell::RefCell;
|
||||
use std::rc::Rc;
|
||||
|
||||
@@ -464,7 +465,7 @@ fn spawn_login(weak: slint::Weak<AppWindow>, ctl: Rc<LaunchController>, server:
|
||||
// the thread boundary.
|
||||
let (tx, rx) = std::sync::mpsc::channel::<LoginMessage>();
|
||||
|
||||
std::thread::spawn(move || {
|
||||
executors::spawn(Executor::Network, "login", move || {
|
||||
// A panic anywhere below would unwind the thread, drop `tx`, and leave
|
||||
// the UI with nothing but a closed channel — which it can only report
|
||||
// as "failed unexpectedly", losing the one piece of information that
|
||||
@@ -533,7 +534,7 @@ fn spawn_direct_login(
|
||||
) {
|
||||
let (tx, rx) = std::sync::mpsc::channel::<LoginMessage>();
|
||||
|
||||
std::thread::spawn(move || {
|
||||
executors::spawn(Executor::Network, "login", move || {
|
||||
let panic_tx = tx.clone();
|
||||
let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(move || {
|
||||
// Multi-thread for the reason the browser flow is: a current-thread
|
||||
@@ -767,7 +768,7 @@ fn spawn_folder_list(weak: slint::Weak<AppWindow>, ctl: Rc<LaunchController>, pa
|
||||
|
||||
let (tx, rx) = std::sync::mpsc::channel::<Result<Vec<String>, String>>();
|
||||
|
||||
std::thread::spawn(move || {
|
||||
executors::spawn(Executor::Network, "folders", move || {
|
||||
// Multi-thread for the same reason as the login worker: a
|
||||
// current-thread runtime left reqwest's connection future unpolled on
|
||||
// Android, so the await never resolved and the thread stopped without
|
||||
|
||||
+1
-1
@@ -747,7 +747,7 @@ fn drain_outbox(library: &Rc<library_ui::LibraryController>) {
|
||||
|
||||
let root = conn.account.root.clone();
|
||||
let rx = export::spawn_upload(conn, root, outbox);
|
||||
std::thread::spawn(move || {
|
||||
executors::spawn(executors::Executor::Io, "upload-log", move || {
|
||||
while let Ok(msg) = rx.recv() {
|
||||
match msg {
|
||||
export::UploadMessage::Status(s) => log::info!("export: {s}"),
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
//! Walking a remote library into the catalog, and pulling other devices'
|
||||
//! judgements out of the sidecars the walk finds along the way.
|
||||
|
||||
use crate::executors::{self, Executor};
|
||||
use dr_catalog::Catalog;
|
||||
use dr_sync::{Connection, RemoteBackend, RemoteId, RemotePath};
|
||||
use dr_types::FormatFilter;
|
||||
@@ -77,7 +78,7 @@ pub fn spawn_scan(
|
||||
) -> Receiver<ScanMessage> {
|
||||
let (tx, rx) = std::sync::mpsc::channel();
|
||||
|
||||
std::thread::spawn(move || {
|
||||
executors::spawn(Executor::Network, "scan", move || {
|
||||
let started = std::time::Instant::now();
|
||||
if let Err(e) = run_scan(&tx, conn, root, filter, catalog_path, started) {
|
||||
let _ = tx.send(ScanMessage::Failed {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
//! Writing local edits out to the catalog's sidecar outbox, and draining
|
||||
//! that outbox to the remote once a connection is available.
|
||||
|
||||
use crate::executors::{self, Executor};
|
||||
use crate::sidecar_cache::SidecarCache;
|
||||
use dr_sync::{Connection, RemoteBackend, RemoteError, RemoteId, RemotePath};
|
||||
use std::path::PathBuf;
|
||||
@@ -138,7 +139,7 @@ pub fn spawn_sidecar_writes(
|
||||
) -> Receiver<SidecarMessage> {
|
||||
let (tx, rx) = std::sync::mpsc::channel();
|
||||
|
||||
std::thread::spawn(move || {
|
||||
executors::spawn(Executor::Network, "sc-write", move || {
|
||||
let cache = SidecarCache::open(cache_dir);
|
||||
|
||||
// The runtime and the backend are only needed to *upload*. Offline,
|
||||
@@ -451,7 +452,7 @@ pub(super) async fn write_one_sidecar_online(
|
||||
pub fn spawn_outbox_drain(conn: Connection, cache_dir: PathBuf) -> Receiver<SidecarMessage> {
|
||||
let (tx, rx) = std::sync::mpsc::channel();
|
||||
|
||||
std::thread::spawn(move || {
|
||||
executors::spawn(Executor::Network, "outbox", move || {
|
||||
let cache = SidecarCache::open(cache_dir);
|
||||
let queued = cache.pending();
|
||||
if queued.is_empty() {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
//! The background sweeps: metadata extraction and thumbnail generation
|
||||
//! for whatever the catalog still owes, a chunk at a time.
|
||||
|
||||
use crate::executors::{self, Executor};
|
||||
use dr_catalog::Catalog;
|
||||
use dr_sync::{Connection, RemoteBackend, RemoteId, RemotePath};
|
||||
use dr_thumbs::ThumbStore;
|
||||
@@ -280,7 +281,7 @@ pub fn spawn_sweep(conn: Connection, catalog_path: PathBuf) -> Receiver<SweepMes
|
||||
// The one place this job names a decoder; everything below takes it.
|
||||
let decoder = dr_decode::default();
|
||||
|
||||
std::thread::spawn(move || {
|
||||
executors::spawn(Executor::Decode, "metadata", move || {
|
||||
let catalog = match Catalog::open(&catalog_path) {
|
||||
Ok(c) => c,
|
||||
Err(e) => {
|
||||
@@ -602,7 +603,7 @@ pub fn spawn_thumbnail_sweep(
|
||||
// The one place this job names a decoder; everything below takes it.
|
||||
let decoder = dr_decode::default();
|
||||
|
||||
std::thread::spawn(move || {
|
||||
executors::spawn(Executor::Decode, "thumb-sweep", move || {
|
||||
let finish_empty = |tx: &Sender<ThumbSweepMessage>| {
|
||||
let _ = tx.send(ThumbSweepMessage::Finished {
|
||||
stored: 0,
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
//! cache, dehydration, and the prefetcher that keeps the grid ahead of
|
||||
//! scrolling.
|
||||
|
||||
use crate::executors::{self, Executor};
|
||||
use crate::sidecar_cache::SidecarCache;
|
||||
use dr_catalog::Catalog;
|
||||
#[cfg(test)]
|
||||
@@ -136,7 +137,7 @@ pub fn spawn_pin_fetch(
|
||||
) -> Receiver<PinMessage> {
|
||||
let (tx, rx) = std::sync::mpsc::channel();
|
||||
|
||||
std::thread::spawn(move || {
|
||||
executors::spawn(Executor::Network, "pin", move || {
|
||||
let (store, catalog) = match (
|
||||
dr_catalog::Cache::open(&cache_dir, budget),
|
||||
Catalog::open(&catalog_path),
|
||||
@@ -324,7 +325,7 @@ pub fn spawn_dehydrate(
|
||||
) -> Receiver<usize> {
|
||||
let (tx, rx) = std::sync::mpsc::channel();
|
||||
|
||||
std::thread::spawn(move || {
|
||||
executors::spawn(Executor::Network, "dehydrate", move || {
|
||||
let Ok(catalog) = Catalog::open(&catalog_path) else {
|
||||
return;
|
||||
};
|
||||
@@ -442,7 +443,7 @@ pub fn spawn_sidecar_fetch(
|
||||
) -> Receiver<Option<dr_pipeline::Sidecar>> {
|
||||
let (tx, rx) = std::sync::mpsc::channel();
|
||||
|
||||
std::thread::spawn(move || {
|
||||
executors::spawn(Executor::Network, "sidecars", move || {
|
||||
let cache = SidecarCache::open(cache_dir);
|
||||
let path_str = sidecar_path(&image_path);
|
||||
|
||||
@@ -565,7 +566,7 @@ pub fn spawn_full_fetch(
|
||||
cache: Option<CacheContext>,
|
||||
) -> Receiver<Result<Vec<u8>, FetchFailure>> {
|
||||
let (tx, rx) = std::sync::mpsc::channel();
|
||||
std::thread::spawn(move || {
|
||||
executors::spawn(Executor::Network, "fetch", move || {
|
||||
let _ = tx.send(fetch_original(conn, &path, cache.as_ref()));
|
||||
});
|
||||
rx
|
||||
@@ -845,10 +846,10 @@ impl Prefetcher {
|
||||
let shared = std::sync::Arc::new(PrefetchShared::default());
|
||||
let (tx, events) = std::sync::mpsc::channel();
|
||||
let worker = shared.clone();
|
||||
std::thread::Builder::new()
|
||||
.name("prefetch".into())
|
||||
.spawn(move || serve_prefetches(&worker, &tx))
|
||||
.expect("spawning the prefetch worker");
|
||||
executors::try_spawn(Executor::Network, "prefetch", move || {
|
||||
serve_prefetches(&worker, &tx)
|
||||
})
|
||||
.expect("spawning the prefetch worker");
|
||||
Self { shared, events }
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
//! Generating thumbnails locally from a decoded preview or original, and
|
||||
//! the metadata that comes along for the ride.
|
||||
|
||||
use crate::executors::{self, Executor};
|
||||
#[cfg(test)]
|
||||
use dr_catalog::Catalog;
|
||||
use dr_sync::{Connection, RemoteBackend, RemoteId, RemotePath};
|
||||
@@ -101,7 +102,7 @@ pub fn spawn_thumbnails(
|
||||
// The one place this job names a decoder; everything below takes it.
|
||||
let decoder = dr_decode::default();
|
||||
|
||||
std::thread::spawn(move || {
|
||||
executors::spawn(Executor::Decode, "thumbs", move || {
|
||||
let mut store = match ThumbStore::open(&store_dir) {
|
||||
Ok(s) => Some(s),
|
||||
Err(e) => {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
//! Pushing local judgements out to XMP sidecars, and reloading sidecars a
|
||||
//! person chose to trust by hand.
|
||||
|
||||
use crate::executors::{self, Executor};
|
||||
use dr_catalog::Catalog;
|
||||
use dr_sync::{Connection, RemoteBackend, RemoteId, RemotePath};
|
||||
use std::path::PathBuf;
|
||||
@@ -32,7 +33,7 @@ pub struct XmpWrite {
|
||||
/// name. Reported once at the end, as the judgement writes are.
|
||||
pub fn spawn_xmp_writes(conn: Connection, writes: Vec<XmpWrite>) -> Receiver<XmpMessage> {
|
||||
let (tx, rx) = std::sync::mpsc::channel();
|
||||
std::thread::spawn(move || {
|
||||
executors::spawn(Executor::Network, "xmp-write", move || {
|
||||
let mut written = 0usize;
|
||||
let mut failed = 0usize;
|
||||
let mut last_error = None;
|
||||
@@ -140,7 +141,7 @@ pub fn spawn_xmp_reload(
|
||||
paths: Vec<String>,
|
||||
) -> Receiver<XmpMessage> {
|
||||
let (tx, rx) = std::sync::mpsc::channel();
|
||||
std::thread::spawn(move || {
|
||||
executors::spawn(Executor::Network, "xmp-reload", move || {
|
||||
let mut written = 0usize;
|
||||
let mut failed = 0usize;
|
||||
let mut last_error = None;
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
//! (`offline`) it hands off to on a failure. See `docs/dev/code-health.md`
|
||||
//! CH-1.
|
||||
|
||||
use crate::executors::{self, Executor};
|
||||
use std::path::PathBuf;
|
||||
use std::rc::Rc;
|
||||
use std::sync::mpsc::Receiver;
|
||||
@@ -271,7 +272,7 @@ enum CatalogOpen {
|
||||
fn spawn_catalog_open(path: PathBuf) -> Receiver<CatalogOpen> {
|
||||
let (tx, rx) = std::sync::mpsc::channel();
|
||||
|
||||
std::thread::spawn(move || {
|
||||
executors::spawn(Executor::Io, "catalog-open", move || {
|
||||
let started = std::time::Instant::now();
|
||||
let message = match Catalog::open_verified(&path) {
|
||||
Ok(cat) => {
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
//! library rather than in response to what the grid is showing.
|
||||
//! See `docs/dev/code-health.md` CH-1.
|
||||
|
||||
use crate::executors::{self, Executor};
|
||||
use std::rc::Rc;
|
||||
|
||||
use slint::ComponentHandle;
|
||||
@@ -33,7 +34,7 @@ fn spawn_scheduled_backup(ctl: &Rc<LibraryController>) {
|
||||
if !dr_catalog::recovery::backup_due(&catalog_path) {
|
||||
return;
|
||||
}
|
||||
std::thread::spawn(move || {
|
||||
executors::spawn(Executor::Io, "backup", move || {
|
||||
let catalog = match dr_catalog::Catalog::open(&catalog_path) {
|
||||
Ok(c) => c,
|
||||
Err(e) => {
|
||||
@@ -111,7 +112,7 @@ pub(super) fn start_derived_sync(window: &AppWindow, ctl: &Rc<LibraryController>
|
||||
let outbox = crate::export::outbox_dir(&conn.account);
|
||||
if crate::export::pending_count(&outbox) > 0 {
|
||||
let rx = crate::export::spawn_upload(conn.clone(), conn.account.root.clone(), outbox);
|
||||
std::thread::spawn(move || {
|
||||
executors::spawn(Executor::Io, "upload-log", move || {
|
||||
while let Ok(msg) = rx.recv() {
|
||||
match msg {
|
||||
crate::export::UploadMessage::Status(s) => log::info!("export: {s}"),
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
//! sensor data stays on the CPU at a fifth of the size — and it is what
|
||||
//! makes a twelve-frame set fit.
|
||||
|
||||
use crate::executors::{self, Executor};
|
||||
use std::collections::VecDeque;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::sync::mpsc::{Receiver, Sender};
|
||||
@@ -727,7 +728,7 @@ fn run_inner(
|
||||
let fill_for_writer = fill_cam.is_some();
|
||||
let file =
|
||||
std::fs::File::create(&out_path).map_err(|e| format!("{}: {e}", out_path.display()))?;
|
||||
let writer = std::thread::spawn(move || -> Result<(), String> {
|
||||
let writer = executors::spawn(Executor::Io, "dng-write", move || -> Result<(), String> {
|
||||
let mut file = std::io::BufWriter::new(file);
|
||||
dr_export::write_linear_dng(
|
||||
&mut file,
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
//! staged, the outbox drains and the library rescans, and the composite
|
||||
//! appears in the grid beside its sources.
|
||||
|
||||
use crate::executors::{self, Executor};
|
||||
use std::cell::{Cell, RefCell};
|
||||
use std::rc::Rc;
|
||||
use std::sync::mpsc::{Receiver, Sender};
|
||||
@@ -446,7 +447,7 @@ fn start<Fetch>(
|
||||
{
|
||||
let cancel = cancel.clone();
|
||||
let tx = tx.clone();
|
||||
std::thread::spawn(move || {
|
||||
executors::spawn(Executor::GpuSubmit, "merge", move || {
|
||||
let Some(frames) = fetch(&tx, &cancel) else {
|
||||
return;
|
||||
};
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
//! locally: the server may have normalised it, or refused it, and the list
|
||||
//! the user picks from should be the server's, not this app's guess at it.
|
||||
|
||||
use crate::executors::{self, Executor};
|
||||
use std::cell::RefCell;
|
||||
use std::rc::Rc;
|
||||
use std::sync::mpsc::TryRecvError;
|
||||
@@ -105,7 +106,7 @@ where
|
||||
{
|
||||
let work: Work = Box::new(work);
|
||||
let (tx, rx) = std::sync::mpsc::channel::<Result<Vec<String>, String>>();
|
||||
std::thread::spawn(move || {
|
||||
executors::spawn(Executor::Network, "remote-dirs", move || {
|
||||
// Multi-thread, for the reason the login worker records: a
|
||||
// current-thread runtime left reqwest's connection future unpolled on
|
||||
// Android, and the await never resolved.
|
||||
|
||||
@@ -41,6 +41,7 @@
|
||||
//! runs over everything the *chosen detector* has not been over, whatever a
|
||||
//! weaker one found there. Same job, one predicate differs.
|
||||
|
||||
use crate::executors::{self, Executor};
|
||||
use std::collections::HashSet;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::mpsc::{Receiver, Sender};
|
||||
@@ -1008,7 +1009,7 @@ pub fn spawn(
|
||||
) -> Receiver<FaceSweepMessage> {
|
||||
let (tx, rx) = std::sync::mpsc::channel();
|
||||
|
||||
std::thread::spawn(move || {
|
||||
executors::spawn(Executor::Decode, "repairs", move || {
|
||||
let finish_empty = |tx: &Sender<FaceSweepMessage>| {
|
||||
let _ = tx.send(FaceSweepMessage::Finished {
|
||||
images: 0,
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
//! halfway with no record of where it stopped is worse than one that reports
|
||||
//! "38 of 40".
|
||||
|
||||
use crate::executors::{self, Executor};
|
||||
use std::path::PathBuf;
|
||||
use std::sync::mpsc::Receiver;
|
||||
|
||||
@@ -169,7 +170,7 @@ pub fn spawn_move(
|
||||
) -> Receiver<TrashMessage> {
|
||||
let (tx, rx) = std::sync::mpsc::channel();
|
||||
|
||||
std::thread::spawn(move || {
|
||||
executors::spawn(Executor::Network, "trash", move || {
|
||||
let total = moves.len();
|
||||
let rt = match crate::net_runtime::build() {
|
||||
Ok(rt) => rt,
|
||||
@@ -281,7 +282,7 @@ pub fn spawn_purge(
|
||||
) -> Receiver<TrashMessage> {
|
||||
let (tx, rx) = std::sync::mpsc::channel();
|
||||
|
||||
std::thread::spawn(move || {
|
||||
executors::spawn(Executor::Network, "purge", move || {
|
||||
let total = paths.len();
|
||||
let rt = match crate::net_runtime::build() {
|
||||
Ok(rt) => rt,
|
||||
|
||||
Reference in New Issue
Block a user