memlink-runtime 0.1.0

Dynamic module loading and execution framework for Rust - plugin architectures, hot-reloadable code, and safe FFI
Documentation

Memlink Runtime

Dynamic module loading and execution framework for Rust


Overview

Memlink Runtime is a high-performance dynamic module loading system that enables plugin architectures, hot-reloadable code, and safe FFI execution. Load shared libraries at runtime, call methods on them, and unload them without restarting your application.

Key Features

  • ๐Ÿ”Œ Dynamic Loading - Load .so, .dll, .dylib files at runtime
  • ๐Ÿ›ก๏ธ Panic Isolation - Module panics don't crash your application
  • ๐Ÿ”ฅ Hot Reload - Replace modules with zero downtime
  • ๐Ÿ“Š Prometheus Metrics - Built-in observability
  • ๐Ÿงต Thread-Safe - Concurrent module calls from multiple threads
  • ๐Ÿ“ฆ Multi-Module - Load and manage multiple modules simultaneously

Quick Start

Installation

[dependencies]

memlink-runtime = "0.1.0"

Basic Example

use memlink_runtime::runtime::{Runtime, ModuleRuntime};
use memlink_runtime::resolver::ModuleRef;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Create runtime
    let runtime = Runtime::with_local_resolver();
    
    // Load a module
    let handle = runtime.load(
        ModuleRef::parse("./my_module.so")?
    )?;
    
    // Call a method
    let result = runtime.call(handle, "process", b"input data")?;
    println!("Result: {:?}", String::from_utf8_lossy(&result));
    
    // Unload when done
    runtime.unload(handle)?;
    
    Ok(())
}

Creating Modules

Modules are shared libraries that export three required functions:

Minimal Module (C)

#include <stdint.h>
#include <string.h>

__attribute__((visibility("default")))
int memlink_init(const unsigned char* config, unsigned long config_len) {
    (void)config;
    (void)config_len;
    return 0;
}

__attribute__((visibility("default")))
int memlink_call(unsigned int method_id, const unsigned char* args,
                unsigned long args_len, unsigned char* output) {
    (void)method_id;
    // Echo input back
    if (args_len > 0 && args != NULL) {
        memcpy(output, args, args_len);
    }
    return 0;
}

__attribute__((visibility("default")))
int memlink_shutdown(void) {
    return 0;
}

Build Commands

Platform Command
Linux cc -shared -fPIC -O2 -o my_module.so my_module.c
Windows cl /LD my_module.c /Fe:my_module.dll
macOS cc -shared -fPIC -O2 -o my_module.dylib my_module.c

See ABI Documentation for full specification.


Advanced Usage

Loading Multiple Modules

use memlink_runtime::runtime::{Runtime, ModuleRuntime};
use memlink_runtime::resolver::ModuleRef;
use std::sync::Arc;
use std::thread;

let runtime = Arc::new(Runtime::with_local_resolver());

// Load multiple modules
let math = runtime.load(ModuleRef::parse("./math.so")?)?;
let string = runtime.load(ModuleRef::parse("./string.so")?)?;
let crypto = runtime.load(ModuleRef::parse("./crypto.so")?)?;

// Call concurrently from different threads
let mut handles = vec![];

for (name, module) in [("math", math), ("string", string), ("crypto", crypto)] {
    let rt = Arc::clone(&runtime);
    let h = thread::spawn(move || {
        for i in 0..100 {
            rt.call(module, "process", format!("{}_{}", name, i).as_bytes()).unwrap();
        }
    });
    handles.push(h);
}

for h in handles {
    h.join().unwrap();
}

Hot Reload

use memlink_runtime::reload::ReloadConfig;
use std::time::Duration;

// Reload a module with new version
let config = ReloadConfig::default()
    .with_drain_timeout(Duration::from_secs(30))
    .with_state_preservation();

let reload_state = runtime.reload_with_config(
    old_handle,
    ModuleRef::parse("./my_module_v2.so")?,
    config
)?;

// Old module drains in-flight calls before unloading

Metrics and Monitoring

// Get usage statistics
let usage = runtime.get_usage(handle).unwrap();
println!("Calls: {}", usage.call_count);
println!("Arena: {} bytes ({:.2}%)", 
    usage.arena_bytes, 
    usage.arena_usage * 100.0
);

// Export Prometheus metrics
let metrics = RuntimeMetrics::new();
// ... after operations ...
println!("{}", metrics.prometheus_export());

Architecture

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚                    Your Application                      โ”‚
โ”‚                     (Memlink Runtime)                    โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚  Runtime                                                โ”‚
โ”‚  โ”œโ”€ Resolver (locate modules)                           โ”‚
โ”‚  โ”œโ”€ Loader (load shared libs)                           โ”‚
โ”‚  โ”œโ”€ Instance Manager (track loaded modules)             โ”‚
โ”‚  โ”œโ”€ Panic Handler (catch module panics)                 โ”‚
โ”‚  โ””โ”€ Metrics (Prometheus-compatible)                     โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
         โ”‚
         โ”œโ”€โ†’ [module_a.so] โ”€โ”€ memlink_init/call/shutdown
         โ”œโ”€โ†’ [module_b.dll] โ”€โ”€ memlink_init/call/shutdown
         โ””โ”€โ†’ [module_c.dylib] โ”€โ”€ memlink_init/call/shutdown

Components

Component Description
Runtime High-level API for module management
Resolver Locates and validates module files
Loader Loads shared libraries and resolves symbols
Instance Represents a loaded module
Arena Fast bump allocator for module memory
Metrics Collects and exports runtime statistics

Performance

Operation Latency Throughput
Module load 92 ฮผs -
Method call (64 bytes) 210 ns 4.7M calls/sec (single thread)
Module unload 52 ฮผs -
Hot reload 237 ฮผs -
Concurrent calls (8 threads) - 2.5M calls/sec
Memory overhead 0.7 MB/module -

Key Highlights:

  • โœ… Sub-microsecond call latency
  • โœ… Linear scalability up to 8 threads
  • โœ… Fast hot reload with zero downtime
  • โœ… Low memory footprint

See Performance Benchmarks for detailed methodology, full results, and optimization recommendations.


Use Cases

Plugin Systems

Load user-created plugins without recompiling your application:

// Load all plugins from a directory
for entry in std::fs::read_dir("./plugins")? {
    let path = entry?.path();
    if path.extension() == Some("so".as_ref()) {
        runtime.load(ModuleRef::parse(path.to_str().unwrap())?)?;
    }
}

Hot-Reloadable Business Logic

Update logic in production without downtime:

// Watch for file changes
notify::Watcher::new(move |event| {
    if event.path.ends_with("business_logic.so") {
        runtime.reload(handle, ModuleRef::parse("./business_logic.so")?)?;
    }
})?;

Sandboxed Execution

Isolate risky code that might panic:

// Module panics are caught and converted to errors
match runtime.call(handle, "risky_operation", data) {
    Ok(result) => println!("Success"),
    Err(Error::ModulePanicked(msg)) => eprintln!("Module panicked: {}", msg),
    Err(e) => eprintln!("Error: {}", e),
}

API Reference

Core Traits

Trait Purpose
ModuleRuntime Main interface for module operations
ModuleResolver Resolve module references to artifacts

Key Types

Type Description
Runtime Default runtime implementation
ModuleHandle Opaque handle to loaded module
ModuleRef Module reference (path or registry)
ModuleUsage Usage statistics per module
ReloadState Tracks hot-reload operations

Full API Documentation


Examples

Run the included examples:

# Multi-module concurrent calls

cargo run --example m2


# Build test modules first (Linux/WSL)

cd examples/modules/build

node build.js --linux

See examples/modules/README.md for module documentation.


Platform Support

Platform Status Extensions
Linux โœ… Tested .so
Windows โœ… Tested .dll
macOS โœ… Tested .dylib

License

Memlink Runtime is licensed under the Apache License 2.0 (LICENSE-APACHE).


Contributing

Contributions are welcome! Please:

  1. Fork the repository
  2. Create a feature branch
  3. Run cargo test and cargo clippy
  4. Submit a pull request

Development

# Build

cargo build


# Test

cargo test


# Lint

cargo clippy --all-targets


# Format

cargo fmt --all


# Build docs

cargo doc --open


Related Crates


Support