Description
A lot of handle types coming from C libs are declared as Void*
, but this also has the effect of turning their wrapper classes non-atomic. Take LLVM::PassBuilderOptions
as an example:
lib LibLLVM
type PassBuilderOptionsRef = Void*
fun create_pass_builder_options = LLVMCreatePassBuilderOptions : PassBuilderOptionsRef
fun dispose_pass_builder_options = LLVMDisposePassBuilderOptions(options : PassBuilderOptionsRef)
end
# allocation calls `GC.malloc` rather than `.malloc_atomic`, because
# `@options` is an internal pointer
class LLVM::PassBuilderOptions
def initialize
@options = LibLLVM.create_pass_builder_options
@disposed = false
end
def to_unsafe
@options
end
def finalize
return if @disposed
@disposed = true
LibLLVM.dispose_pass_builder_options(self)
end
end
Given an unreachable LLVM::PassBuilderOptions
object on the heap, Boehm GC will scan the object's contents, but not the contents referred by its @options
variable, because it knows that the pointer doesn't belong to its own heap (we cannot pass the GC's allocator functions to LLVM). In some other C libraries, the Void*
might not even physically refer to a (virtual) memory address, e.g. most LibC::HANDLE
s. If we could guarantee this, we may as well use a regular integer type rather than Void*
:
lib LibLLVM
type PassBuilderOptionsRef = IntPtr
end
Since Crystal no longer sees any pointers inside LLVM::PassBuilderOptions
's instance variables, LLVM::PassBuilderOptions
will now use GC.malloc_atomic
instead, and the GC won't scan the object contents at all.
A similar argument holds for struct wrappers:
lib LibLLVM
type ValueRef = Void*
end
module LLVM::ValueMethods
def initialize(@unwrap : LibLLVM::ValueRef)
end
end
struct LLVM::Value
include ValueMethods
end
An Array(LLVM::Value)
maintains its buffer via Pointer(LLVM::Value).malloc
. This uses non-atomic allocation because @unwrap
is a pointer, but can be made atomic if LibLLVM::ValueRefValueRef
becomes an IntPtr
instead. Note that in this case LLVM manages the lifetimes of all LibLLVM::ValueRef
s; there is no C API to dispose a value.
IntPtr
may be obtained from LibC
, or we could also expose it publicly.