Version: 1.0.0
Date: 2024-12-10
Status: Reference Implementation Complete
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.
- 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
This specification covers:
- Thread state management APIs
- RAII guard implementation
- Safepoint checking mechanisms
- Build configuration options
- Safety guarantees and constraints
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++ |
┌──────────────┐ switchThreadStateNative() ┌──────────────┐
│ │─────────────────────────────>│ │
│ kRunnable │ │ kNative │
│ │<─────────────────────────────│ │
└──────────────┘ switchThreadStateRunnable() └──────────────┘
Transition Costs:
- Standalone: 0 cycles (inlined away)
- With Kotlin Native: ~10-50 cycles (atomic operation + state check)
┌─────────────────────────────────────────┐
│ 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 │
└──────────────┘
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
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
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
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.
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)
inline bool is_kotlin_native_runtime_available();Purpose: Runtime detection of Kotlin Native presence.
Returns:
true: Functions resolve to Kotlin Native runtimefalse: Functions are inlined stubs
Use Case: Conditional logic for dual-mode operation.
option(KOTLIN_NATIVE_RUNTIME_AVAILABLE
"Link with Kotlin Native runtime" OFF)Values:
OFF(default): Standalone mode, inline stubsON: Kotlin Native mode, weak-linked functions
#define KOTLIN_NATIVE_RUNTIME_AVAILABLE 0 // Standalone
#define KOTLIN_NATIVE_RUNTIME_AVAILABLE 1 // With Kotlin NativeStandalone:
clang++ -DKOTLIN_NATIVE_RUNTIME_AVAILABLE=0 source.cpp
# Functions are inlined away
# Zero runtime overheadWith 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| 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 |
KotlinNativeStateGuard provides:
- Constructor throws: Not applicable (no failure modes)
- Destructor throws:
noexceptguarantee (C++ standard) - Exception safety: Strong - always restores state
- Stack unwinding: Safe - destructor always invoked
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
| 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 |
- Code size: +0 bytes standalone (inlined away)
- Data size: 0 bytes (no static state)
- Stack size: sizeof(KotlinNativeStateGuard) = 1 byte (empty class)
- 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
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
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
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
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 |
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
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.
#include "kotlinx/coroutines/KotlinGCBridge.hpp"extern "C" void your_function() {
kotlinx::coroutines::KotlinNativeStateGuard guard;
// Your existing code here
}# 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)extern "C" void long_operation() {
kotlinx::coroutines::KotlinNativeStateGuard guard;
do_work();
}@kotlin.native.internal.GCUnsafeCall("long_operation")
external fun longOperation()fun main() {
longOperation() // C++ runs in kNative, zero GC latency
}src/kotlinx/coroutines/KotlinGCBridge.hpp- API implementationtests/gc_bridge/- Test suitedocs/SUSPEND_IMPLEMENTATION.md- Suspend implementation and compiler lowering notes
- 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/
| 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