105 lines · 3.2 KB · Rust
RawDownload
use std::{    io::{Read, Write},    net::{Ipv4Addr, SocketAddr, SocketAddrV4, TcpListener, TcpStream},    thread,    time::Duration,}; use util::channel::ReleaseChannel; const LOCALHOST: Ipv4Addr = Ipv4Addr::new(127, 0, 0, 1);const CONNECT_TIMEOUT: Duration = Duration::from_millis(10);const RECEIVE_TIMEOUT: Duration = Duration::from_millis(35);const SEND_TIMEOUT: Duration = Duration::from_millis(20); fn address() -> SocketAddr {    let port = match *util::channel::RELEASE_CHANNEL {        ReleaseChannel::Dev => 43737,        ReleaseChannel::Preview => 43738,        ReleaseChannel::Stable => 43739,        ReleaseChannel::Nightly => 43740,    };     SocketAddr::V4(SocketAddrV4::new(LOCALHOST, port))} fn instance_handshake() -> &'static str {    match *util::channel::RELEASE_CHANNEL {        ReleaseChannel::Dev => "Zed Editor Dev Instance Running",        ReleaseChannel::Nightly => "Zed Editor Nightly Instance Running",        ReleaseChannel::Preview => "Zed Editor Preview Instance Running",        ReleaseChannel::Stable => "Zed Editor Stable Instance Running",    }} #[derive(Debug, Clone, Copy, PartialEq, Eq)]pub enum IsOnlyInstance {    Yes,    No,} pub fn ensure_only_instance() -> IsOnlyInstance {    if *db::ZED_STATELESS || *util::channel::RELEASE_CHANNEL == ReleaseChannel::Dev {        return IsOnlyInstance::Yes;    }     if check_got_handshake() {        return IsOnlyInstance::No;    }     let listener = match TcpListener::bind(address()) {        Ok(listener) => listener,         Err(err) => {            log::warn!("Error binding to single instance port: {err}");            if check_got_handshake() {                return IsOnlyInstance::No;            }             // Avoid failing to start when some other application by chance already has            // a claim on the port. This is sub-par as any other instance that gets launched            // will be unable to communicate with this instance and will duplicate            log::warn!("Backup handshake request failed, continuing without handshake");            return IsOnlyInstance::Yes;        }    };     thread::spawn(move || {        for stream in listener.incoming() {            let mut stream = match stream {                Ok(stream) => stream,                Err(_) => return,            };             _ = stream.set_nodelay(true);            _ = stream.set_read_timeout(Some(SEND_TIMEOUT));            _ = stream.write_all(instance_handshake().as_bytes());        }    });     IsOnlyInstance::Yes} fn check_got_handshake() -> bool {    match TcpStream::connect_timeout(&address(), CONNECT_TIMEOUT) {        Ok(mut stream) => {            let mut buf = vec![0u8; instance_handshake().len()];             stream.set_read_timeout(Some(RECEIVE_TIMEOUT)).unwrap();            if let Err(err) = stream.read_exact(&mut buf) {                log::warn!("Connected to single instance port but failed to read: {err}");                return false;            }             if buf == instance_handshake().as_bytes() {                log::info!("Got instance handshake");                return true;            }             log::warn!("Got wrong instance handshake value");            false        }         Err(_) => false,    }}