# C Coding Conventions for Aviation

## Memory Management Rules

### 1. Allocation and Deallocation

#### Paired Operations
- Every `malloc`/`calloc` must have a corresponding `free`
- Every `pthread_mutex_init` must have a corresponding `pthread_mutex_destroy`
- Every retained reference must have a corresponding release

#### Allocation Pattern
```c
// GOOD: Check allocation immediately
Type *obj = calloc(1, sizeof(Type));
if (!obj) {
    return NULL;
}

// BAD: Missing null check
Type *obj = malloc(sizeof(Type));
obj->field = value;  // Potential crash
```

### 2. Resource Ownership

#### Clear Ownership
- Document who owns allocated memory in comments
- Use naming conventions: `_create()` transfers ownership, `_get()` does not

#### Reference Counting Pattern
```c
typedef struct {
    atomic_int ref_count;
    // ... other fields
} RefCountedObject;

RefCountedObject *obj_retain(RefCountedObject *obj) {
    if (obj) {
        atomic_fetch_add(&obj->ref_count, 1);
    }
    return obj;
}

void obj_release(RefCountedObject *obj) {
    if (obj && atomic_fetch_sub(&obj->ref_count, 1) == 1) {
        obj_destroy(obj);
    }
}
```

### 3. Error Handling

#### Cleanup on Error
```c
// GOOD: Clean up partial allocations
Object *object_create() {
    Object *obj = calloc(1, sizeof(Object));
    if (!obj) return NULL;

    obj->buffer = malloc(BUFFER_SIZE);
    if (!obj->buffer) {
        free(obj);
        return NULL;
    }

    obj->mutex = malloc(sizeof(pthread_mutex_t));
    if (!obj->mutex) {
        free(obj->buffer);
        free(obj);
        return NULL;
    }

    if (pthread_mutex_init(obj->mutex, NULL) != 0) {
        free(obj->mutex);
        free(obj->buffer);
        free(obj);
        return NULL;
    }

    return obj;
}
```

### 4. Thread Safety

#### Mutex Patterns
```c
// GOOD: Always unlock on all paths
void critical_section(Object *obj) {
    pthread_mutex_lock(&obj->mutex);

    if (error_condition) {
        pthread_mutex_unlock(&obj->mutex);
        return;
    }

    // ... work ...

    pthread_mutex_unlock(&obj->mutex);
}
```

#### Snapshot Pattern for Callbacks
```c
// GOOD: Copy under lock, execute outside
void emit_event(EventBus *bus, Event *evt) {
    Listener snapshot[MAX_LISTENERS];
    int count;

    pthread_mutex_lock(&bus->mutex);
    count = bus->listener_count;
    memcpy(snapshot, bus->listeners, count * sizeof(Listener));
    pthread_mutex_unlock(&bus->mutex);

    // Safe to call without holding lock
    for (int i = 0; i < count; i++) {
        snapshot[i].callback(evt, snapshot[i].userdata);
    }
}
```

### 5. Native Handle Management

#### Retain/Release Balance
```c
typedef struct {
    NativeHandleRetain retain_fn;
    NativeHandleRelease release_fn;
    void *current_handle;
} HandleManager;

void handle_manager_set(HandleManager *mgr, void *new_handle) {
    // Retain new before releasing old (prevents premature dealloc)
    if (new_handle && mgr->retain_fn) {
        mgr->retain_fn(new_handle);
    }

    if (mgr->current_handle && mgr->release_fn) {
        mgr->release_fn(mgr->current_handle);
    }

    mgr->current_handle = new_handle;
}
```

### 6. Static Analysis

#### Required Attributes
```c
// Use compiler attributes for better static analysis
void *my_malloc(size_t size) __attribute__((malloc));
void my_free(void *ptr) __attribute__((nonnull(1)));
const char *get_string(void) __attribute__((returns_nonnull));
```

### 7. Memory Debugging

#### Debug Allocations
```c
#ifdef DEBUG_MEMORY
#define ALLOC(size) debug_malloc(size, __FILE__, __LINE__)
#define FREE(ptr) debug_free(ptr, __FILE__, __LINE__)
#else
#define ALLOC(size) malloc(size)
#define FREE(ptr) free(ptr)
#endif
```

### 8. Documentation Requirements

Every allocation function must document:
- Who owns the returned memory
- When/how it should be freed
- Thread safety guarantees
- Error conditions

```c
/**
 * Creates a new widget.
 * @return Newly allocated widget. Caller must call widget_destroy() when done.
 *         Returns NULL on allocation failure.
 * Thread-safe: Yes
 */
Widget *widget_create(void);
```

## Static Analysis Tools

### Required Checks
1. Run clang static analyzer: `scan-build make`
2. Use AddressSanitizer in debug builds
3. Use Valgrind for leak detection
4. Enable all compiler warnings: `-Wall -Wextra -Wpedantic`

### Pre-commit Checks
```bash
# Add to .git/hooks/pre-commit
clang-format --dry-run -Werror *.c *.h
cppcheck --enable=all --error-exitcode=1 *.c
```

## Testing Requirements

### Memory Leak Tests
- Every module must have leak tests
- Use Valgrind in CI pipeline
- Test error paths explicitly

### Example Test
```c
void test_create_destroy_no_leak() {
    for (int i = 0; i < 1000; i++) {
        Object *obj = object_create();
        assert(obj != NULL);
        object_destroy(obj);
    }
    // Valgrind should report 0 leaks
}

void test_error_path_no_leak() {
    // Force allocation failure
    force_malloc_fail_after(2);
    Object *obj = object_create();
    assert(obj == NULL);
    // Valgrind should report 0 leaks
}
```
