cond

package module
v0.1.1 Latest Latest
Warning

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

Go to latest
Published: Mar 31, 2024 License: MIT Imports: 3 Imported by: 0

README

Go-cond

Go Reference Go Report Card

Safer sync.Cond alternative

Overview

This library provides Cond and RWCond with safer and enhanced API. Standard sync.Cond is prone to hanging goroutines and this library addresses these issues. Built on top of wake library, it provides the same API layout.

Slower than sync.Cond ~3x (used sync/cond_test.go, which benchmarks only broadcast).

Features

Operation sync.Cond go-cond Notes
Wake a goroutine (if any) c.Signal() m := c.Signal(1) Unlike standard sync.Cond, Signal reports, how many goroutines were awoken by this call
Wake all goroutines c.Broadcast() c.Broadcast()
Wait for signal c.Wait() ok := c.Wait() Wait reports, if it was unblocked due receiving signal/broadcast or Cond was closed
Wake "n" goroutines (if any) m := c.Signal(n) You can wake N goroutines
Wake exactly "n" goroutines m, err := c.SignalWithContext(ctx, n) You can wake exactly N goroutines. Blocks until wakes all N, context is cancelled or cond is closed
Wait with context for signal ok, err := c.WaitWithContext(ctx) Wait with context. Same as Wait + unblocks in case of context cancellation
Get a number of waiting goroutines n := c.WaitCount()
Close Cond first := c.Close() Close cond. All waiting goroutines will be awoken. Reported value indicates, if it is the first Close call. All methods are safe to use even after cond is closed
Use RWMutex + RLock/RUnlock NewRW(&l) You can create RWCond, which uses RLock and RUnlock in Wait* methods.

Example

go get github.com/nursik/go-cond
import (
	"context"
	"fmt"
	"sync"
	"time"

	"github.com/nursik/go-cond"
)

func Wait(c *cond.Cond, mp map[int]int, key int) {
	c.L.Lock()
	for {
		value, ok := mp[key]
		if ok {
			fmt.Printf("Found %v at key %v\n", key, value)
			break
		}
		// Cond.Wait returns false only if Cond was closed.
		if !c.Wait() {
			fmt.Printf("Not found key %v\n", key)
			break
		}
	}
	c.L.Unlock()

	fmt.Printf("Key %v checker exit\n", key)
}

func main() {
	var locker sync.Mutex
	mp := make(map[int]int)

	c := cond.New(&locker)
	defer c.Close()

	go Wait(c, mp, 1)
	go Wait(c, mp, 2)
	go Wait(c, mp, 3)

	locker.Lock()
	mp[1] = 2
	locker.Unlock()

	c.Broadcast()

	locker.Lock()
	mp[2] = 4
	locker.Unlock()

	c.Broadcast()

	locker.Lock()
	mp[3] = 6
	locker.Unlock()

	c.Signal(1)

	ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(time.Second))
	defer cancel()

	locker.Lock()
	// Nobody can wake. Unblocks with (true, context.DeadlineExceeded).
	notClosed, err := c.WaitWithContext(ctx)
	fmt.Println(notClosed, err)
	locker.Unlock()
}

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Cond

type Cond struct {
	L sync.Locker
	// contains filtered or unexported fields
}

func New

func New(l sync.Locker) *Cond

New returns Cond with associated locker. Same as sync.Cond in terms of usage, but has more functionality. Only Wait and WaitWithContext methods use associated locker and other methods do not use locker. Using closed Cond is safe. Slower than sync.Cond by ~3 times (sync.Cond's tests which only benchmarks broadcast).

func (*Cond) Broadcast

func (c *Cond) Broadcast()

Broadcast wakes up all goroutines.

func (*Cond) Close

func (c *Cond) Close() bool

Close closes Cond/RWCond and wakes all waiting goroutines. The first Close() returns true and subsequent calls always return false.

func (*Cond) IsClosed

func (c *Cond) IsClosed() bool

IsClosed reports if Cond/RWCond is closed.

func (*Cond) Signal

func (c *Cond) Signal(n int) int

Signal wakes n goroutines (if there are any) and reports how many goroutines were awoken. If n <= 0 it wakes all goroutines and returns 0 (same as [commonCond.Broadcast]).

func (*Cond) SignalWithContext

func (c *Cond) SignalWithContext(ctx context.Context, n int) (int, error)

SignalWithContext wakes n goroutines and reports how many goroutines were awoken and ctx.Err() if context was cancelled. It is a blocking operation and will be finished when all n goroutines are awoken, context is cancelled or Cond/RWCond was closed. If n <= 0, it wakes all goroutines (same as [commonCond.Broadcast]) regardless of context cancellation.

func (*Cond) Wait

func (c *Cond) Wait() bool

Wait Unlocks locker, blocks until awaken (returns true) or Cond was closed (returns false), and at the end Locks locker again.

func (*Cond) WaitCount

func (c *Cond) WaitCount() int

WaitCount returns current number of goroutines waiting for signal.

func (*Cond) WaitWithContext

func (c *Cond) WaitWithContext(ctx context.Context) (bool, error)

WaitWithContext Unlocks locker, blocks until awaken, context was cancelled or Cond was closed, and at the end Locks locker again. Returns true and nil, if awaken by signal/broadcast. Returns false and nil, if Cond was closed. Returns false and ctx.Err(), if context was cancelled.

type RWCond

type RWCond struct {
	L *sync.RWMutex
	// contains filtered or unexported fields
}

func NewRW

func NewRW(l *sync.RWMutex) *RWCond

NewRW returns RWCond with associated sync.RWMutex. Uses RUnlock and RLock for Wait and WaitWithContext methods. Other methods do not use associated sync.RWMutex.

func (*RWCond) Broadcast

func (c *RWCond) Broadcast()

Broadcast wakes up all goroutines.

func (*RWCond) Close

func (c *RWCond) Close() bool

Close closes Cond/RWCond and wakes all waiting goroutines. The first Close() returns true and subsequent calls always return false.

func (*RWCond) IsClosed

func (c *RWCond) IsClosed() bool

IsClosed reports if Cond/RWCond is closed.

func (*RWCond) Signal

func (c *RWCond) Signal(n int) int

Signal wakes n goroutines (if there are any) and reports how many goroutines were awoken. If n <= 0 it wakes all goroutines and returns 0 (same as [commonCond.Broadcast]).

func (*RWCond) SignalWithContext

func (c *RWCond) SignalWithContext(ctx context.Context, n int) (int, error)

SignalWithContext wakes n goroutines and reports how many goroutines were awoken and ctx.Err() if context was cancelled. It is a blocking operation and will be finished when all n goroutines are awoken, context is cancelled or Cond/RWCond was closed. If n <= 0, it wakes all goroutines (same as [commonCond.Broadcast]) regardless of context cancellation.

func (*RWCond) Wait

func (c *RWCond) Wait() bool

Wait RUnlocks locker, blocks until awaken (returns true) or RWCond was closed (returns false), and at the end RLocks locker again.

func (*RWCond) WaitCount

func (c *RWCond) WaitCount() int

WaitCount returns current number of goroutines waiting for signal.

func (*RWCond) WaitWithContext

func (c *RWCond) WaitWithContext(ctx context.Context) (bool, error)

WaitWithContext RUnlocks locker, blocks until awaken, context was cancelled or RWCond was closed, and at the end RLocks locker again. Returns true and nil, if awaken by signal/broadcast. Returns false and nil, if RWCond was closed. Returns false and ctx.Err(), if context was cancelled.

Jump to

Keyboard shortcuts

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