Skip to content
/ sema Public

A low-level concurrency manager for Go with support for select-based waiting and internal counter inspection. Providing an alternative to sync.WaitGroup, channel-based limiter and semaphore.Weighted.

License

Notifications You must be signed in to change notification settings

asmsh/sema

Repository files navigation

Sema: A feature-rich concurrency manager for Go.

PkgGoDev Go Report Card Tests Go Coverage

It bridges the gap between using channels, sync.WaitGroup and semaphore.Weighted for managing concurrency.

Features

  • Fast, low-overhead implementation with performance comparable to sync.WaitGroup and semaphore.Weighted.
  • Enables select-based waiting, in addition to standard sync.WaitGroup wait behavior.
  • Exposes the internal counters in a concurrent-safe way.
  • Clear API that's compatible with sync.WaitGroup and semaphore.Weighted with minimal changes.
  • Usable zero value that's comparable to a sync.WaitGroup.

Notes

  • It wakes up blocked calls in random order, unlike semaphore.Weighted which preserves the order of calls.
  • It's more suitable as a replacement for semaphore.Weighted when all the weights are of equal size.
  • It relies on a single channel for blocking and wake-ups, so the Go runtime guarantees no starvation.

Examples

Using it as a sync.WaitGroup:

func example() {
	var sg sema.Group

	for i := 0; i < 10; i++ {
		sg.Reserve()
		go func(i int) {
			defer sg.Free()

			// Do some work...
		}(i)
	}

	sg.Wait()
}

Using select-based waiting:

func example() {
	var sg sema.Group

	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
	defer cancel()

	for i := 0; i < 10; i++ {
		sg.Reserve()
		go func(i int) {
			defer sg.Free()

			// Do some work using the ctx...
			ctx = ctx
		}(i)
	}

	waitChan := sg.WaitChan()

	select {
	case <-waitChan:
		// all the work is done.
	case <-ctx.Done():
		// the work times out.
	}
}

Using it to limit concurrency (instead of a semaphore.Weighted or a channel):

func example() {
	var sg sema.Group
	sg.SetSize(10)

	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
	defer cancel()

	for i := 0; i < 10; i++ {
		sg.ReserveN(ctx.Done(), 5)
		go func(i int) {
			defer sg.FreeN(5)

			// Do some work...
			log.Println("some work...")
		}(i)
	}

	sg.Wait()
}

About

A low-level concurrency manager for Go with support for select-based waiting and internal counter inspection. Providing an alternative to sync.WaitGroup, channel-based limiter and semaphore.Weighted.

Topics

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages