terraform: Adding a semaphore implementation
This commit is contained in:
parent
8f98a79cf1
commit
ad31023252
|
@ -1,5 +1,47 @@
|
||||||
package terraform
|
package terraform
|
||||||
|
|
||||||
|
// Semaphore is a wrapper around a channel to provide
|
||||||
|
// utility methods to clarify that we are treating the
|
||||||
|
// channel as a semaphore
|
||||||
|
type Semaphore chan struct{}
|
||||||
|
|
||||||
|
// NewSemaphore creates a semaphore that allows up
|
||||||
|
// to a given limit of simultaneous acquisitions
|
||||||
|
func NewSemaphore(n int) Semaphore {
|
||||||
|
if n == 0 {
|
||||||
|
panic("semaphore with limit 0")
|
||||||
|
}
|
||||||
|
ch := make(chan struct{}, n)
|
||||||
|
return Semaphore(ch)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Acquire is used to acquire an available slot.
|
||||||
|
// Blocks until available.
|
||||||
|
func (s Semaphore) Acquire() {
|
||||||
|
s <- struct{}{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TryAcquire is used to do a non-blocking acquire.
|
||||||
|
// Returns a bool indicating success
|
||||||
|
func (s Semaphore) TryAcquire() bool {
|
||||||
|
select {
|
||||||
|
case s <- struct{}{}:
|
||||||
|
return true
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Release is used to return a slot. Acquire must
|
||||||
|
// be called as a pre-condition.
|
||||||
|
func (s Semaphore) Release() {
|
||||||
|
select {
|
||||||
|
case <-s:
|
||||||
|
default:
|
||||||
|
panic("release without an acquire")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// strSliceContains checks if a given string is contained in a slice
|
// strSliceContains checks if a given string is contained in a slice
|
||||||
// When anybody asks why Go needs generics, here you go.
|
// When anybody asks why Go needs generics, here you go.
|
||||||
func strSliceContains(haystack []string, needle string) bool {
|
func strSliceContains(haystack []string, needle string) bool {
|
||||||
|
|
|
@ -1,6 +1,36 @@
|
||||||
package terraform
|
package terraform
|
||||||
|
|
||||||
import "testing"
|
import (
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestSemaphore(t *testing.T) {
|
||||||
|
s := NewSemaphore(2)
|
||||||
|
timer := time.AfterFunc(time.Second, func() {
|
||||||
|
panic("deadlock")
|
||||||
|
})
|
||||||
|
defer timer.Stop()
|
||||||
|
|
||||||
|
s.Acquire()
|
||||||
|
if !s.TryAcquire() {
|
||||||
|
t.Fatalf("should acquire")
|
||||||
|
}
|
||||||
|
if s.TryAcquire() {
|
||||||
|
t.Fatalf("should not acquire")
|
||||||
|
}
|
||||||
|
s.Release()
|
||||||
|
s.Release()
|
||||||
|
|
||||||
|
// This release should panic
|
||||||
|
defer func() {
|
||||||
|
r := recover()
|
||||||
|
if r == nil {
|
||||||
|
t.Fatalf("should panic")
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
s.Release()
|
||||||
|
}
|
||||||
|
|
||||||
func TestStrSliceContains(t *testing.T) {
|
func TestStrSliceContains(t *testing.T) {
|
||||||
if strSliceContains(nil, "foo") {
|
if strSliceContains(nil, "foo") {
|
||||||
|
|
Loading…
Reference in New Issue