Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rely on the kernel for port allocation #498

Merged
merged 4 commits into from
Apr 5, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 15 additions & 32 deletions subxt/tests/integration/utils/node_proc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,6 @@ use std::{
},
net::TcpListener,
process,
sync::atomic::{
AtomicU16,
Ordering,
},
thread,
time,
};
Expand Down Expand Up @@ -189,38 +185,25 @@ impl TestNodeProcessBuilder {
}
}

/// The start of the port range to scan.
const START_PORT: u16 = 9900;
/// The end of the port range to scan.
const END_PORT: u16 = 10000;
/// The maximum number of ports to scan before giving up.
const MAX_PORTS: u16 = 1000;
/// Next available unclaimed port for test node endpoints.
static PORT: AtomicU16 = AtomicU16::new(START_PORT);

/// Returns the next set of 3 open ports.
///
/// Returns None if there are not 3 open ports available.
fn next_open_port() -> Option<(u16, u16, u16)> {
let mut ports = Vec::new();
let mut ports_scanned = 0u16;
loop {
let _ = PORT.compare_exchange(
END_PORT,
START_PORT,
Ordering::SeqCst,
Ordering::SeqCst,
);
let next = PORT.fetch_add(1, Ordering::SeqCst);
if TcpListener::bind(("0.0.0.0", next)).is_ok() {
ports.push(next);
if ports.len() == 3 {
return Some((ports[0], ports[1], ports[2]))
// Ask the kernel to allocate a port.
let next_port = || {
match TcpListener::bind(("127.0.0.1", 0)) {
Ok(listener) => {
if let Ok(address) = listener.local_addr() {
Some(address.port())
} else {
None
}
}
Err(_) => None,
}
ports_scanned += 1;
if ports_scanned == MAX_PORTS {
return None
}
}
};

// The ports allocated should be different, unless in
// the unlikely case that the system has less than 3 available ports.
Some((next_port()?, next_port()?, next_port()?))
}
41 changes: 10 additions & 31 deletions test-runtime/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,6 @@ use std::{
},
path::Path,
process::Command,
sync::atomic::{
AtomicU16,
Ordering,
},
thread,
time,
};
Expand All @@ -49,8 +45,7 @@ async fn run() {
env::var(SUBSTRATE_BIN_ENV_VAR).unwrap_or_else(|_| "substrate".to_owned());

// Run binary.
let port = next_open_port()
.expect("Cannot spawn substrate: no available ports in the given port range");
let port = next_open_port().expect("Cannot spawn substrate: no available ports");
let cmd = Command::new(&substrate_bin)
.arg("--dev")
.arg("--tmp")
Expand Down Expand Up @@ -138,33 +133,17 @@ async fn run() {
println!("cargo:rerun-if-changed=build.rs");
}

/// Returns the next open port, or None if no port found in range.
/// Returns the next open port, or None if no port found.
fn next_open_port() -> Option<u16> {
/// The start of the port range to scan.
const START_PORT: u16 = 9900;
/// The end of the port range to scan.
const END_PORT: u16 = 10000;
/// The maximum number of ports to scan before giving up.
const MAX_PORTS: u16 = 1000;

let next_port: AtomicU16 = AtomicU16::new(START_PORT);
let mut ports_scanned = 0u16;
loop {
// Loop back from the beginning if needed
let _ = next_port.compare_exchange(
END_PORT,
START_PORT,
Ordering::SeqCst,
Ordering::SeqCst,
);
let next = next_port.fetch_add(1, Ordering::SeqCst);
if TcpListener::bind(("0.0.0.0", next)).is_ok() {
return Some(next)
}
ports_scanned += 1;
if ports_scanned == MAX_PORTS {
return None
match TcpListener::bind(("127.0.0.1", 0)) {
Ok(listener) => {
if let Ok(address) = listener.local_addr() {
Some(address.port())
} else {
None
}
}
Err(_) => None,
}
}

Expand Down