# How Promise.SetResult Handles Concurrent Resolution Attempts in Go

> Learn how Promise.SetResult in Go handles concurrent resolution attempts using atomic boolean swaps, ensuring only one goroutine succeeds and subsequent calls are no-ops.

- Repository: [Aperture Robotics/util](https://github.com/aperturerobotics/util)
- Tags: internals
- Published: 2026-02-25

---

**Promise.SetResult uses an atomic boolean swap to ensure exactly one goroutine succeeds when multiple threads attempt concurrent resolution, making all subsequent calls no-ops that return false.**

The `aperturerobotics/util` repository provides a thread-safe `Promise[T]` type for managing one-time asynchronous results. When multiple goroutines race to resolve the same promise, the `SetResult` method guarantees safe concurrent resolution without locks or panics. This design pattern is critical for coordination primitives where multiple producers might attempt to signal completion.

## The Atomic Resolution Mechanism

The concurrency safety of `SetResult` relies on a single `atomic.Bool` field named `isDone`. This flag coordinates the transition from *pending* to *resolved* using lock-free atomic operations.

### First-Caller-Wins Semantics

Inside [`promise/promise.go`](https://github.com/aperturerobotics/util/blob/main/promise/promise.go) (lines 43-53), the method uses `p.isDone.Swap(true)` to atomically attempt resolution:

```go
func (p *Promise[T]) SetResult(val T, err error) bool {
    // Atomically swap the flag from false → true.
    // If it was already true, another goroutine set the result first.
    if p.isDone.Swap(true) {
        return false        // resolution already happened
    }
    // First caller proceeds:
    p.result = &val
    p.err = err
    close(p.done)          // unblock any Await callers
    return true
}

```

The `Swap` operation returns the previous value. The first goroutine sees `false` (indicating "not done"), proceeds to store the result and close the channel, then returns `true`. Every subsequent caller sees `true`, immediately returns `false`, and leaves the promise state untouched.

### Memory Ordering Guarantees

The atomic swap establishes a **happens-before** relationship according to the Go memory model. This ensures that writes to `p.result`, `p.err`, and the `close(p.done)` operation are visible to any goroutine that subsequently observes the resolved state through `Await` or `AwaitWithErrCh`. Even on weakly-ordered architectures, consumers will see the complete resolution state, not partial writes.

### Panic-Free Channel Closing

Because only the winning goroutine reaches the `close(p.done)` line, the implementation eliminates the risk of double-close panics. The atomic flag acts as a gatekeeper, ensuring the channel is closed exactly once regardless of how many goroutines invoke `SetResult` simultaneously.

## Source Code Analysis

The implementation in [`promise/promise.go`](https://github.com/aperturerobotics/util/blob/main/promise/promise.go) demonstrates deliberate minimalism. By using `atomic.Bool` rather than a `sync.Mutex`, the method avoids contention and scheduler overhead during high-concurrency races. The `SetResult` signature returns a boolean to allow callers to detect whether they won the resolution race, enabling conditional logic for cleanup or logging.

## Concurrent Resolution in Practice

The following example demonstrates three workers racing to resolve the same promise. Only one succeeds, and the final result matches the winning worker's value:

```go
func ExampleConcurrentSetResult() {
    p := promise.NewPromise[int]()

    // Worker that tries to set the result after a random delay.
    worker := func(id int, val int) {
        time.Sleep(time.Duration(rand.Intn(100)) * time.Millisecond)
        if ok := p.SetResult(val, nil); ok {
            fmt.Printf("worker %d succeeded\n", id)
        } else {
            fmt.Printf("worker %d lost the race\n", id)
        }
    }

    go worker(1, 42)
    go worker(2, 99)
    go worker(3, 7)

    // Wait for the promise to be resolved.
    result, _ := p.Await(context.Background())
    fmt.Printf("final result: %d\n", result)
}

```

Typical output shows exactly one success:

```

worker 2 succeeded
worker 1 lost the race
worker 3 lost the race
final result: 99

```

## Integration with Background Workers

The [`promise/once.go`](https://github.com/aperturerobotics/util/blob/main/promise/once.go) file (lines 25-70) demonstrates production usage of this safety guarantee. Background goroutines spawned by the `Once` type call `SetResult` from multiple concurrent paths, relying on the atomic mechanism to coordinate the single permitted resolution. This pattern appears throughout the `aperturerobotics/util` codebase wherever lazy initialization or single-flight semantics are required.

## Summary

- **Atomic boolean swap** in [`promise/promise.go`](https://github.com/aperturerobotics/util/blob/main/promise/promise.go) ensures exactly one goroutine transitions the promise from pending to resolved.
- **Happens-before ordering** guarantees that the stored result and error are fully visible to all awaiting consumers.
- **Single channel close** prevents panics by ensuring `p.done` is closed only by the winning caller.
- **Boolean return value** allows callers to detect resolution success (`true`) versus no-op rejection (`false`).

## Frequently Asked Questions

### What happens if two goroutines call SetResult at the exact same time?

Only one goroutine succeeds. The first to execute `p.isDone.Swap(true)` receives `false` as the previous value, stores the result, and returns `true`. The second goroutine receives `true` from the swap, indicating the promise is already resolved, and returns `false` without modifying state.

### Does Promise.SetResult use locks or mutexes?

No, the implementation is lock-free. It uses `atomic.Bool` operations to coordinate concurrent attempts, avoiding the scheduler overhead and contention associated with `sync.Mutex`. This makes `SetResult` suitable for high-frequency concurrent resolution attempts.

### Can calling SetResult multiple times cause a panic?

No. The atomic flag prevents the `close(p.done)` operation from executing more than once. Without this protection, closing an already-closed Go channel would panic, but `SetResult` safely drops subsequent calls before reaching the channel close.

### How does the atomic operation affect memory visibility for other goroutines?

The atomic swap provides a **happens-before** relationship that synchronizes memory between the resolving goroutine and any goroutine calling `Await` or `AwaitWithErrCh`. This ensures that when an awaiting goroutine observes the resolved state, it sees the complete writes to `p.result` and `p.err`, not stale or partially updated values.