func (p *Pauser) cancel(waiter chan<- struct{}) {
	for {
		oldState := atomic.LoadUint32(&p.state)
		if oldState != pauseStateLocked && atomic.CompareAndSwapUint32(&p.state, oldState, pauseStateLocked) {
			delete(p.waiters, waiter)
			atomic.StoreUint32(&p.state, oldState)
			return
		}
		runtime.Gosched()
	}
}
cancel removes a waiter from the waiters map
package common
import (
	"context"
	"runtime"
	"sync/atomic"
)
const (
	pauseStateRunning uint32 = iota
	pauseStatePaused
	pauseStateLocked
)
type Pauser struct {
	state   uint32
	waiters map[chan<- struct{}]struct{}
}
func NewPauser() *Pauser {
	return &Pauser{
		state:   pauseStateRunning,
		waiters: make(map[chan<- struct{}]struct{}, 32),
	}
}
func (p *Pauser) Pause() {
	for {
		oldState := atomic.LoadUint32(&p.state)
		if oldState == pauseStatePaused || atomic.CompareAndSwapUint32(&p.state, pauseStateRunning, pauseStatePaused) {
			return
		}
		runtime.Gosched()
	}
}
func (p *Pauser) Resume() {
	for {
		oldState := atomic.LoadUint32(&p.state)
		if oldState == pauseStateRunning {
			return
		}
		if atomic.CompareAndSwapUint32(&p.state, pauseStatePaused, pauseStateLocked) {
			break
		}
		runtime.Gosched()
	}
	allWaiters := p.waiters
	p.waiters = make(map[chan<- struct{}]struct{}, len(allWaiters))
	atomic.StoreUint32(&p.state, pauseStateRunning)
	for waiter := range allWaiters {
		close(waiter)
	}
}
func (p *Pauser) IsPaused() bool {
	return atomic.LoadUint32(&p.state) != pauseStateRunning
}
func (p *Pauser) Wait(ctx context.Context) error {
	for {
		oldState := atomic.LoadUint32(&p.state)
		if oldState == pauseStateRunning {
			return nil
		}
		if atomic.CompareAndSwapUint32(&p.state, pauseStatePaused, pauseStateLocked) {
			break
		}
		runtime.Gosched()
	}
	waiter := make(chan struct{})
	p.waiters[waiter] = struct{}{}
	atomic.StoreUint32(&p.state, pauseStatePaused)
	select {
	case <-ctx.Done():
		err := ctx.Err()
		p.cancel(waiter)
		return err
	case <-waiter:
		return nil
	}
}