spin

package module
v0.1.3 Latest Latest
Warning

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

Go to latest
Published: Dec 28, 2025 License: MIT Imports: 4 Imported by: 0

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 hint and must not block or yield the scheduler.
  • 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. It must not block or yield the scheduler.

Defaults to 30 cycles if not specified. Uses optimized assembly on amd64/arm64.

Usage:

Pause()     // 30 cycles (default)
Pause(1)    // 1 cycle
Pause(50)   // 50 cycles

func SetYieldDuration

func SetYieldDuration(d time.Duration)

SetYieldDuration sets the base duration unit for Yield(). 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.

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, spinning with an adaptive backoff.

func (*Lock) Try

func (sl *Lock) Try() bool

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

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