Pitfalls

Pitfalls

Grouped by what breaks.

Lifetime

  • A raw pointer into the managed heap is valid only until the next collection.
  • &T, opaque, and cstr are untracked. An object reachable only through one will be reclaimed.
  • A raw C string cannot be relabeled into a managed string; it points at memory the collector does not own. Copy it with runtime::create_managed.
  • Anything the OS or a library tracks by address must be malloc'd, not managed: socket handles, TLS contexts, thread bookkeeping.
  • A managed object's fields are written after its allocation returns, so a collection in between must not see garbage. The allocator zeroes the payload for exactly this reason; do not defeat it by handing out uninitialized memory.

Threads

  • Every blocking call from an attached thread must be bracketed, or the process deadlocks silently.
  • No GC operation while parked.
  • No GC operation while holding a lock.
  • A callback fired from a parked event loop must unpark before running managed code, and re-resolve any managed pointer through its handle.
  • Every thread touching managed memory must attach from inside itself and detach when done.
  • Killing a thread outright can leave a lock held or an allocation half-finished. Use cooperative cancellation.
  • There is no header dependency tracking. Editing a .h does not rebuild the .c files that include it. Touch the sources or clean.
  • A per-platform source file must compile to nothing on other platforms, because every listed source is compiled for every target.
  • Archives must follow the objects that reference them.
  • Symbol names are not mangled, so two packages exporting the same name collide.
  • Duplicated headers across packages can drift apart silently. Prefer one copy in one package and include it.

FFI

  • A declaration without p_fn or p_var is invisible, with no error.
  • Reserved words cannot name a parameter, function, or variable. fn, in, and arch catch people most often.
  • Raw * is rejected; use p_gc(T), p_gc_opaque, or p_opaque.
  • p_gcsafe goes before the return type.
  • A sibling .peko file shadows a .peko.h of the same name.
  • Omitting p_gcsafe on a function that can allocate or block is a silent correctness bug, not a compile error.

Language

  • The excluded branch of a platform or arch block is not type-checked.
  • An unknown platform name in such a block produces no diagnostic; it just never matches.
  • danger_cast between non-numeric types emits no instruction. It is a relabel.
  • constant<T> on a non-literal is also a relabel.
  • Pointer arithmetic does not exist.

When something is already wrong

Run the suspect workload under PEKO_GC_STRESS=1 to force a collection at every allocation. A corruption that appears once an hour becomes reproducible, and the crash moves close to its cause. PEKO_GC_VERIFY adds a mark-completeness check on top.

If a crash lands inside a method dispatch on an object unrelated to your change, suspect a stale managed pointer held across an allocating call.