8000 GitHub - zeebo/swaparoo: package swaparoo provides a scalable way to ensure there are no handles to some resource
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content
/ swaparoo Public

package swaparoo provides a scalable way to ensure there are no handles to some resource

Notifications You must be signed in to change notification settings

zeebo/swaparoo

Repository files navigation

swaparoo

import "github.com/zeebo/swaparoo"

package swaparoo provides a scalable way to ensure there are no handles to some resource.

Consider the case where you have some counters that you periodically want to read and reset. A lock-free way to implement this might be:

var counters [1000]uint64

func AddToCounter(n int) {
	atomic.AddUint64(&counter[n], 1)
}

func Reset() (out [1000]uint64) {
	for i := 0; i < len(counters); i++ {
		for {
			old := atomic.LoadUint64(&counter[i])
			if atomic.CompareAndSwapUint64(&counter[i], old, 0) {
				out[i] = old
			}
		}
	}
	return out
}

This solution suffers from having to do a number of atomic operations that scales with the number of counters. Additionally, it does not provide a consistent snapshot of the values in the counters because it cannot read all of them at once. Using the types in this package, however, both of these problems can be solved in a lock-free way:

var (
	counters [2][1000]uint64
	tracker  = swaparoo.NewTracker()
)

func AddToCounter(n int) {
	token := tracker.Acquire()
	atomic.AddUint64(&counters[token.Gen()%2][n], 1)
	token.Release()
}

func Reset() (out [1000]uint64) {
	gen := tracker.Increment().Wait()%2
	out, counters[gen] = counters[gen], [1000]uint64{}
	return out
}

The Acquire and Release calls, in the common case with no outstanding Increments, will read some rarely-changing shared value and modify a counter in some best-effort thread local storage, adding little overhead. The Increment call should be more infrequent becasuse it changes the shared value and reads/writes all of the thread-local counters. It does not stop the progress of future Acquire calls, however, allowing throughput on Acquire to remain high.

Benchmarks

BenchmarkSwaparoo/Acquire-8             100000000        14.5 ns/op
BenchmarkSwaparoo/Increment-8           20000000         87.7 ns/op
BenchmarkSwaparoo/Parallel/Acquire-8    1000000000       2.67 ns/op
BenchmarkSwaparoo/Parallel/Increment-8  500000000        4.01 ns/op

Usage

type Pending

type Pending struct {
}

Pending represents a generation of the tracker that has been Incremented past. When a call to Wait returns, it can be sure that no one has any Tokens with the same generation.

func (Pending) Gen

func (p Pending) Gen() uint64

Gen returns the generation the Pending is associated to.

func (Pending) Wait

func (p Pending) Wait() uint64

Wait blocks until all Tokens with the same generation are Released. It returns the generation the Pending is associated to.

type Token

type Token struct {
}

Token keeps track of the Tracker's current generation and prevents changes to it while it is not Released.

func (Token) Gen

func (t Token) Gen() uint64

Gen reports the current generation of the Tracker.

func (Token) Hint

func (t Token) Hint() uint64

Hint reports a thread hint associated with the Token.

func (Token) Release

func (t Token) Release()

Release invalidates the Token and must be called exactly once.

type Tracker

type Tracker struct {
}

Tracker allows one to acquire Tokens that come with a monotonically increasing generation number. It does so in a scalable way, and optimizes for the case where not many changes to the generation happen. The zero value is safe to use.

func (*Tracker) Acquire

func (t *Tracker) Acquire() Token

Acquire returns a Token that can be used to inspect the current generation. It must be Released before an Increment of the Token's generation can complete. It is safe to be called concurrently.

func (*Tracker) Increment

func (t *Tracker) Increment() Pending

Increment bumps the generation of the Tracker for future Acquire calls and returns a Pending that can be used to Wait until all currently Acquired Tokens with the same generation are Released. It is safe to be called concurrently.

About

package swaparoo provides a scalable way to ensure there are no handles to some resource

Resources

Stars

Watchers

Forks

Packages

No packages published

Languages

0