This specification is maintained by the AgentRules Working Group. Have enhancements or want to claim maintainership?Submit Specification →
mobilecritical PriorityID: kmp-skia-surface-destruction-synchronization

KMP Skia Native Render Surface Lifecycle Synchronization

Enforces synchronous teardown of Skia hardware render targets during Android SurfaceHolder lifecycle and iOS CAMetalLayer view controller teardown. Asynchronous deallocation permits the native OS compositor to destroy backing native windows while Skia render passes are still in flight, triggering headless graphics driver panics and SIGSEGV memory faults.

Prohibited Anti-Pattern: Asynchronous or Unsynchronized Native GPU Render-Surface Teardown in Multiplatform Bindings

Export Ready-to-Drop Configuration

Select format below to copy code or download file directly.

---
description: KMP Skia Native Render Surface Lifecycle Synchronization - Enforces synchronous teardown of Skia hardware render targets during Android SurfaceHolder lifecycle and iOS CAMetalLayer view controller teardown. Asynchronous deallocation permits the native OS compositor to destroy backing native windows while Skia render passes are still in flight, triggering headless graphics driver panics and SIGSEGV memory faults.
globs: **/src/androidMain/**/*.kt, **/src/iosMain/**/*.kt, **/src/commonMain/**/render/*.kt
alwaysApply: true
verified_by: "agentrules.org"
license: "CC-BY-4.0"
source: "https://agentrules.org/rules/kmp-skia-surface-destruction-synchronization"
---

# KMP Skia Native Render Surface Lifecycle Synchronization

> **Severity**: CRITICAL | **Category**: mobile
> **Anti-Pattern Warning**: Asynchronous or Unsynchronized Native GPU Render-Surface Teardown in Multiplatform Bindings

## System Constraints
You are a Principal Mobile Systems Architect enforcing determinism in Kotlin Multiplatform (KMP) Compose/Skia rendering layers. You must enforce that platform render surfaces (ANativeWindow/SurfaceHolder on Android, CAMetalLayer/UIViewController on iOS) synchronously block surface destruction until pending Skia GrDirectContext GPU commands finish and render targets are unregistered.

## Strict Guidelines
- Engineers MUST ALWAYS synchronously flush, submit, and close the Skia GrDirectContext/Surface inside the platform destruction callback (e.g., SurfaceHolder.Callback.surfaceDestroyed or UIViewController.viewWillDisappear/deinit) before returning control to the OS window manager.
- Engineers MUST NOT rely on Kotlin garbage collection, CoroutineScope cancellation, or dispatching to CoroutineDispatchers.Default to release Skia Surfaces or native ANativeWindow handles.
- Platform bridges MUST hold an explicit reentrant lock or mutex between the rendering thread draw invocation and the lifecycle surface destruction thread.
- Code MUST unbind the Skia BackendRenderTarget immediately after context flushing to invalidate any pending draw commands prior to native window release.
- Native rendering components NEVER allow an ongoing render frame to execute draw calls once the OS has initiated surface destruction signals.

## Bad vs Good Implementations
### Android Surface Destruction Synchronization Guardrail

#### Bad Antipattern (AI Common Hallucination)
> **Why it fails**: surfaceDestroyed returns immediately without blocking. When the Android framework destroys the native window handle, Skia's background thread submits commands to a dead hardware buffer, crashing with fatal driver panics.
```kotlin
class SkiaSurfaceView(context: Context) : SurfaceView(context), SurfaceHolder.Callback {
    private val scope = CoroutineScope(Dispatchers.Default)
    private var skiaSurface: org.jetbrains.skia.Surface? = null

    override fun surfaceCreated(holder: SurfaceHolder) {
        scope.launch {
            skiaSurface = initSkiaSurface(holder.surface)
        }
    }

    override fun surfaceChanged(holder: SurfaceHolder, format: Int, width: Int, height: Int) {}

    override fun surfaceDestroyed(holder: SurfaceHolder) {
        // BUG: Asynchronous release allows the OS to invalidate ANativeWindow
        // before Skia completes in-flight GPU commands, causing a driver panic (SIGSEGV).
        scope.launch {
            skiaSurface?.close()
            skiaSurface = null
        }
    }
}
```

#### Production Standard (Correct)
> **Standard**: Acquires renderLock and synchronously calls grContext.flush(syncCpu = true) followed by surface.close(). The OS thread is blocked until the GPU finishes all operations on the native buffer, preventing headless driver panics.
```kotlin
class SkiaSurfaceView(context: Context) : SurfaceView(context), SurfaceHolder.Callback {
    private val renderLock = java.lang.Object()
    private var grContext: org.jetbrains.skia.DirectContext? = null
    private var skiaSurface: org.jetbrains.skia.Surface? = null

    override fun surfaceCreated(holder: SurfaceHolder) {
        synchronized(renderLock) {
            grContext = DirectContext.makeGL()
            skiaSurface = initSkiaSurface(holder.surface, grContext)
        }
    }

    override fun surfaceChanged(holder: SurfaceHolder, format: Int, width: Int, height: Int) {}

    override fun surfaceDestroyed(holder: SurfaceHolder) {
        // INVARIANT: Block the OS thread until GPU flush completes and surfaces are released.
        synchronized(renderLock) {
            grContext?.flush(syncCpu = true)
            skiaSurface?.close()
            skiaSurface = null
            grContext?.abandon()
            grContext?.close()
            grContext = null
        }
    }
}
```

Recommended MCP Servers (mcp-serverlist.com)

Equip agent with system tools to verify code for this stack.

Recommended Agent Skills (askills.dev)

Ready-to-use executable skill functions tailored to this stack.

Architectural Specification & Invariant Rationale

Architectural Invariant Design: KMP Skia Native Render Surface Lifecycle Synchronization

KMP Skia Native Render Surface Lifecycle Synchronization defines an industrial-grade behavioral standard for mobile engineering. Enforces synchronous teardown of Skia hardware render targets during Android SurfaceHolder lifecycle and iOS CAMetalLayer view controller teardown. Asynchronous deallocation permits the native OS compositor to destroy backing native windows while Skia render passes are still in flight, triggering headless graphics driver panics and SIGSEGV memory faults. 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: Asynchronous or Unsynchronized Native GPU Render-Surface Teardown in Multiplatform Bindings 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 DriftAsynchronous or Unsynchronized Native GPU Render-Surface Teardown in Multiplatform BindingsCritical
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 synchronously flush, submit, and close the Skia GrDirectContext/Surface inside the platform destruction callback (e.g., SurfaceHolder.Callback.surfaceDestroyed or UIViewController.viewWillDisappear/deinit) before returning control to the OS window manager.

Engineers MUST ALWAYS synchronously flush, submit, and close the Skia GrDirectContext/Surface inside the platform destruction callback (e.g., SurfaceHolder.Callback.surfaceDestroyed or UIViewController.viewWillDisappear/deinit) before returning control to the OS window manager.

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 rely on Kotlin garbage collection, CoroutineScope cancellation, or dispatching to CoroutineDispatchers.Default to release Skia Surfaces or native ANativeWindow handles.

Engineers MUST NOT rely on Kotlin garbage collection, CoroutineScope cancellation, or dispatching to CoroutineDispatchers.Default to release Skia Surfaces or native ANativeWindow handles.

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

Platform bridges MUST hold an explicit reentrant lock or mutex between the rendering thread draw invocation and the lifecycle surface destruction thread.

Platform bridges MUST hold an explicit reentrant lock or mutex between the rendering thread draw invocation and the lifecycle surface destruction thread.

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

Code MUST unbind the Skia BackendRenderTarget immediately after context flushing to invalidate any pending draw commands prior to native window release.

Code MUST unbind the Skia BackendRenderTarget immediately after context flushing to invalidate any pending draw commands prior to native window release.

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

Native rendering components NEVER allow an ongoing render frame to execute draw calls once the OS has initiated surface destruction signals.

Native rendering components NEVER allow an ongoing render frame to execute draw calls once the OS has initiated surface destruction signals.

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 KMP Skia Native Render Surface Lifecycle Synchronization critical for AI coding assistants?

Enforces synchronous teardown of Skia hardware render targets during Android SurfaceHolder lifecycle and iOS CAMetalLayer view controller teardown. Asynchronous deallocation permits the native OS compositor to destroy backing native windows while Skia render passes are still in flight, triggering headless graphics driver panics and SIGSEGV memory faults. 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 (Asynchronous or Unsynchronized Native GPU Render-Surface Teardown in Multiplatform Bindings) 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

KMP