Module jlrs::memory[][src]

Structs and traits to protect data from being garbage collected.

Julia is a garbage-collected programming language, whenever Julia is called through its C API the user is responsible for ensuring the garbage collector is made aware when this value is in use. In practice, this comes down to managing a linked list of gc frames which contain pointers to values that are in use; these values have been rooted. While a value is rooted, it and all other values it contains pointers to will not be freed by the garbage collector. Several macros are available in C that create a new frame and push it to the stack, and a macro to pop that frame from the stack. These macros cannot be used in Rust because they use alloca to allocate the dynamically-sized raw frame on the stack. This module provides a reimplementation of this system which is backed by a stack of pages, each page can store multiple frames.

In order to call Julia from Rust with jlrs, the methods Julia::scope or Julia::scope_with_slots must be used. These methods take a closure that take a Global and mutable reference to a GcFrame; before calling the closure the frame is created and pushed the stack, and it’s popped when it’s dropped. This means that any value that is rooted in that frame will be protected from garbage collection while inside the closure. Global is an access token for global data in Julia, like modules and their contents.

Most functionality provided by the GcFrame is available through three traits; Frame, Scope and ScopeExt. Frame provides access to the number of roots and slots a frame has, and its capacity, while the other two provide methods to create a nested scope. The simplest kind of these methods is ScopeExt::scope, like Julia::scope it creates a new frame and pushes it to the stack, calls the given closure with a mutable reference to that new frame, the frame is popped after the closure returns. The main limitation of this method is that it can’t be used to create a new Julia value and return it from the scope. This functionality is provided by the Scope trait. The methods Scope::value_scope and Scope::result_scope can be used to return a value or the result of a function call from a closure and postpone rooting that result until the target frame can be used again.

The closure that these two methods will call doesn’t only provide a new frame, but also an Output. The closures must return a value of a specific type. The frame can be used to allocate temporary values. When all temporary values have been created, the Output must be converted to an OutputScope by calling Output::into_scope. The frame can now no longer be used because this method will borrow it for the rest of the closure, but because it implements Scope it can create a nested scope which propagates the output to this new scope, and be used to create a new value or call a Julia function. This result can be returned from the closure. It is rooted if the parent scope is a frame, and left unrooted if it’s an OutputScope.

There are two other kinds of frame, NullFrame and AsyncGcFrame. The first of these can be used when calling a Rust function from Julia with ccall. It doesn’t support creating nested scopes and can’t root any values, but it can be used to access array data. Accessing this data requires a frame to prevent mutable aliasing. The other is available when the async feature flag is enabled. AsyncGcFrame offers the same methods as GcFrame, implements the same traits, but also provides async variations of Scope::value_scope, Scope::result_scope, and ScopeExt::scope. This frame type can be used by implementing the JuliaTask trait.

Modules

frame

Frames protect values from garbage collection.

global

Access token for global Julia data.

mode

The runtime modes

output

Root a value in an earlier frame.

traits

Traits used to protect Julia data from being garbage collected, managing their lifetimes, and controlling the garbage collector.