This specification is maintained by the AgentRules Working Group. Have enhancements or want to claim maintainership?Submit Specification →
mobilecritical PriorityID: kmp-native-coroutine-memory-arena-isolation

Kotlin Multiplatform 2.1 Native Coroutine Lifecycle and Memory Arena Isolation

Enforces strict boundary isolation between Swift Concurrency tasks and Kotlin Native coroutine execution in KMP 2.1. Prevents unmanaged memory pinning, native arena escapes across suspension points, and thread starvation on Apple GCD runtimes through explicit structured cancellation propagation.

Prohibited Anti-Pattern: Unscoped Swift-Kotlin Continuation Bridging and Cross-Suspension Native Arena Pinning

Export Ready-to-Drop Configuration

Select format below to copy code or download file directly.

---
description: Kotlin Multiplatform 2.1 Native Coroutine Lifecycle and Memory Arena Isolation - Enforces strict boundary isolation between Swift Concurrency tasks and Kotlin Native coroutine execution in KMP 2.1. Prevents unmanaged memory pinning, native arena escapes across suspension points, and thread starvation on Apple GCD runtimes through explicit structured cancellation propagation.
globs: **/commonMain/**/*.kt, **/iosMain/**/*.kt, **/appleMain/**/*.kt, **/nativeMain/**/*.kt
alwaysApply: true
verified_by: "agentrules.org"
license: "CC-BY-4.0"
source: "https://agentrules.org/rules/kmp-native-coroutine-memory-arena-isolation"
---

# Kotlin Multiplatform 2.1 Native Coroutine Lifecycle and Memory Arena Isolation

> **Severity**: CRITICAL | **Category**: mobile
> **Anti-Pattern Warning**: Unscoped Swift-Kotlin Continuation Bridging and Cross-Suspension Native Arena Pinning

## System Constraints
You are a Principal Mobile Architect enforcing Kotlin Multiplatform 2.1 Native Coroutine Lifecycle and Memory Arena Isolation standards. You must eliminate all uncollected native continuation wrappers, prohibit native memory allocations (e.g., memScoped, Arena) across coroutine suspension points, and enforce bidirectional cooperative cancellation between Swift Tasks and Kotlin CoroutineScope instances. Never allow GlobalScope or Dispatchers.Unconfined in platform bridge layers.

## Strict Guidelines
- Engineers MUST ALWAYS scope Kotlin coroutines executing in Native environments to an explicit, cancellable lifecycle (e.g., through SKIE or custom Swift Task-tied CoroutineScope) and cancel the root Job when the platform container deallocates.
- Engineers MUST NOT allow native memory allocations via memScoped, Arena, or nativeHeap to span suspension points, as native arenas become invalid or pin physical pages across thread-hop resumptions.
- Engineers MUST convert native C-pointers and Objective-C object references into immutable Kotlin value types before crossing suspend function boundaries.
- Bridge code SHOULD ALWAYS utilize suspendCancellableCoroutine instead of suspendCoroutine to register Swift Task cancellation handlers that invoke Job.cancel() on the Kotlin side.
- Background workers MUST NEVER use Dispatchers.Main or Dispatchers.Unconfined for blocking foreign runtime bridge calls; IO or dedicated bounded thread pools must be used to prevent GCD runloop starvation.
- Platform components NEVER retain direct strong references to Swift UI controllers or Android Contexts inside long-lived shared state holders or coroutine contexts.

## Bad vs Good Implementations
### Native Memory Arena Across Coroutine Suspension Point and Unscoped Continuation

#### Bad Antipattern (AI Common Hallucination)
> **Why it fails**: GlobalScope causes leaks when the iOS caller cancels its Swift Task. Wrapping an async call inside `memScoped` causes undefined behavior: coroutine suspension unbinds the native execution frame, leading to native memory corruption, arena invalidation, and dangling pointers upon coroutine resumption on a different GCD pthread.
```kotlin
import kotlinx.cinterop.*
import kotlinx.coroutines.*

class NativeDataProcessor {
    // BAD: GlobalScope bypasses Swift Task lifecycle, leaks continuation
    // BAD: memScoped crosses a suspension point, pinning native memory or causing SIGSEGV upon resume
    @OptIn(ExperimentalForeignApi::class)
    fun processNativePayload(bytes: ByteArray): Job = GlobalScope.launch(Dispatchers.Default) {
        memScoped {
            val nativeBuffer = allocArray<ByteVar>(bytes.size)
            bytes.forEachIndexed { index, byte -> nativeBuffer[index] = byte }
            
            // Suspend call inside memScoped pins or corrupts native frame on resumption
            val transformed = executeAsyncRemoteTransform(nativeBuffer.rawValue)
            
            // Undefined behavior: nativeBuffer memory is unpinned or reallocated across dispatch
            persistNativeData(nativeBuffer)
        }
    }

    private suspend fun executeAsyncRemoteTransform(ptr: NativePtr): Int {
        delay(100) // Suspension hops pthreads
        return 42
    }
    
    private external fun persistNativeData(ptr: CPointer<ByteVar>)
}
```

#### Production Standard (Correct)
> **Standard**: Native memory allocations are strictly confined to synchronous `memScoped` blocks that complete before and after coroutine suspension points. Kotlin managed types (`ByteArray`) safely bridge across suspensions, and the coroutine executes under a structured `CoroutineScope` that propagates cancellation cleanly to and from Swift.
```kotlin
import kotlinx.cinterop.*
import kotlinx.coroutines.*
import kotlin.coroutines.CoroutineContext

class NativeDataProcessor(private val scope: CoroutineScope) {
    // GOOD: Coroutine is bound to caller lifecycle and structured cancellation
    @OptIn(ExperimentalForeignApi::class)
    suspend fun processNativePayload(bytes: ByteArray): Result<Unit> = withContext(Dispatchers.Default) {
        runCatching {
            // 1. Process native data strictly within synchronous memory boundaries
            val processedPayload: ByteArray = memScoped {
                val nativeBuffer = allocArray<ByteVar>(bytes.size)
                bytes.forEachIndexed { index, byte -> nativeBuffer[index] = byte }
                // Synchronous native processing ONLY
                nativeBuffer.readBytes(bytes.size)
            }
            
            // 2. Suspend safely using decoupled Kotlin managed primitives
            val transformToken = executeAsyncRemoteTransform(processedPayload)
            
            // 3. Re-enter isolated native arena for persistence post-suspension
            memScoped {
                val writeBuffer = allocArray<ByteVar>(processedPayload.size)
                processedPayload.forEachIndexed { index, byte -> writeBuffer[index] = byte }
                persistNativeData(writeBuffer, transformToken)
            }
        }
    }

    private suspend fun executeAsyncRemoteTransform(data: ByteArray): Int {
        return withContext(Dispatchers.IO) {
            delay(100)
            42
        }
    }
    
    private external fun persistNativeData(ptr: CPointer<ByteVar>, token: Int)
}
```

Architectural Specification & Invariant Rationale

Architectural Invariant Design: Kotlin Multiplatform 2.1 Native Coroutine Lifecycle and Memory Arena Isolation

Kotlin Multiplatform 2.1 Native Coroutine Lifecycle and Memory Arena Isolation defines an industrial-grade behavioral standard for mobile engineering. Enforces strict boundary isolation between Swift Concurrency tasks and Kotlin Native coroutine execution in KMP 2.1. Prevents unmanaged memory pinning, native arena escapes across suspension points, and thread starvation on Apple GCD runtimes through explicit structured cancellation propagation. Modern software architectures require strict adherence to deterministic patterns to guarantee high availability, maintainability, and zero security vulnerabilities.

Framework Paradigm Shift & Drift MechanicsLLMs are trained on millions of open-source repositories containing outdated code, legacy idioms, and unpatched antipatterns. When coding assistants generate implementations for this stack, they default to common statistical tokens: Unscoped Swift-Kotlin Continuation Bridging and Cross-Suspension Native Arena Pinning Without an explicit system instruction file, developers spend hours debugging silent failures.
Failure Modes & Production Blast Radius

Hazard Analysis: What Breaks Without This Rule

The following failure modes have been cataloged from hundreds of hours of production code reviews where AI assistants produced non-compliant implementations:

Failure ModeAI Symptom (The Anti-Pattern)SeverityProduction Impact & Remedy
AI Hallucination & Deprecation DriftUnscoped Swift-Kotlin Continuation Bridging and Cross-Suspension Native Arena PinningCritical
Violating this industrial standard introduces regression bugs, security risks, and unexpected runtime failures in production.
Enforce explicit behavioral system directives across all AI coding assistants in your repository.
Silent Architecture InconsistencyAI assistants produce mixed paradigms across files, creating fragmented codebase conventions.Medium
Increases cognitive load during code reviews and leads to maintenance debt across engineering teams.
Pin project conventions in .cursor/rules, CLAUDE.md, and AGENTS.md so all team agents follow identical guardrails.
Architectural Directives in Detail

Step-by-Step Production Directives

Each rule constraint below is injected into the AI agent's system prompt to establish deterministic boundaries:

1

Engineers MUST ALWAYS scope Kotlin coroutines executing in Native environments to an explicit, cancellable lifecycle (e.g., through SKIE or custom Swift Task-tied CoroutineScope) and cancel the root Job when the platform container deallocates.

Engineers MUST ALWAYS scope Kotlin coroutines executing in Native environments to an explicit, cancellable lifecycle (e.g., through SKIE or custom Swift Task-tied CoroutineScope) and cancel the root Job when the platform container deallocates.

Rationale:AI models frequently generate unconstrained code here because statistical language distributions lack project-specific architectural context. Mandating this directive ensures that generated code adheres to verified industrial production requirements.
Enforcement:Integrated into the Agent Pack rule file. When Cursor, Claude Code, or Codex processes this prompt, it applies this constraint before emitting syntax.
2

Engineers MUST NOT allow native memory allocations via memScoped, Arena, or nativeHeap to span suspension points, as native arenas become invalid or pin physical pages across thread-hop resumptions.

Engineers MUST NOT allow native memory allocations via memScoped, Arena, or nativeHeap to span suspension points, as native arenas become invalid or pin physical pages across thread-hop resumptions.

Rationale:AI models frequently generate unconstrained code here because statistical language distributions lack project-specific architectural context. Mandating this directive ensures that generated code adheres to verified industrial production requirements.
Enforcement:Integrated into the Agent Pack rule file. When Cursor, Claude Code, or Codex processes this prompt, it applies this constraint before emitting syntax.
3

Engineers MUST convert native C-pointers and Objective-C object references into immutable Kotlin value types before crossing suspend function boundaries.

Engineers MUST convert native C-pointers and Objective-C object references into immutable Kotlin value types before crossing suspend function boundaries.

Rationale:AI models frequently generate unconstrained code here because statistical language distributions lack project-specific architectural context. Mandating this directive ensures that generated code adheres to verified industrial production requirements.
Enforcement:Integrated into the Agent Pack rule file. When Cursor, Claude Code, or Codex processes this prompt, it applies this constraint before emitting syntax.
4

Bridge code SHOULD ALWAYS utilize suspendCancellableCoroutine instead of suspendCoroutine to register Swift Task cancellation handlers that invoke Job.cancel() on the Kotlin side.

Bridge code SHOULD ALWAYS utilize suspendCancellableCoroutine instead of suspendCoroutine to register Swift Task cancellation handlers that invoke Job.cancel() on the Kotlin side.

Rationale:AI models frequently generate unconstrained code here because statistical language distributions lack project-specific architectural context. Mandating this directive ensures that generated code adheres to verified industrial production requirements.
Enforcement:Integrated into the Agent Pack rule file. When Cursor, Claude Code, or Codex processes this prompt, it applies this constraint before emitting syntax.
5

Background workers MUST NEVER use Dispatchers.Main or Dispatchers.Unconfined for blocking foreign runtime bridge calls

Background workers MUST NEVER use Dispatchers.Main or Dispatchers.Unconfined for blocking foreign runtime bridge calls; IO or dedicated bounded thread pools must be used to prevent GCD runloop starvation.

Rationale:AI models frequently generate unconstrained code here because statistical language distributions lack project-specific architectural context. Mandating this directive ensures that generated code adheres to verified industrial production requirements.
Enforcement:Integrated into the Agent Pack rule file. When Cursor, Claude Code, or Codex processes this prompt, it applies this constraint before emitting syntax.
6

Platform components NEVER retain direct strong references to Swift UI controllers or Android Contexts inside long-lived shared state holders or coroutine contexts.

Platform components NEVER retain direct strong references to Swift UI controllers or Android Contexts inside long-lived shared state holders or coroutine contexts.

Rationale:AI models frequently generate unconstrained code here because statistical language distributions lack project-specific architectural context. Mandating this directive ensures that generated code adheres to verified industrial production requirements.
Enforcement:Integrated into the Agent Pack rule file. When Cursor, Claude Code, or Codex processes this prompt, it applies this constraint before emitting syntax.
Technical Questions & Answers

Frequently Asked Technical Questions

Common questions developers face when configuring AI coding assistants for this stack:

Q:Why is Kotlin Multiplatform 2.1 Native Coroutine Lifecycle and Memory Arena Isolation critical for AI coding assistants?

Enforces strict boundary isolation between Swift Concurrency tasks and Kotlin Native coroutine execution in KMP 2.1. Prevents unmanaged memory pinning, native arena escapes across suspension points, and thread starvation on Apple GCD runtimes through explicit structured cancellation propagation. Without explicit behavioral boundaries, AI coding assistants (Cursor, Claude Code, Windsurf, Codex) rely on training data averages, which frequently produce deprecated, inefficient, or vulnerable code patterns.

Q:How does adopting this rule prevent AI hallucinations?

This rule explicitly declares what the AI must avoid (Unscoped Swift-Kotlin Continuation Bridging and Cross-Suspension Native Arena Pinning) and prescribes verified positive examples. Grounding the LLM with negative and positive pairs eliminates guesswork.

Q:Which AI coding environments are supported?

This specification is multi-platform compatible: Cursor (.cursor/rules/*.mdc & .cursorrules), Anthropic Claude Code (CLAUDE.md), Windsurf (.windsurfrules), Codex / Cline (AGENTS.md), and GitHub Copilot (.github/copilot-instructions.md).

Q:Can I combine this rule with other technology stacks?

Yes. Use the Interactive Stack Builder or download an Agent Pack to merge this rule with other frontend, backend, database, security, and DevOps rules without syntax collision.

Open Specification · CC BY 4.0·Free for Personal, Team & Commercial In-Repo Use

Citation & Open Source Attribution Guidelines

This specification is licensed under Creative Commons Attribution 4.0 International (CC BY 4.0). You are completely free to adapt and share this rule in your workspace. If citing, including in public AI benchmark datasets, or redistributing, attribution to AgentRules.org is required.

Related MOBILE Rules

View all rules →
1 Rule in Stack

Kotlin