This package provides a weighted semaphore that is context-aware. The code is derived from a similar package inside Google. Change-Id: Id1dad96d79e8ccfd289e4299e8265aa5bdad3a5b Reviewed-on: https://go-review.googlesource.com/38298 Reviewed-by: Ian Lance Taylor <iant@golang.org> Reviewed-by: Damien Neil <dneil@google.com> Run-TryBot: Ian Lance Taylor <iant@golang.org> TryBot-Result: Gobot Gobot <gobot@golang.org>
		
			
				
	
	
		
			130 lines
		
	
	
	
		
			2.8 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			130 lines
		
	
	
	
		
			2.8 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| // Copyright 2017 The Go Authors. All rights reserved.
 | |
| // Use of this source code is governed by a BSD-style
 | |
| // license that can be found in the LICENSE file.
 | |
| 
 | |
| // +build go1.7
 | |
| 
 | |
| package semaphore
 | |
| 
 | |
| import (
 | |
| 	"fmt"
 | |
| 	"testing"
 | |
| 
 | |
| 	"golang.org/x/net/context"
 | |
| )
 | |
| 
 | |
| // weighted is an interface matching a subset of *Weighted.  It allows
 | |
| // alternate implementations for testing and benchmarking.
 | |
| type weighted interface {
 | |
| 	Acquire(context.Context, int64) error
 | |
| 	TryAcquire(int64) bool
 | |
| 	Release(int64)
 | |
| }
 | |
| 
 | |
| // semChan implements Weighted using a channel for
 | |
| // comparing against the condition variable-based implementation.
 | |
| type semChan chan struct{}
 | |
| 
 | |
| func newSemChan(n int64) semChan {
 | |
| 	return semChan(make(chan struct{}, n))
 | |
| }
 | |
| 
 | |
| func (s semChan) Acquire(_ context.Context, n int64) error {
 | |
| 	for i := int64(0); i < n; i++ {
 | |
| 		s <- struct{}{}
 | |
| 	}
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func (s semChan) TryAcquire(n int64) bool {
 | |
| 	if int64(len(s))+n > int64(cap(s)) {
 | |
| 		return false
 | |
| 	}
 | |
| 
 | |
| 	for i := int64(0); i < n; i++ {
 | |
| 		s <- struct{}{}
 | |
| 	}
 | |
| 	return true
 | |
| }
 | |
| 
 | |
| func (s semChan) Release(n int64) {
 | |
| 	for i := int64(0); i < n; i++ {
 | |
| 		<-s
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // acquireN calls Acquire(size) on sem N times and then calls Release(size) N times.
 | |
| func acquireN(b *testing.B, sem weighted, size int64, N int) {
 | |
| 	b.ResetTimer()
 | |
| 	for i := 0; i < b.N; i++ {
 | |
| 		for j := 0; j < N; j++ {
 | |
| 			sem.Acquire(context.Background(), size)
 | |
| 		}
 | |
| 		for j := 0; j < N; j++ {
 | |
| 			sem.Release(size)
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // tryAcquireN calls TryAcquire(size) on sem N times and then calls Release(size) N times.
 | |
| func tryAcquireN(b *testing.B, sem weighted, size int64, N int) {
 | |
| 	b.ResetTimer()
 | |
| 	for i := 0; i < b.N; i++ {
 | |
| 		for j := 0; j < N; j++ {
 | |
| 			if !sem.TryAcquire(size) {
 | |
| 				b.Fatalf("TryAcquire(%v) = false, want true", size)
 | |
| 			}
 | |
| 		}
 | |
| 		for j := 0; j < N; j++ {
 | |
| 			sem.Release(size)
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func BenchmarkNewSeq(b *testing.B) {
 | |
| 	for _, cap := range []int64{1, 128} {
 | |
| 		b.Run(fmt.Sprintf("Weighted-%d", cap), func(b *testing.B) {
 | |
| 			for i := 0; i < b.N; i++ {
 | |
| 				_ = NewWeighted(cap)
 | |
| 			}
 | |
| 		})
 | |
| 		b.Run(fmt.Sprintf("semChan-%d", cap), func(b *testing.B) {
 | |
| 			for i := 0; i < b.N; i++ {
 | |
| 				_ = newSemChan(cap)
 | |
| 			}
 | |
| 		})
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func BenchmarkAcquireSeq(b *testing.B) {
 | |
| 	for _, c := range []struct {
 | |
| 		cap, size int64
 | |
| 		N         int
 | |
| 	}{
 | |
| 		{1, 1, 1},
 | |
| 		{2, 1, 1},
 | |
| 		{16, 1, 1},
 | |
| 		{128, 1, 1},
 | |
| 		{2, 2, 1},
 | |
| 		{16, 2, 8},
 | |
| 		{128, 2, 64},
 | |
| 		{2, 1, 2},
 | |
| 		{16, 8, 2},
 | |
| 		{128, 64, 2},
 | |
| 	} {
 | |
| 		for _, w := range []struct {
 | |
| 			name string
 | |
| 			w    weighted
 | |
| 		}{
 | |
| 			{"Weighted", NewWeighted(c.cap)},
 | |
| 			{"semChan", newSemChan(c.cap)},
 | |
| 		} {
 | |
| 			b.Run(fmt.Sprintf("%s-acquire-%d-%d-%d", w.name, c.cap, c.size, c.N), func(b *testing.B) {
 | |
| 				acquireN(b, w.w, c.size, c.N)
 | |
| 			})
 | |
| 			b.Run(fmt.Sprintf("%s-tryAcquire-%d-%d-%d", w.name, c.cap, c.size, c.N), func(b *testing.B) {
 | |
| 				tryAcquireN(b, w.w, c.size, c.N)
 | |
| 			})
 | |
| 		}
 | |
| 	}
 | |
| }
 |