Skip to content

⚠️ Deprecated

This RFC has been superseded by RFC-024: New Concurrency Model.

RFC-001's three-layer concurrency architecture (L1/L2/L3), @block/@eager annotations, and DAG automatic analysis have been removed. The new design uses spawn {} blocks as the sole parallelism primitive, requiring no annotations.

This document is retained for historical reference only.



title: "RFC-001: Concurrent Spawn Model and Error Handling System"

RFC-001: Concurrent Spawn Model and Error Handling System

Design Sources

DocumentRelationship
async-whitepaperDesign origin, theoretical basis
language-specSpecification target

Abstract

Proposes YaoXiang's concurrent spawn model: describe logic in synchronous syntax, automatically execute concurrently at runtime. Core mechanism: three-layer concurrency architecture + DAG dependency analysis + Result type system.

Quick Reference

ScenarioSyntaxDescription
Auto-parallelNo annotation (default)Maximize parallelism
Sync wait@eagerWait for dependencies
Fully sequential@blockNo concurrency, for debugging
Local concurrencyspawnConcurrency within @block scope

Motivation

Current mainstream language concurrency models have obvious flaws:

LanguageConcurrency ModelProblems
Rustasync/await + tokioAsync contagion, steep learning curve
GogoroutineNo type safety
PythonasyncioGIL limitations
JavaScriptPromise/asyncComplex callbacks

Core Contradictions

  1. Transparency vs Controllability: Fully transparent but uncontrollable vs fully controllable but opaque
  2. Concurrency vs Debuggability: Concurrent programs are hard to debug vs debuggable programs are hard to parallelize

Proposal

1. Concurrent Spawn Model: Three-Layer Concurrency Architecture

Note: L1/L2/L3 are mental models to help users understand different scenarios. The actual implementation has only one mechanism: automatic DAG analysis + annotation control.

LayerMental ModelSyntaxExecution ModeParallelism
L1Concurrency forbidden@blockPure sequential execution❌ None
L2Concurrency within @blockspawnControlled concurrency within @block scope⚠️ Partial
L3Full concurrencyDefault (no annotation)Automatic DAG analysis✅ Full

L1: @block Synchronous Mode

yaoxiang
main: () -> Void @block = {
    data1 = fetch_sync("api1")
    data2 = fetch_sync("api2")
    process(data1, data2)    # Strict order, no concurrency
}

L2: Controlled Concurrency Within @block

yaoxiang
# spawn can only be used inside @block functions
main: () -> Void @block = {
    spawn { data1 = fetch_data("api1") }
    spawn { data2 = fetch_data("api2") }
    # Wait for all spawns to complete (stdlib control)
    process(data1, data2)
}

L3: Fully Transparent (Default)

yaoxiang
# No annotations needed, compiler automatically analyzes DAG
heavy_calc: (n: Int) -> Int = fibonacci(n)

auto_parallel: (n: Int) -> Int = {
    a = heavy_calc(1)    # Auto-parallel
    b = heavy_calc(2)    # Auto-parallel
    c = heavy_calc(3)    # Auto-parallel
    a + b + c            # Wait for all results when values are needed
}

2. Annotation Complete Comparison

DimensionDefault (no annotation)@eager@blockspawn
ExecutionAutomatic DAG analysisSync wait for depsPure sequentialConcurrency within @block
Parallelism✅ Full⚠️ By dependency order❌ None⚠️ Partial
DAG Building

Selection Guide:

  • Maximum concurrency → No annotation (default)
  • Need ordered side effects → @eager
  • Debugging/newcomers/critical code → @block
  • Need concurrency within @block → spawn
yaoxiang
# Default: maximize parallelism
calc_all: () -> Int = {
    a = heavy_calc(1)    # Auto-parallel
    b = heavy_calc(2)    # Auto-parallel
    a + b
}

# @eager: sync wait
calc_seq: () -> Int @eager = {
    a = heavy_calc(1)    # Sync execution
    b = heavy_calc(2)    # Sync execution
    a + b
}

# @block: pure sequential
calc_simple: () -> Int @block = {
    a = heavy_calc(1)    # Force sync
    b = heavy_calc(2)    # Sync
    a + b
}

# spawn: concurrency within @block
calc_mixed: () -> Int @block = {
    spawn { heavy_calc(1) }
    spawn { heavy_calc(2) }
    heavy_calc(3)        # Sync
}

3. DAG Dependency Analysis

3.1 Core Principle: Bottom-Up Execution

User code (sync syntax):
    a = fetch(url0)
    b = fetch(url1)
    print(a)

Compile-time analysis (bottom-up):
    print(a) needs a → depends on fetch(url0)
    fetch(url1) nobody needs → isolated DAG

Runtime scheduling (from leaves):
    fetch(url0) → print(a)    ← dependency chain, sequential
    fetch(url1)                ← isolated, parallel independently

Key Insight: Not "top-down" generating Futures, but "bottom-up" reverse-analyzing dependencies from results.

3.2 Isolated DAG: Independent Parallelism

Main flow: fetch(url0) → process → print
Isolated:  fetch(url1)  ← nobody needs result, parallel independently

Scheduler: main flow executes by dependency chain, isolated uses another core in parallel

3.3 Resource Types and Side Effects

Core Idea: Resource operations are marked through types, DAG is automatically constructed. Same resource is automatically serialized, different resources are automatically parallelized.

Resource Type Boundaries — Clearly Defined:

Resource types are compiler-built-in marker types. The following types are recognized by the compiler as resources:

Resource TypeDescriptionCompiler Behavior
FilePathFilesystem pathSame-path operations auto-serialized
HttpUrlHTTP endpointSame URL operations auto-serialized
DBUrlDatabase connectionSame connection operations auto-serialized
ConsoleStandard outputAll Console operations auto-serialized

User-defined resource types need explicit marking:

yaoxiang
Database: Resource              # Explicitly marked as resource type
query: (Database, String) -> Result(Row, Error)
# Parameter Database is Resource, auto-recognized as resource operation

Types not marked as Resource are not tracked by the compiler for resource dependencies.

Usage Rules:

  • Pass resource handles through variables, DAG automatically manages order
  • Using literals directly on the same resource is a user design issue, not a language responsibility
yaoxiang
# ✅ Correct: variable passing, DAG auto-serializes
filename: String = "data.txt"
File.write(filename, x)
File.write(filename, y)    # DAG serialized

# ⚠️ User responsibility: literals
File.write("data.txt", x)
File.write("data.txt", y)  # May be parallel, user is responsible

3.4 Infinite Loop Handling

1 loop → Direct sync execution, zero scheduling overhead
Multiple loops → Scheduler slices and switches, true concurrency

4. Result Type and Error Handling

yaoxiang
Result: (T: Type, E: Type) -> Type = { ok: (T) -> Self, err: (E) -> Self }

# ? operator transparently propagates
process: () -> Result(Data, Error) = {
    data = fetch_data()?
    processed = transform(data)?
    save(processed)?
}

5. DAG Node Design

rust
enum NodeKind {
    Task,      // Task node
    Value,     // Value node
    Control,   // Control flow node
}

struct Node {
    id: NodeId,
    kind: NodeKind,
    inputs: Vec<ValueNodeId>,   // Input dependencies
    outputs: Vec<ValueNodeId>,  // Output values
    span: Span,                 // Source location
}
Edge TypeSymbolSemantics
DataEdgeData dependency (value flow)
ControlEdgeControl dependency (sequential execution)
SpawnEdgeConcurrency entry (parallelizable start)

6. Type System

Send → Safe to transfer across threads
Sync → Safe to share across threads
Arc(T) implements Send + Sync (thread-safe reference counting)

Tradeoffs

Advantages

  1. Progressive Adoption: Three-layer model adapts to different skill levels
  2. Natural Syntax: Sync code gains parallel performance
  3. Compile-time Safety: Send/Sync constraints eliminate data races
  4. Debuggable: Error graphs provide clear error propagation views

Disadvantages

  1. Learning Curve: Need to understand DAG dependency concept
  2. Compile Time: Whole-program DAG analysis may be slow
  3. Toolchain Complexity: Need brand new debugging and visualization tools

Alternative Approaches

ApproachWhy Not Chosen
Only explicit async/awaitCannot achieve transparent concurrency
Only fully transparent concurrencyUsers lose control
Go-style goroutineNo type safety, cannot compile-time check
L1 mode onlyAbandon core value of concurrent spawn model

Implementation Strategy

Phase Breakdown

  1. Phase 1 (v0.1): @block sync mode, basic types
  2. Phase 2 (v0.2): FlowScheduler scheduler
  3. Phase 3 (v0.3): spawn blocks, explicit concurrency
  4. Phase 4 (v0.5): L3 fully transparent, DAG automatic analysis
  5. Phase 5 (v0.6): Error graphs, graph debugger
  6. Phase 6 (v1.0): Production-ready optimizations

Dependencies

  • RFC-001 has no external dependencies (core foundation)
  • RFC-008 (Runtime Concurrency Model) → Design complete
  • RFC-011 (Generics System) → Design complete

Risks

  1. DAG Analysis Performance: Whole-program analysis may be O(n²), needs optimization
  2. Toolchain Missing: Debugger needs to be developed from scratch
  3. User Acceptance: Transparent concurrency needs good documentation

Design Decision Log

DecisionDecisionDate
Three-layer concurrencyL1/L2/L3 progressive2025-01-05
@block annotation positionAfter return type2025-01-05
DAG error propagationPropagate up along dependency edges2025-01-06
DAG performanceIncremental construction + caching2025-01-06
Runtime selectionGenerics + compile-time injection2025-01-06
Node interfaceGenerics + function injection (no trait)2025-01-06
Error graph memoryDAG only built within single function2025-01-06
Resource conflict detectionDAG data flow dependency, user variable passing2025-01-06
Resource type systemResource marker + DAG auto-dependency2026-01-06
L1/L2/L3 mental modelThree-layer abstraction, not implementation mechanism2026-01-06
@auto annotationRemoved, duplicates default behavior2026-05-11
L1 auto-fallbackRemoved, behavior unpredictable2026-05-11

Appendix: Glossary

TermDefinition
Concurrent spawn modelYaoXiang's concurrency paradigm: sync syntax, async nature
DAGDirected Acyclic Graph, describes computation dependencies
spawnControlled concurrency within @block scope
@blockSync annotation, disables concurrency optimization
@eagerEager evaluation, waits for dependencies to complete
ResourceResource type marker, operations auto-build DAG dependencies
Error graphVisualized error propagation path

References