Coverage Report

Created: 2026-11-13 23:29

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/build/source/src/bin/nativelink.rs
Line
Count
Source
1
// Copyright 2024 Trace Machina, Inc. All rights reserved.
2
//
3
// Licensed under the Functional Source License, Version 1.1, Apache 2.0 Future License (the "License");
4
// you may not use this file except in compliance with the License.
5
// You may obtain a copy of the License at
6
//
7
//    See LICENSE file for details
8
//
9
// Unless required by applicable law or agreed to in writing, software
10
// distributed under the License is distributed on an "AS IS" BASIS,
11
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
// See the License for the specific language governing permissions and
13
// limitations under the License.
14
15
// The worker's action future is a long chain of combinators; proving it
16
// Send for the spawn walks the whole chain, and the default limit of 128 is
17
// a few steps short of it on the sanitizer toolchain.
18
#![recursion_limit = "256"]
19
20
use clap::Parser;
21
use futures::future::try_join_all;
22
use mimalloc::MiMalloc;
23
use nativelink_config::cas_server::{CasConfig, GlobalConfig};
24
use nativelink_config::stores::ConfigDigestHashFunction;
25
use nativelink_error::{Code, Error, ResultExt, make_err};
26
use nativelink_util::common::fs::set_open_file_limit;
27
use nativelink_util::digest_hasher::{DigestHasherFunc, set_default_digest_hasher_func};
28
use nativelink_util::fs;
29
#[cfg(target_family = "unix")]
30
use nativelink_util::shutdown_guard::Priority;
31
use nativelink_util::shutdown_guard::ShutdownGuard;
32
use nativelink_util::store_trait::{
33
    DEFAULT_DIGEST_SIZE_HEALTH_CHECK_CFG, set_default_digest_size_health_check,
34
};
35
use nativelink_util::telemetry::init_tracing;
36
use tokio::select;
37
#[cfg(target_family = "unix")]
38
use tokio::signal::unix::{SignalKind, signal};
39
use tokio::sync::oneshot::Sender;
40
use tokio::sync::{broadcast, oneshot};
41
use tracing::trace_span;
42
#[cfg(target_family = "unix")]
43
use tracing::warn;
44
45
#[global_allocator]
46
static GLOBAL: MiMalloc = MiMalloc;
47
48
/// Broadcast Channel Capacity
49
/// Note: The actual capacity may be greater than the provided capacity.
50
const BROADCAST_CAPACITY: usize = 1;
51
52
0
fn install_default_rustls_crypto_provider() {
53
0
    drop(tokio_rustls::rustls::crypto::ring::default_provider().install_default());
54
0
}
55
56
/// Backend for bazel remote execution / cache API.
57
#[derive(Parser, Debug)]
58
#[clap(
59
    author = "Trace Machina, Inc. <nativelink@tracemachina.com>",
60
    version,
61
    about,
62
    long_about = None
63
)]
64
struct Args {
65
    /// Config file to use.
66
    #[clap(value_parser)]
67
    config_file: String,
68
}
69
70
0
async fn inner_main(
71
0
    cfg: CasConfig,
72
0
    shutdown_tx: broadcast::Sender<ShutdownGuard>,
73
0
    scheduler_shutdown_tx: Sender<()>,
74
0
) -> Result<(), Error> {
75
0
    let server = nativelink::build_server(cfg, shutdown_tx, scheduler_shutdown_tx).await?;
76
77
0
    let result = select! {
78
0
        result = try_join_all(server.root_futures) => result.map(|_| ()),
79
0
        result = server.single_use_complete_rx, if server.single_use_enabled => {
80
0
            result.map_err(|err| make_err!(Code::Internal, "Single-use worker completion channel closed: {err}"))
81
        },
82
    };
83
0
    if let Err(e) = result {
84
0
        panic!("{e:?}");
85
0
    }
86
87
0
    Ok(())
88
0
}
89
90
0
fn get_config() -> Result<CasConfig, Error> {
91
0
    let args = Args::parse();
92
0
    CasConfig::try_from_json5_file(&args.config_file)
93
0
}
94
95
0
fn main() -> Result<(), Box<dyn core::error::Error>> {
96
0
    install_default_rustls_crypto_provider();
97
98
    // Set QoS to USER_INITIATED on the main thread *before* the tokio
99
    // runtime is built so the spawned worker threads inherit P-core
100
    // scheduling preference via pthread QoS inheritance on Apple
101
    // Silicon. `on_thread_start` below is a belt-and-suspenders hook
102
    // for any thread that misses the inherited class (e.g. tokio
103
    // blocking pool threads created lazily). No-op on non-macOS.
104
0
    let _ = nativelink_worker::qos::set_user_initiated();
105
106
    #[expect(clippy::disallowed_methods, reason = "starting main runtime")]
107
0
    let runtime = tokio::runtime::Builder::new_multi_thread()
108
0
        .on_thread_start(|| {
109
0
            let _ = nativelink_worker::qos::set_user_initiated();
110
0
        })
111
0
        .enable_all()
112
0
        .build()?;
113
114
    // The OTLP exporters need to run in a Tokio context
115
    // Do this first so all the other logging works
116
    #[expect(clippy::disallowed_methods, reason = "tracing init on main runtime")]
117
0
    runtime.block_on(async { tokio::spawn(async { init_tracing().await }).await? })?;
118
119
0
    let mut cfg = get_config()?;
120
121
0
    let global_cfg = if let Some(global_cfg) = &mut cfg.global {
122
0
        if global_cfg.max_open_files == 0 {
123
0
            global_cfg.max_open_files = fs::DEFAULT_OPEN_FILE_LIMIT;
124
0
        }
125
0
        if global_cfg.default_digest_size_health_check == 0 {
126
0
            global_cfg.default_digest_size_health_check = DEFAULT_DIGEST_SIZE_HEALTH_CHECK_CFG;
127
0
        }
128
129
0
        *global_cfg
130
    } else {
131
0
        GlobalConfig {
132
0
            max_open_files: fs::DEFAULT_OPEN_FILE_LIMIT,
133
0
            default_digest_hash_function: None,
134
0
            default_digest_size_health_check: DEFAULT_DIGEST_SIZE_HEALTH_CHECK_CFG,
135
0
        }
136
    };
137
0
    set_open_file_limit(global_cfg.max_open_files);
138
0
    set_default_digest_hasher_func(DigestHasherFunc::from(
139
0
        global_cfg
140
0
            .default_digest_hash_function
141
0
            .unwrap_or(ConfigDigestHashFunction::Sha256),
142
0
    ))?;
143
0
    set_default_digest_size_health_check(global_cfg.default_digest_size_health_check)?;
144
145
    // Initiates the shutdown process by broadcasting the shutdown signal via the `oneshot::Sender` to all listeners.
146
    // Each listener will perform its cleanup and then drop its `oneshot::Sender`, signaling completion.
147
    // Once all `oneshot::Sender` instances are dropped, the worker knows it can safely terminate.
148
0
    let (shutdown_tx, _) = broadcast::channel::<ShutdownGuard>(BROADCAST_CAPACITY);
149
    #[cfg(target_family = "unix")]
150
0
    let shutdown_tx_clone = shutdown_tx.clone();
151
    #[cfg(target_family = "unix")]
152
0
    let mut shutdown_guard = ShutdownGuard::default();
153
154
0
    let (scheduler_shutdown_tx, scheduler_shutdown_rx) = oneshot::channel();
155
156
    // Not used in non-unix targets
157
    #[cfg(not(target_family = "unix"))]
158
    drop(scheduler_shutdown_rx);
159
160
    // SIGINT takes the SIGTERM path: a worker stopped from a terminal
161
    // drains like one stopped by its supervisor. A second SIGINT during
162
    // the drain exits at once, for the operator who meant it.
163
    #[cfg(target_family = "unix")]
164
    #[expect(clippy::disallowed_methods, reason = "signal handler on main runtime")]
165
0
    runtime.spawn(async move {
166
0
        let mut sigterm = signal(SignalKind::terminate()).expect("Failed to listen to SIGTERM");
167
0
        let mut sigint = signal(SignalKind::interrupt()).expect("Failed to listen to SIGINT");
168
0
        let exit_code = tokio::select! {
169
0
            _ = sigterm.recv() => { warn!("Process terminated via SIGTERM"); 143 }
170
0
            _ = sigint.recv() => { warn!("Process terminated via SIGINT, draining; send it again to exit at once"); 130 }
171
        };
172
0
        tokio::spawn(async move {
173
0
            sigint.recv().await;
174
0
            eprintln!("User terminated process via second SIGINT");
175
0
            std::process::exit(130);
176
        });
177
0
        drop(shutdown_tx_clone.send(shutdown_guard.clone()));
178
0
        scheduler_shutdown_rx
179
0
            .await
180
0
            .expect("Failed to receive scheduler shutdown");
181
0
        let () = shutdown_guard.wait_for(Priority::P0).await;
182
0
        warn!("Successfully shut down nativelink.");
183
0
        std::process::exit(exit_code);
184
    });
185
186
    #[expect(clippy::disallowed_methods, reason = "waiting on everything to finish")]
187
0
    runtime
188
0
        .block_on(async {
189
0
            trace_span!("main")
190
0
                .in_scope(|| async { inner_main(cfg, shutdown_tx, scheduler_shutdown_tx).await })
191
0
                .await
192
0
        })
193
0
        .err_tip(|| "main() function failed")?;
194
0
    Ok(())
195
0
}