Skip to content

Latest commit

 

History

History
281 lines (201 loc) · 7.68 KB

File metadata and controls

281 lines (201 loc) · 7.68 KB

Contributing to UMR

Thank you for considering a contribution to the Universal Memory Runtime. This guide covers everything you need to get from zero to a merged pull request.


Table of contents

  1. Getting started
  2. Repository layout
  3. Build and test
  4. Coding standards
  5. Submitting changes
  6. Issue templates
  7. Design decisions

1. Getting started

# Fork, then clone
git clone https://github.com/your-fork/UMR.git
cd UMR

# Configure a debug build (recommended for development)
cmake -S . -B build-dev -DCMAKE_BUILD_TYPE=Debug \
      -DUMR_ENABLE_DEBUG=ON \
      -DUMR_BUILD_TESTS=ON \
      -DUMR_BUILD_BENCHMARKS=ON \
      -DUMR_BUILD_CLI=ON

cmake --build build-dev

# Run the full test suite
ctest --test-dir build-dev --output-on-failure

All tests must pass before you submit a PR.


2. Repository layout

core/           Runtime lifecycle, policy presets, TLS heap
allocator/      Engine implementations (arena, pool, slab, buddy, general)
fragment/       Fragment engine (split, merge, manager)
heap/           Global heap singleton, metadata, size classes
platform/       OS memory provider + mutex (linux/windows/macos)
profiler/       Tracker, leak detector, heap visualizer, snapshot
include/
  umr.h                  Public API (stable — ABI freeze post v1)
  umr/internal/          Private headers (no ABI guarantee)
tests/          Unit test files (one per module)
benchmarks/     Microbenchmarks
examples/       Getting-started programs (each self-contained)
tools/umr-cli/  In-process CLI tool
docs/           Markdown documentation
cmake/          CMake config + pkg-config templates

3. Build and test

Minimum requirements

Tool Version
CMake 3.20+
C compiler GCC 12+, Clang 15+, or MSVC 2022
C standard C11 minimum; C23 preferred

Debug vs Release

Always develop against a debug build (-DUMR_ENABLE_DEBUG=ON). This enables magic-value checks, abort-on-bad-free, and source location tracking in block headers.

Running a single test

The test runner is a single binary that runs all suites in order. To focus on one suite, add a printf-grep:

./build-dev/umr_tests 2>&1 | grep -A 20 "\[fragment\]"

Running benchmarks

cmake --build build-dev --target umr_bench_suite
./build-dev/umr_bench_suite

Compare output before and after your change. A regression of > 5 % on any benchmark in the suite needs a justification comment in the PR.


4. Coding standards

Language

  • Write C23 where possible; fall back to C11 when a feature is unavailable.
  • No C++ (the project is a pure C library).
  • No external dependencies (stdlib + OS syscalls only).

Naming

Kind Convention Example
Public API functions umr_verb_noun() umr_arena_create()
Internal functions module_verb_noun() frag_block_merge()
Types umr_noun_t umr_fragment_heap_t
Constants / macros UMR_UPPER_SNAKE UMR_FRAG_MIN_BLOCK
Static file-local k_ prefix for constants k_size_classes[]
Global singletons g_ prefix g_tracker, g_heap

File structure

Every .c file should open with a Doxygen file comment, then includes in this order:

  1. "umr.h" or the owning internal header
  2. Other internal UMR headers
  3. Standard library headers

No #include of one .c from another. All cross-module communication goes through the internal headers in include/umr/internal/.

Error handling

  • Functions that can fail return int (0 = success, -1 = error) or a pointer (NULL = failure). Never abort() in release builds except via UMR_ASSERT.
  • UMR_ASSERT(cond) is a debug-only abort. In release builds it compiles to ((void)0).

Memory

  • Never call malloc / free / new / delete inside the library. Use umr_os_alloc / umr_os_free for control structures that must be independent of the tracked heap.
  • Zero newly allocated control structures with memset.

Thread safety

  • The global heap (umr_heap_t) is protected by heap->lock.
  • Phase 2 engine control structs that are shared need their own mutex.
  • Read the mutex discipline comment at the top of the file you are modifying.

Debug builds

Guard all diagnostic paths with #if defined(UMR_DEBUG) && UMR_DEBUG. Release builds must compile cleanly with -O2 -DNDEBUG and no warnings.


5. Submitting changes

Commit style

subsystem: short imperative description (≤ 72 chars)

Optional longer explanation of why, not what.
Reference issues with: Fixes #N

Examples:

fragment: fix boundary-tag mismatch on zero-payload split
arena: add aligned allocation path for SIMD buffers
docs: update feature.md with Phase 4 strategy selection guide

Pull request checklist

Before opening a PR:

  • All tests pass (ctest --test-dir build-dev --output-on-failure)
  • Debug build compiles cleanly (-DUMR_ENABLE_DEBUG=ON, no warnings)
  • Release build compiles cleanly (no warnings on GCC/Clang/MSVC)
  • New functionality has tests in tests/test_<module>.c
  • Public API additions are documented in include/umr.h (Doxygen comment)
  • docs/feature.md updated if a feature contract changed
  • Benchmark impact noted in PR description (run bench_suite before/after)
  • No new external dependencies introduced

PR description template

## Summary
One paragraph explaining what this changes and why.

## Changes
- `module/file.c`: what changed and why
- `include/umr.h`: new API surface (if any)

## Test coverage
Which test cases cover the new/changed behaviour.

## Benchmark impact
Paste bench_suite output before and after (or "no measurable impact").

## Notes
Any design trade-offs, known limitations, or follow-up items.

6. Issue templates

Bug report

**Describe the bug**
A clear description of what went wrong.

**To reproduce**
Minimal C code that triggers the bug.

**Expected behaviour**
What should have happened.

**Environment**
- OS and version:
- Compiler and version:
- UMR version / commit:
- Build type (Debug/Release, UMR_ENABLE_DEBUG):

**Additional context**
Stack trace, address sanitiser output, or core dump if available.

Feature request

**Problem**
What allocation problem or use case is not addressed today?

**Proposed solution**
Sketch of the API or behaviour you have in mind.

**Alternatives considered**
Other approaches and why they are less suitable.

**Phase**
Which phase does this fit into?  (2 = specialized engines, 3 = observability,
4 = fragment, 5 = ecosystem, or new phase?)

7. Design decisions

A few standing rules to keep the project coherent:

No heap inside the library. Control structures (tracker table, pool chunks, arena chunks, fragment heap) are all allocated with umr_os_alloc so they never recurse into the heap they manage.

Public API is stable after v1. include/umr.h is the ABI boundary. Internal headers under include/umr/internal/ have no ABI guarantee and can change between minor versions.

Phase boundaries are real. Each phase must be fully implemented and passing tests before the next begins. Do not mix Phase N+1 code into a Phase N branch.

Feature flags over forks. Observability (tracker, leak detector) is always compiled in but zero-cost when disabled. Do not create separate "debug" and "release" builds of core algorithms.

Portability is not optional. Every change must compile and pass tests on Linux (x86-64), Windows (x86-64), and macOS (ARM64). Platform-specific code lives only in platform/.