Concurrency Contract for State Ownership in BitChat: Three-Domain Architecture Explained

BitChat enforces a strict three-domain concurrency contract that isolates mutable state to specific serial queues—engine-confined, BLE queue-confined, and lock-backed stores—guaranteeing deadlock freedom through a unidirectional synchronization edge order.

This guide examines the thread-safety architecture in the permissionlesstech/bitchat repository, a Bluetooth Low Energy (BLE) mesh messaging framework written in Swift. The concurrency contract for state ownership in BitChat organizes all mutable state into distinct isolation domains, preventing data races while maintaining high throughput across the mesh network stack.

The Three Concurrency Domains

BitChat's BLE transport layer divides mutable state into three distinct concurrency domains, each governed by specific ownership rules that eliminate contention.

Engine-Confined State

The engine-confined domain restricts mutable access to the serial engine queue (mesh.message). All cross-thread mutations must use the onEngine helper to hop onto this queue. This domain protects core mesh engine state including the codec, fragmentation logic, deduplication tables, relay policies, peer registry, topology management, and Noise protocol orchestration.

BLE Queue-Confined State

The bleQueue-confined domain houses state adjacent to CoreBluetooth objects, including the link store, write buffers, notification buffers, and link-authentication maps. Access is strictly limited to the BLE queue, ensuring that peripheral write operations and link-layer authentication data remain consistent with CoreBluetooth threading requirements.

Lock-Backed Global Stores

The lock-backed store domain allows read access from any thread through a read-write lock, though writes remain confined to a single domain (either engine or BLE queue). This pattern protects global stores—such as BLEPeerRegistryStore, local identity/capabilities, and traffic monitors—that require visibility across multiple domains while guaranteeing readers never observe torn data.

Synchronization Edge Order and Deadlock Prevention

BitChat enforces a unidirectional synchronization edge order that guarantees deadlock freedom. The hierarchy flows as:


main / test threads ──sync──▶ engine ──sync──▶ bleQueue
                                   └──sync──▶ noise / identity queues (leaves)

Critical enforcement occurs in the onEngine helper: no code may synchronously wait in the reverse direction. The BLE queue and cryptographic queues may only async-dispatch to the engine, and the engine never synchronously dispatches back to the main thread. This construction eliminates circular wait conditions entirely.

Contract Enforcement in BLEService.swift

The concurrency contract is implemented and debug-checked in bitchat/Services/BLE/BLEService.swift. The onEngine method (around line 396) contains assertions verifying that only the engine queue may call into the BLE queue and vice-versa. According to the BLE Architecture V3 design document (docs/BLE-ARCHITECTURE-V3.md, lines 54-66), these runtime checks validate the sync-edge order during development, while the test suite in bitchatTests/Services/BLEQueueContractTests.swift greps for illegal synchronous dispatches to prevent regressions.

Practical Implementation Examples

The following patterns demonstrate safe cross-domain state access in BitChat.

Safely mutating engine-confined state:

// Closure executes exclusively on the serial engine queue
BLEService.shared.onEngine {
    meshEngine.enqueue(event: .incomingMessage(payload))
    // Safe to mutate: codec, fragmentation, deduplication, relay policy
}

Reading from lock-backed stores:

// Read from any thread via read-write lock
let peerCount = BLEPeerRegistryStore.shared.withReadLock { 
    $0.allPeers.count 
}

Dispatching to the BLE queue from the engine:

BLEService.shared.onEngine {
    // Engine-scheduled work requiring BLE layer access
    let pending = BLEPendingWrite(data: encryptedPayload)
    bleQueue.enqueue(pending)  // Safe: maintains queue confinement
}

Summary

  • Three-domain isolation separates state into engine-confined, BLE queue-confined, and lock-backed stores, each with explicit ownership rules.
  • Unidirectional sync edges flow from main/test threads to engine to BLE queue (and engine to crypto queues), with strict prohibition on reverse synchronous waits.
  • Runtime enforcement via onEngine assertions in BLEService.swift and automated contract testing prevents deadlock and data races.
  • Lock-backed patterns enable safe cross-domain visibility for global stores like BLEPeerRegistryStore without compromising the serial queue model.

Frequently Asked Questions

What happens if code attempts to synchronously dispatch against the grain of the sync-edge order?

The onEngine helper contains debug assertions that trap illegal synchronous dispatches. In production builds, violating the unidirectional flow (such as having the BLE queue block-wait for the engine) would create a deadlock risk, which is why the architecture mandates strict async callbacks for reverse-direction communication.

Why does BitChat use a read-write lock for the peer registry instead of queue confinement?

The peer registry requires visibility from both the engine domain and the BLE domain while supporting high-concurrency reads. The read-write lock pattern allows any thread to read consistent state without queue hopping overhead, while write operations remain serialized through either the engine or BLE queue owner, ensuring writers cannot interfere with each other.

How does the onEngine helper prevent accidental thread migration?

In BLEService.swift, the onEngine implementation checks the current execution context and asserts that callers from the BLE queue or crypto queues do not attempt synchronous dispatches back to the engine. This validation ensures that state access respects the domain boundaries defined in the BLE Architecture V3 specification.

Where is the concurrency contract documented and tested?

The formal contract resides in docs/BLE-ARCHITECTURE-V3.md (lines 54-66), while enforcement logic lives in bitchat/Services/BLE/BLEService.swift. The test suite bitchatTests/Services/BLEQueueContractTests.swift provides automated validation by scanning for prohibited synchronous dispatch patterns across the codebase.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →