Skip to content

Add free / finalizer callback for void* v in nix_create_external_value #12470

@roberth

Description

@roberth

Feature Request: Add Free Callback for void* v in nix_create_external_value

Is your feature request related to a problem?

Currently, the nix_create_external_value API takes a void* v argument, which is typically used as user data. While the returned ExternalValue* is owned by the Nix evaluator, there is no clear mechanism for managing the lifecycle of void* v. Since Nix cannot free this memory itself, one would expect to see a free callback in [NixCExternalValueDesc](https://hydra.nixos.org/build/289480061/download/1/html/structNixCExternalValueDesc.html#details), but no such function exists.

Additionally, there does not appear to be any general mechanism to detect when an external value is freed, meaning the user has no clear way to know when to clean up their allocated memory. This leads to the question:
Am I just meant to leak my void* v?

Proposed solution

A free callback should be added to NixCExternalValueDesc to allow proper cleanup of void* v when an external value is garbage collected. This would ensure that resources are not leaked unnecessarily.

Implement a mechanism for registering finalizers in mkExternal, so that destructors may be called.

Since Nix uses a conservative garbage collector, finalizers are not always guaranteed to run before process exit. Even so, adding a free callback would improve resource management in most cases. This should be documented.

To ensure correctness, a test utility should be introduced to force finalization, allowing unit tests to verify that the free callback is correctly handled.

Alternative solutions

  • Document the current behavior explicitly to warn users about potential memory leaks.

Additional context

This issue was initially raised by sodiboo on Matrix. Investigation shows that while ExternalValueBase has a virtual destructor, it is never called because Value relies entirely on GC, and mkExternal does not register a finalizer. This means that external values are never explicitly freed, leading to potential memory leaks.

If Nix were to introduce finalization in the future, it could start invoking code written by C API consumers that has never run before. If finalization is not fully implemented and tested, this could result in unexpected crashes when untested code starts executing. Ensuring that finalization is tested from the start would mitigate this risk.


Add 👍 to issues you find important.

Metadata

Metadata

Assignees

No one assigned

    Labels

    c apiNix as a C library with a stable interfacefeatureFeature request or proposallanguageThe Nix expression language; parser, interpreter, primops, evaluation, etc

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions