Skip to content

Latest commit

 

History

History
484 lines (356 loc) · 14.4 KB

File metadata and controls

484 lines (356 loc) · 14.4 KB

Kotlin Native GC Integration Specification

Version: 1.0.0
Date: 2024-12-10
Status: Reference Implementation Complete

1. Executive Summary

This specification defines the integration layer between C++ coroutine libraries and Kotlin Native's garbage collector. The solution provides zero-overhead GC coordination for C++ code that may be called from Kotlin Native, using weak linking to maintain standalone compatibility.

1.1 Key Features

  • Zero-overhead standalone operation: When not linked with Kotlin Native, all GC functions are inlined no-ops
  • Automatic GC coordination: RAII guards manage thread state transitions
  • Explicit control: Manual state switching for fine-grained control
  • Thread-safe: Per-thread state management
  • Portable: Single codebase works with or without Kotlin Native

1.2 Scope

This specification covers:

  • Thread state management APIs
  • RAII guard implementation
  • Safepoint checking mechanisms
  • Build configuration options
  • Safety guarantees and constraints

2. Architecture

2.1 Thread State Model

Kotlin Native uses a two-state model for thread management:

State Description GC Behavior Capabilities
kRunnable Thread can execute Kotlin code Waits at safepoints Can access Kotlin objects, call Kotlin functions
kNative Thread is in native code Proceeds without waiting Cannot access Kotlin objects, unrestricted C++

2.2 State Transitions

┌──────────────┐  switchThreadStateNative()   ┌──────────────┐
│              │─────────────────────────────>│              │
│  kRunnable   │                              │   kNative    │
│              │<─────────────────────────────│              │
└──────────────┘  switchThreadStateRunnable() └──────────────┘

Transition Costs:

  • Standalone: 0 cycles (inlined away)
  • With Kotlin Native: ~10-50 cycles (atomic operation + state check)

2.3 Component Architecture

┌─────────────────────────────────────────┐
│  C++ Application Code                    │
├─────────────────────────────────────────┤
│  KotlinGCBridge.hpp                     │
│  ┌─────────────────────────────────────┐│
│  │ KotlinNativeStateGuard (RAII)      ││
│  │ check_safepoint()                   ││
│  │ is_kotlin_native_runtime_available()││
│  └─────────────────────────────────────┘│
├─────────────────────────────────────────┤
│  Weak-Linked Function Declarations      │
│  ┌─────────────────────────────────────┐│
│  │ Kotlin_mm_switchThreadStateNative()││
│  │ Kotlin_mm_switchThreadStateRunnable()│
│  │ Kotlin_mm_safePointWhileLoopBody()  ││
│  └─────────────────────────────────────┘│
└────────────┬────────────────────┬────────┘
             │                    │
    Standalone Mode      Kotlin Native Mode
             │                    │
      ┌──────▼──────┐      ┌──────▼───────┐
      │ Inline Stubs│      │ Kotlin Native│
      │ (no-ops)    │      │ Runtime      │
      └─────────────┘      │ Memory.cpp   │
                           └──────────────┘

3. API Specification

3.1 Core Functions

3.1.1 Kotlin_mm_switchThreadStateNative()

extern "C" void Kotlin_mm_switchThreadStateNative();

Purpose: Transition current thread from kRunnable to kNative state.

Preconditions:

  • Thread must be in kRunnable state
  • No Kotlin objects on C++ stack (undefined behavior if accessed afterward)

Postconditions:

  • Thread is in kNative state
  • GC will not wait for this thread
  • Cannot safely access Kotlin-managed objects

Complexity: O(1) - atomic store + state check

3.1.2 Kotlin_mm_switchThreadStateRunnable()

extern "C" void Kotlin_mm_switchThreadStateRunnable();

Purpose: Transition current thread from kNative to kRunnable state.

Preconditions:

  • Thread must be in kNative state

Postconditions:

  • Thread is in kRunnable state
  • May access Kotlin-managed objects
  • GC will coordinate with this thread at safepoints

Complexity: O(1) - atomic store + potential safepoint check

3.1.3 Kotlin_mm_safePointWhileLoopBody()

extern "C" void Kotlin_mm_safePointWhileLoopBody();

Purpose: Check if GC requires thread suspension.

Preconditions:

  • Thread should be in kRunnable state (no effect if kNative)

Postconditions:

  • If GC pending: thread pauses until GC completes
  • Otherwise: immediate return

Complexity: O(1) - atomic load, rare conditional pause

3.2 C++ API

3.2.1 KotlinNativeStateGuard

class KotlinNativeStateGuard {
public:
    KotlinNativeStateGuard();
    ~KotlinNativeStateGuard();
    
    // Non-copyable, non-movable
    KotlinNativeStateGuard(const KotlinNativeStateGuard&) = delete;
    KotlinNativeStateGuard& operator=(const KotlinNativeStateGuard&) = delete;
};

Purpose: RAII guard for automatic thread state management.

Behavior:

  • Constructor: Calls Kotlin_mm_switchThreadStateNative()
  • Destructor: Calls Kotlin_mm_switchThreadStateRunnable()

Thread Safety: Thread-local, no synchronization required.

Exception Safety: Strong guarantee - destructor always called.

3.2.2 check_safepoint()

inline void check_safepoint();

Purpose: Insert explicit safepoint check.

Use Cases:

  • Long-running loops (>1ms iteration time)
  • Reducing GC pause latency
  • Periodic coordination with GC

Performance:

  • Standalone: 0 cycles (inlined away)
  • With Kotlin Native: ~5-10 cycles (atomic load)

3.2.3 is_kotlin_native_runtime_available()

inline bool is_kotlin_native_runtime_available();

Purpose: Runtime detection of Kotlin Native presence.

Returns:

  • true: Functions resolve to Kotlin Native runtime
  • false: Functions are inlined stubs

Use Case: Conditional logic for dual-mode operation.

4. Build Configuration

4.1 CMake Options

option(KOTLIN_NATIVE_RUNTIME_AVAILABLE
    "Link with Kotlin Native runtime" OFF)

Values:

  • OFF (default): Standalone mode, inline stubs
  • ON: Kotlin Native mode, weak-linked functions

4.2 Compiler Definitions

#define KOTLIN_NATIVE_RUNTIME_AVAILABLE 0  // Standalone
#define KOTLIN_NATIVE_RUNTIME_AVAILABLE 1  // With Kotlin Native

4.3 Link Time Behavior

Standalone:

clang++ -DKOTLIN_NATIVE_RUNTIME_AVAILABLE=0 source.cpp
# Functions are inlined away
# Zero runtime overhead

With Kotlin Native:

clang++ -DKOTLIN_NATIVE_RUNTIME_AVAILABLE=1 source.cpp -c
kotlinc-native kotlin_code.kt -library source.o
# Weak symbols resolved by Kotlin Native runtime
# GC coordination active

5. Safety Guarantees

5.1 Thread State Constraints

Operation kRunnable kNative
Access Kotlin objects ✓ Safe ✗ Undefined Behavior
Call Kotlin functions ✓ Safe ✗ Undefined Behavior
Allocate Kotlin objects ✓ Safe ✗ Undefined Behavior
C++ operations ✓ Safe ✓ Safe
Heavy computation ⚠ Blocks GC ✓ Safe
Safepoint checks ✓ Active ✗ No effect

5.2 RAII Safety

KotlinNativeStateGuard provides:

  • Constructor throws: Not applicable (no failure modes)
  • Destructor throws: noexcept guarantee (C++ standard)
  • Exception safety: Strong - always restores state
  • Stack unwinding: Safe - destructor always invoked

5.3 Race Conditions

Thread-local state: Each thread has independent state.

  • No data races: State transitions are thread-local
  • No synchronization needed: Between state switches
  • GC coordination: Managed by Kotlin Native runtime

6. Performance Characteristics

6.1 Overhead Analysis

Operation Standalone With Kotlin Native
State switch 0 cycles ~10-50 cycles
Safepoint check 0 cycles ~5-10 cycles
Guard construction 0 cycles ~10-50 cycles
Guard destruction 0 cycles ~10-50 cycles

6.2 Memory Overhead

  • Code size: +0 bytes standalone (inlined away)
  • Data size: 0 bytes (no static state)
  • Stack size: sizeof(KotlinNativeStateGuard) = 1 byte (empty class)

6.3 Scalability

  • Thread scaling: O(1) - per-thread state
  • GC coordination: O(threads in kRunnable) - GC waits only for kRunnable threads
  • Safepoint overhead: O(1) - atomic load

7. Usage Patterns

7.1 Long-Running Operations

extern "C" void mlx_inference(int64_t model_ptr, int64_t input_ptr) {
    kotlinx::coroutines::KotlinNativeStateGuard guard;
    
    auto* model = reinterpret_cast<mlx::core::nn::Module*>(model_ptr);
    auto* input = reinterpret_cast<mlx::core::array*>(input_ptr);
    
    // Heavy computation - GC doesn't wait
    auto result = model->forward(*input);
    
    return reinterpret_cast<int64_t>(new mlx::core::array(result));
}

Analysis:

  • GC impact: Zero - thread in kNative during inference
  • Latency: Minimal - two state switches (~20-100 cycles)
  • Safety: Guaranteed by RAII guard

7.2 Mixed Kotlin/C++ Operations

extern "C" void process_with_callbacks(
    void (*progress_callback)(int),
    int iterations
) {
    kotlinx::coroutines::KotlinNativeStateGuard guard;
    
    for (int i = 0; i < iterations; i++) {
        // Heavy C++ work in kNative
        process_batch(i);
        
        // Need to call Kotlin? Switch back temporarily
        Kotlin_mm_switchThreadStateRunnable();
        progress_callback(i);
        Kotlin_mm_switchThreadStateNative();
    }
    
    // Guard destructor handles final state transition
}

Analysis:

  • GC coordination: Automatic during callbacks
  • Overhead: 2 state switches per iteration
  • Flexibility: Full control over state transitions

7.3 Interruptible Operations

extern "C" void long_computation(int items) {
    kotlinx::coroutines::KotlinNativeStateGuard guard;
    
    for (int i = 0; i < items; i++) {
        process_item(i);
        
        // Periodic safepoint for responsiveness
        if (i % 1000 == 0) {
            Kotlin_mm_switchThreadStateRunnable();
            kotlinx::coroutines::check_safepoint();
            Kotlin_mm_switchThreadStateNative();
        }
    }
}

Analysis:

  • GC latency: Bounded by safepoint frequency
  • Overhead: ~40-200 cycles per 1000 iterations
  • Interruptibility: Allows GC to run periodically

8. Testing

8.1 Test Suite

Located in tests/gc_bridge/:

Test Purpose Coverage
test_kotlin_gc_bridge.cpp Standalone mode validation Memory, threading, performance
test_kotlin_gc_bridge_impl.cpp Kotlin Native integration State transitions, GC coordination
test_kotlin_gc_bridge.kt Kotlin-side testing End-to-end integration

8.2 Validation Criteria

Standalone Mode:

  • ✓ Zero performance overhead
  • ✓ No memory leaks
  • ✓ Thread-safe operation
  • ✓ Functions inlined away

Kotlin Native Mode:

  • ✓ Correct state transitions
  • ✓ GC coordination works
  • ✓ No deadlocks
  • ✓ Callback safety

8.3 Performance Benchmarks

From tests/gc_bridge/test_kotlin_gc_bridge.cpp:

=== Standalone C++ ===
Test 1 (without guard): 153 ms
Test 2 (with guard):    153 ms  ← Same performance!
Test 3 (safepoints):    110 ms
Multi-threaded:          55 ms

Memory delta: 0 MB (no leaks)

Conclusion: Zero overhead validated empirically.

9. Integration Guide

9.1 Adding to Existing Projects

Step 1: Include Header

#include "kotlinx/coroutines/KotlinGCBridge.hpp"

Step 2: Wrap Long Operations

extern "C" void your_function() {
    kotlinx::coroutines::KotlinNativeStateGuard guard;
    // Your existing code here
}

Step 3: Build Configuration

# Standalone
target_compile_definitions(your_target PRIVATE
    KOTLIN_NATIVE_RUNTIME_AVAILABLE=0)

# With Kotlin Native
target_compile_definitions(your_target PRIVATE
    KOTLIN_NATIVE_RUNTIME_AVAILABLE=1)

9.2 Kotlin Native Integration

Define C++ Functions

extern "C" void long_operation() {
    kotlinx::coroutines::KotlinNativeStateGuard guard;
    do_work();
}

Kotlin Bindings

@kotlin.native.internal.GCUnsafeCall("long_operation")
external fun longOperation()

Usage

fun main() {
    longOperation()  // C++ runs in kNative, zero GC latency
}

10. References

10.1 Source Files

  • src/kotlinx/coroutines/KotlinGCBridge.hpp - API implementation
  • tests/gc_bridge/ - Test suite
  • docs/SUSPEND_IMPLEMENTATION.md - Suspend implementation and compiler lowering notes

10.2 External References

  • Kotlin Native Runtime: tmp/kotlin/kotlin-native/runtime/src/main/cpp/Memory.h
  • Thread State Implementation: tmp/kotlin/kotlin-native/runtime/src/mm/cpp/ThreadState.hpp
  • GC Implementation: tmp/kotlin/kotlin-native/runtime/src/gc/

10.3 Related Specifications

11. Revision History

Version Date Changes
1.0.0 2024-12-10 Initial specification and reference implementation

Document Status: Approved for Production Use
Implementation Status: Complete and Tested
Maintenance: Active