1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
use std::fmt;
use std::num::NonZeroUsize;
use std::sync::Arc;
use tokio::sync::Semaphore;
/// Concurrency limit settings.
// TODO(konsti): We should find a pattern that doesn't require having both semaphores and counts.
#[derive(Clone)]
pub struct Concurrency {
/// The maximum number of concurrent downloads.
///
/// Note this value must be non-zero.
pub downloads: usize,
/// The maximum number of concurrent builds.
///
/// Note this value must be non-zero.
pub builds: usize,
/// The maximum number of concurrent installs.
///
/// Note this value must be non-zero.
pub installs: usize,
/// A global semaphore to limit the number of concurrent downloads.
pub downloads_semaphore: Arc<Semaphore>,
/// A global semaphore to limit the number of concurrent builds.
pub builds_semaphore: Arc<Semaphore>,
}
/// Custom `Debug` to hide semaphore fields from `--show-settings` output.
#[expect(clippy::missing_fields_in_debug)]
impl fmt::Debug for Concurrency {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Concurrency")
.field("downloads", &self.downloads)
.field("builds", &self.builds)
.field("installs", &self.installs)
.finish()
}
}
impl Default for Concurrency {
fn default() -> Self {
Self::new(Self::DEFAULT_DOWNLOADS, Self::threads(), Self::threads())
}
}
impl Concurrency {
// The default concurrent downloads limit.
pub const DEFAULT_DOWNLOADS: usize = 50;
/// Create a new [`Concurrency`] with the given limits.
pub fn new(downloads: usize, builds: usize, installs: usize) -> Self {
Self {
downloads,
builds,
installs,
downloads_semaphore: Arc::new(Semaphore::new(downloads)),
builds_semaphore: Arc::new(Semaphore::new(builds)),
}
}
// The default concurrent builds and install limit.
pub fn threads() -> usize {
std::thread::available_parallelism()
.map(NonZeroUsize::get)
.unwrap_or(1)
}
}