app-instance-detector

Crates.ioapp-instance-detector
lib.rsapp-instance-detector
version1.0.0
created_at2025-10-10 17:17:17.450845+00
updated_at2025-10-10 17:17:17.450845+00
descriptionA Rust library for detecting 1 or more instances of an app are running locally, that doesn't use lockfiles.
homepagehttps://github.com/AutonomoDev/app-instance-detector
repositoryhttps://github.com/AutonomoDev/app-instance-detector
max_upload_size
id1877245
size14,202
Theodore R. Smith (hopeseekr)

documentation

https://docs.rs/app-instance-detector

README

App Instance Detector

License: MIT

A lightweight, lockfile-free Rust library for detecting and communicating with other running instances of an application on the same machine.

It uses a TCP-based handshake mechanism, allowing applications to discover each other, find available ports, and exchange basic information.

This library is used in conjunction with the Autonomo CLI Platform.

GitHub: https://github.com/AutonomoDev/app-instance-detector

Features

  • Instance Detection: Scan a range of ports to find all running instances of your application.
  • Port Discovery: Find the next available TCP port for a new application instance to bind to.
  • Lockfile-Free: Avoids the complexities and cleanup issues associated with using filesystem lockfiles for singleton patterns.
  • Customizable Handshakes: Send custom messages to identify instances and receive custom responses (e.g., process ID, version, or application state).

Installation

To include app-instance-detector in your Rust application, add the following line to your Cargo.toml file:

[dependencies]
app-instance-detector = "0.9.0"

Getting Started & Usage

The primary use case is for an application to check for existing instances upon startup. If none are found, it starts its own "handshake server" to become discoverable by future instances.

Here is a complete example demonstrating how to use the library in a typical application.

Example main.rs

use app_instance_detector::{
    find_all_instances, find_next_available_port, start_handshake_server,
};
use std::time::Duration;
use std::{process, thread};

fn main() {
    const BASE_PORT: u16 = 9000;
    const MAX_SEARCH: u16 = 50;
    const HANDSHAKE_MESSAGE: &[u8] = b"ARE_YOU_MY_APP?";
    const TIMEOUT: Duration = Duration::from_millis(200);

    // 1. Check for other running instances of this application.
    let instances = find_all_instances(BASE_PORT, MAX_SEARCH, TIMEOUT, HANDSHAKE_MESSAGE);

    if !instances.is_empty() {
        println!("Found {} running instance(s):", instances.len());
        for (port, response) in instances {
            println!("  - Instance on port {} responded: '{}'", port, response);
        }
        // Optional: Exit if you only want a single instance of your app to run.
        println!("\nAnother instance is already running. Exiting.");
        return;
    }

    // 2. No other instances found. Let's start this one.
    // Find an available port for our new instance to listen on.
    let my_port = find_next_available_port(BASE_PORT, MAX_SEARCH);
    println!(
        "No running instances found. Starting new instance on port {}.",
        my_port
    );

    // 3. Start a background handshake server for this instance.
    // This server will respond to discovery requests from other instances.
    // We'll make it respond with this instance's Process ID (PID).
    let my_pid = process::id();
    start_handshake_server(my_port, move || {
        // This closure generates the response string for any handshake request.
        format!("MY_APP_INSTANCE_PID:{}", my_pid)
    });

    // --- Your application's main logic goes here ---
    println!(
        "\nApplication with PID {} is now running.", my_pid
    );
    println!("It is discoverable by other instances on port {}.", my_port);
    println!("Run this program again in another terminal to see detection in action.");

    // Keep the main thread alive to allow the background server to run.
    thread::sleep(Duration::from_secs(60));

    println!("\nApplication shutting down.");
}

How to Run the Example

  1. Save the code above as src/main.rs in your project.
  2. Ensure your Cargo.toml includes the dependency.
  3. Run the application from your terminal:
    cargo run
    
  4. While the first instance is running, open a new terminal and run cargo run again. The second instance will detect the first one and exit.

Compiling

To compile the library or any application using it, run the build script:

./build.sh

API Overview

The library exposes a few key functions:

  • find_all_instances(base_port, max_search, timeout, request_message) -> Vec<(u16, String)>: Scans a port range, sends a handshake request, and returns a list of ports and responses from discovered instances.
  • find_next_available_port(base_port, max_search) -> u16: Finds the first TCP port that is not in use within a given range.
  • start_handshake_server<F>(port, response_generator: F): Spawns a background thread that listens on the specified port and responds to any incoming connection using the provided response_generator closure.
  • is_port_in_use(port) -> bool: A simple utility to check if a specific port is currently in use.
  • send_handshake_request(port, timeout, request_message) -> anyhow::Result<String>: The low-level function for sending a request to a single port and getting a response.

License

This project is licensed under the MIT License.

Commit count: 0

cargo fmt