spin

package module
v0.1.6 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 4, 2026 License: MIT Imports: 4 Imported by: 3

README

Go Reference Go Report Card Coverage Status

spin

Provides spinlock(Lock) and spin-wait(Wait) primitives for performance-sensitive spinning.

  • Lock — spinlock for extremely short critical sections
  • Wait — spin wait for adaptive spinning
  • Pause — CPU hint for tight loops
  • Yield — cooperative yield/sleep for non-hot paths

Languages: English | 简体中文 | Español | 日本語 | Français

Installation

go get code.hybscloud.com/spin

Quick start

func workReady() bool { return true }

func main() {
    var sl spin.Lock
    sl.Lock()
    // critical section
    sl.Unlock()
    fmt.Println("ok")

    var sw spin.Wait
    for !workReady() {
        sw.Once()
    }

    spin.Pause() // CPU hint in a hot loop
    spin.Yield() // cooperative yield (non-hot path)
}

API overview

  • type Lock

    • Lock() spins until acquired with adaptive backoff.
    • Unlock() releases.
    • Try() attempts to acquire without waiting; returns true on success.
  • type Wait

    • Once() performs one adaptive step (CPU Pause or cooperative yield), suitable for tight loops.
    • WillYield() reports whether the next Once() will yield instead of pausing.
    • Reset() clears internal counters.
  • func Pause(cycles ...int)

    • Issues an architecture-specific CPU pause hint. If omitted, cycles defaults to 30 pause hints. cycles is a repeat count for pause hints, not a calibrated CPU-cycle delay. On hardware-pause targets it does not block or yield the scheduler; wasm and unsupported fallback targets may yield via runtime.Gosched().
  • func Yield(duration ...time.Duration)

    • Cooperatively yields. By default sleeps for a small duration; if non-positive, falls back to runtime.Gosched().
  • func SetYieldDuration(d time.Duration)

    • Sets the base sleep duration used by Yield() when no explicit argument is provided.

Notes:

  • Lock is non-fair and should not be used as a general-purpose mutex.
  • Prefer Wait in spin loops instead of ad-hoc for {} + runtime.Gosched().

Architectures: amd64, arm64, 386, arm, riscv64, ppc64le, s390x, loong64, wasm.

When to use

  • Use Lock only for extremely short critical sections; it is non-fair and intended for specialized scenarios.
  • Use Wait for adaptive spin-waiting when progress is expected very soon; it escalates from Pause to cooperative yield.
  • Use Pause in tight polling loops on hot paths to reduce power and contention between hyper-threads.
  • Use Yield in non-hot paths to cooperatively let other goroutines run (optionally sleeping a short duration).

For general-purpose mutual exclusion, prefer sync.Mutex.

License

MIT — see LICENSE.

©2025 Hayabusa Cloud Co., Ltd.

Documentation

Overview

Package spin provides minimal spin-based primitives for performance-critical code paths:

  • Lock — non-fair spinlock for extremely short critical sections
  • Wait — adaptive spin-wait helper for tight polling loops
  • Pause — architecture-specific CPU hint for busy-wait loops
  • Yield — cooperative yield/sleep for non-hot paths

Design notes

  • Hot paths should prefer Pause (light CPU hint) and Wait (adaptive backoff) instead of ad-hoc loops with runtime.Gosched().
  • Lock is intentionally non-fair and intended only for very short critical sections where OS mutex overhead is prohibitive.
  • No allocations in hot paths; functions return immediately and never block unless explicitly documented (Yield with a positive sleep duration).

Architectures: amd64, arm64, 386, arm, riscv64, ppc64le, s390x, loong64, wasm.

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func Pause

func Pause(cycles ...int)

Pause executes CPU pause instructions to reduce energy consumption in spin-wait loops. On hardware-pause targets, it does not block or yield the scheduler. On wasm and unsupported fallback targets, it may yield via runtime.Gosched.

Defaults to 30 pause hints if not specified. The cycles parameter is a historical repeat-count name, not a calibrated CPU-cycle delay. Uses optimized assembly on supported hardware-pause targets.

Usage:

Pause()     // 30 pause hints (default)
Pause(1)    // 1 pause hint
Pause(50)   // 50 pause hints

func SetYieldDuration

func SetYieldDuration(d time.Duration)

SetYieldDuration sets the base duration unit for Yield(). Safe for concurrent use. Recommended: 50-250 microseconds for real-time systems, 1-4 ms for general workloads.

func Yield

func Yield(duration ...time.Duration)

Yield cooperatively yields execution to reduce CPU burn in tight loops.

By default, Yield sleeps for a small duration (configured by SetYieldDuration) to give other goroutines and the OS scheduler a chance to run.

If an explicit duration is provided, it overrides the default for this call. A non-positive duration disables sleeping and falls back to runtime.Gosched() (a purely cooperative yield without a timer sleep).

For automatic adaptive backoff in tight loops, use Wait instead.

Example
package main

import (
	"fmt"
	"time"

	"code.hybscloud.com/spin"
)

func main() {
	spin.Yield(10 * time.Millisecond)
	fmt.Println("yielded")
}

Types

type Lock

type Lock struct {
	// contains filtered or unexported fields
}

Lock is a minimal, non-fair spin lock intended for very short critical sections on hot paths. It avoids allocations and OS mutex overhead but should not be used as a general-purpose lock.

Acquisition uses FAA (Fetch-And-Add) with a TTAS (test-and-test-and-set) slow path. FAA completes in a single atomic instruction (LOCK XADD on x86, LDADDAL on arm64) — under contention, CAS produces O(n²) cache-line invalidations while FAA keeps traffic at O(n). The TTAS slow path spins on read-only Load (MESI Shared) to avoid bouncing the cache line, and only attempts FAA when the lock appears free.

Example
package main

import (
	"fmt"

	"code.hybscloud.com/spin"
)

func main() {
	var lk spin.Lock
	lk.Lock()
	// critical section
	lk.Unlock()
	fmt.Println("locked")
}
Output:
locked

func (*Lock) Lock

func (sl *Lock) Lock()

Lock acquires the lock. Fast path: single FAA. Slow path: TTAS (test-and-test-and-set) — spin on read-only Load to keep the cache line in MESI Shared state, then attempt FAA only when the lock appears free.

func (*Lock) Try

func (sl *Lock) Try() bool

Try attempts to acquire the lock without blocking. It returns true if the lock was acquired.

Uses FAA rather than CAS: on x86 LOCK CMPXCHG takes exclusive cache-line ownership regardless of comparison outcome, so a failed CAS costs the same coherence traffic as FAA. FAA keeps the atomic pattern consistent across the Lock API.

A failed Try increments n without a corresponding decrement. This is intentional: Unlock resets n to zero via Store(0), clearing all accumulated increments. The uintptr counter space is large enough that overflow is not a practical concern.

func (*Lock) Unlock

func (sl *Lock) Unlock()

Unlock releases the lock.

type Wait

type Wait struct {
	// contains filtered or unexported fields
}

Wait is a lightweight adaptive spin-wait helper used in tight polling loops.

It escalates from a short CPU pause to a cooperative scheduler yield based on the recent history of calls. The zero value is ready to use.

Example
package main

import (
	"fmt"
	"sync/atomic"
	"time"

	"code.hybscloud.com/spin"
)

func main() {
	var sw spin.Wait
	var ready atomic.Bool
	go func() {
		time.Sleep(50 * time.Microsecond)
		ready.Store(true)
	}()
	for !ready.Load() {
		sw.Once()
	}
	fmt.Println("ready")
}
Output:
ready

func (*Wait) Once

func (s *Wait) Once()

Once performs a single adaptive step in the spin-wait strategy: it either issues a light `Pause()` or yields the scheduler via `runtime.Gosched()`.

func (*Wait) Reset

func (s *Wait) Reset()

Reset resets the counters in Wait.

func (*Wait) WillYield

func (s *Wait) WillYield() bool

WillYield reports whether the next call to `Once` will yield the processor (`runtime.Gosched`) instead of performing a light `Pause()`.

It is a pure "peek": it does not modify the internal state.

Directories

Path Synopsis
internal
pause
Package pause contains arch-specific pause/yield primitives.
Package pause contains arch-specific pause/yield primitives.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL