Expand description
§mem-isolate
: Contain memory leaks and fragmentation
It runs your function via a fork()
, waits for the result, and returns it.
This grants your code access to an exact copy of memory and state at the time just before the call, but guarantees that the function will not affect the parent process’s memory footprint in any way.
It forces functions to be memory pure (pure with respect to memory), even if they aren’t.
use mem_isolate::execute_in_isolated_process;
// No heap, stack, or program memory out here...
let result = mem_isolate::execute_in_isolated_process(|| {
// ...Can be affected by anything in here
Box::leak(Box::new(vec![42; 1024]));
});
To keep things simple, this crate exposes only two public interfaces:
execute_in_isolated_process
- The function that executes your code in an isolated process.MemIsolateError
- The error type that function returns ☝️
For more code examples, see
examples/
.
This
one
in particular shows how you should think about error handling.
For more information, see the README.
§Limitations
§Performance & Usability
- Works only on POSIX systems (Linux, macOS, BSD)
- Data returned from the
callable
function must be serialized to and from the child process (usingserde
), which can be expensive for large data. - Excluding serialization/deserialization cost,
execute_in_isolated_process()
introduces runtime overhead on the order of ~1ms compared to a direct invocation of thecallable
.
In performance-critical systems, these overheads can be no joke. However,
for many use cases, this is an affordable trade-off for the memory safety
and snapshotting behavior that mem-isolate
provides.
§Safety & Correctness
The use of fork()
, which this crate uses under the hood, has a slew of
potentially dangerous side effects and surprises if you’re not careful.
- For single-threaded use only: It is generally unsound to
fork()
in multi-threaded environments, especially when mutexes are involved. Only the thread that callsfork()
will be cloned and live on in the new process. This can easily lead to deadlocks and hung child processes if other threads are holding resource locks that the child process expects to acquire. - Signals delivered to the parent process won’t be automatically
forwarded to the child process running your
callable
during its execution. See one of theexamples/blocking-signals-*
files for an example of how to handle this. - Channels can’t be used to communicate between the parent and child processes. Consider using shared mmaps, pipes, or the filesystem instead.
- Shared mmaps break the isolation guarantees of this crate. The child
process will be able to mutate
mmap(..., MAP_SHARED, ...)
regions created by the parent process. - Panics in your
callable
won’t panic the rest of your program, as they would withoutmem-isolate
. That’s as useful as it is harmful, depending on your use case, but it’s worth noting. - Mutable references, static variables, and raw pointers accessible to
your
callable
won’t be modified as you would expect them to. That’s kind of the whole point of this crate… ;)
Failing to understand or respect these limitations will make your code more susceptible to both undefined behavior (UB) and heap corruption, not less.
§Feature Flags
The following crate feature flags are available:
tracing
: Enable tracing instrumentation. Instruments all high-level functions inlib.rs
and creates spans for child and parent processes inexecute_in_isolated_process
. Events are mostlydebug!
anderror!
level. Seeexamples/tracing.rs
for an example.
By default, no additional features are enabled.
Re-exports§
pub use errors::MemIsolateError;
Modules§
- errors
- Error handling is an important part of the
mem-isolate
crate. If something went wrong, we want to give the caller as much context as possible about how that error affected theircallable
, so they are well-equipped to know what to do about it.
Macros§
- trace
- Conditionally emits a trace-level log message when the “tracing” feature is enabled.
Traits§
- Deserialize
Owned - A data structure that can be deserialized without borrowing any data from the deserializer.
- Serialize
- A data structure that can be serialized into any data format supported by Serde.
Functions§
- execute_
in_ isolated_ process - Executes a user-supplied
callable
in a forked child process so that any memory changes during execution do not affect the parent. The child serializes its result (using bincode) and writes it through a pipe, which the parent reads and deserializes.