native-code
Domain knowledge for building native extensions for the BEAM VM. Covers the
NIF boundary (C and Rust), SIMD via Zig, and the cross-cutting concerns that
apply regardless of implementation language.
For general language patterns (C, Zig, Rust), see droo-stack.
What You Get
- NIF boundary patterns (C
erl_nif.h, Rust Rustler, Zig SIMD)
- Scheduler contract and memory model rules
- Elixir-side module structure for NIF wrappers
- Common crash/leak patterns and fixes
- Two-level testing strategy (native unit + ExUnit boundary)
When to use
- Writing or reviewing NIF code (C
erl_nif.h or Rust Rustler)
- Integrating Zig SIMD routines into BEAM via C ABI
- Designing the Elixir-side module for a NIF
- Debugging scheduler issues, memory leaks, or crashes at the native boundary
- Building tree-sitter grammar parsers as NIFs
- Setting up precompiled NIF releases
When NOT to use
- General C, Zig, or Rust coding -- use droo-stack
- General Elixir patterns -- use droo-stack
- Raxol TUI framework -- use raxol skill
Reading guide
| Working on |
Read |
| Scheduler contract, memory model, deployment |
references/boundary-patterns |
| Elixir-side NIF module structure |
references/nif-elixir |
C NIFs (erl_nif.h, resources, tree-sitter) |
references/nif-c |
| Rust NIFs (Rustler, ResourceArc, safety) |
references/nif-rust |
Zig SIMD (@Vector, reductions, NIF integration) |
references/simd-zig |
Key principles
- Never block the BEAM scheduler -- NIFs must return within 1ms or use dirty schedulers
- The BEAM owns terms, you own native memory -- never store
ERL_NIF_TERM across NIF calls
- Crash in NIF = crash the entire VM -- no panics (Rust), no segfaults, no undefined behavior
- Test at two levels -- native unit tests (cargo test, zig test) AND ExUnit boundary tests
- Zero-copy where possible -- use BEAM binaries for large data transfer
Common pitfalls
| Mistake |
Impact |
Fix |
| NIF takes >1ms without dirty scheduler |
Scheduler starvation, latency spikes |
Add ERL_NIF_DIRTY_JOB_CPU_BOUND or schedule = "DirtyCpu" |
Storing ERL_NIF_TERM in native struct |
Use-after-free, undefined behavior |
Copy data out of terms, or use enif_make_copy with a process-independent env |
Rust panic! / unwrap() inside NIF |
Entire BEAM VM crashes |
Use Result, catch panics at boundary with std::panic::catch_unwind |
| Missing NIF stub in Elixir module |
Silent nil return or confusing error |
Stub must raise "NIF not loaded" |
| Not freeing native resources |
Memory leak proportional to call rate |
Register destructors via resource types |
See also
- droo-stack -- C, Zig, Rust language patterns
- raxol -- Elixir TUI framework (potential NIF consumer)
- nix -- packaging native deps with Nix (buildRustPackage, mkDerivation)
1---2name: native-code3description: NIF (Native Implemented Functions) development and SIMD patterns for Elixir/BEAM. TRIGGER when: writing NIFs in C or Rust (Rustler), using erl_nif.h, Zig SIMD code for BEAM integration, tree-sitter grammar NIFs, or discussing native performance boundaries in Elixir. DO NOT TRIGGER when: general C/Zig/Rust language questions (use droo-stack), general Elixir patterns (use droo-stack), Raxol framework (use raxol skill), or ffmpeg upstream / libav* hand-written assembly (use ffmpeg-asm skill).4---56# native-code78Domain knowledge for building native extensions for the BEAM VM. Covers the9NIF boundary (C and Rust), SIMD via Zig, and the cross-cutting concerns that10apply regardless of implementation language.1112For general language patterns (C, Zig, Rust), see droo-stack.1314## What You Get1516- NIF boundary patterns (C `erl_nif.h`, Rust Rustler, Zig SIMD)17- Scheduler contract and memory model rules18- Elixir-side module structure for NIF wrappers19- Common crash/leak patterns and fixes20- Two-level testing strategy (native unit + ExUnit boundary)2122## When to use2324- Writing or reviewing NIF code (C `erl_nif.h` or Rust Rustler)25- Integrating Zig SIMD routines into BEAM via C ABI26- Designing the Elixir-side module for a NIF27- Debugging scheduler issues, memory leaks, or crashes at the native boundary28- Building tree-sitter grammar parsers as NIFs29- Setting up precompiled NIF releases3031## When NOT to use3233- General C, Zig, or Rust coding -- use droo-stack34- General Elixir patterns -- use droo-stack35- Raxol TUI framework -- use raxol skill3637## Reading guide3839| Working on | Read |40| ------------------------------------------------- | --------------------------------------------------------------- |41| Scheduler contract, memory model, deployment | [references/boundary-patterns](references/boundary-patterns.md) |42| Elixir-side NIF module structure | [references/nif-elixir](references/nif-elixir.md) |43| C NIFs (`erl_nif.h`, resources, tree-sitter) | [references/nif-c](references/nif-c.md) |44| Rust NIFs (Rustler, ResourceArc, safety) | [references/nif-rust](references/nif-rust.md) |45| Zig SIMD (`@Vector`, reductions, NIF integration) | [references/simd-zig](references/simd-zig.md) |4647## Key principles48491. **Never block the BEAM scheduler** -- NIFs must return within 1ms or use dirty schedulers502. **The BEAM owns terms, you own native memory** -- never store `ERL_NIF_TERM` across NIF calls513. **Crash in NIF = crash the entire VM** -- no panics (Rust), no segfaults, no undefined behavior524. **Test at two levels** -- native unit tests (cargo test, zig test) AND ExUnit boundary tests535. **Zero-copy where possible** -- use BEAM binaries for large data transfer5455## Common pitfalls5657| Mistake | Impact | Fix |58| --------------------------------------- | -------------------------------------- | ------------------------------------------------------------------------------ |59| NIF takes >1ms without dirty scheduler | Scheduler starvation, latency spikes | Add `ERL_NIF_DIRTY_JOB_CPU_BOUND` or `schedule = "DirtyCpu"` |60| Storing `ERL_NIF_TERM` in native struct | Use-after-free, undefined behavior | Copy data out of terms, or use `enif_make_copy` with a process-independent env |61| Rust `panic!` / `unwrap()` inside NIF | Entire BEAM VM crashes | Use `Result`, catch panics at boundary with `std::panic::catch_unwind` |62| Missing NIF stub in Elixir module | Silent `nil` return or confusing error | Stub must `raise "NIF not loaded"` |63| Not freeing native resources | Memory leak proportional to call rate | Register destructors via resource types |6465## See also6667- droo-stack -- C, Zig, Rust language patterns68- raxol -- Elixir TUI framework (potential NIF consumer)69- nix -- packaging native deps with Nix (buildRustPackage, mkDerivation)