// Copyright 2011, 2012, 2013 Canonical Ltd.
// Licensed under the LGPLv3, see LICENCE file for details.

package utils

import (
	"fmt"
)

type empty struct{}
type limiter chan empty

// Limiter represents a limited resource (eg a semaphore).
type Limiter interface {
	// Acquire another unit of the resource.
	// Acquire returns false to indicate there is no more availability,
	// until another entity calls Release.
	Acquire() bool
	// AcquireWait requests a unit of resource, but blocks until one is
	// available.
	AcquireWait()
	// Release returns a unit of the resource. Calling Release when there
	// are no units Acquired is an error.
	Release() error
}

func NewLimiter(max int) Limiter {
	return make(limiter, max)
}

// Acquire requests some resources that you can return later
// It returns 'true' if there are resources available, but false if they are
// not. Callers are responsible for calling Release if this returns true, but
// should not release if this returns false.
func (l limiter) Acquire() bool {
	e := empty{}
	select {
	case l <- e:
		return true
	default:
		return false
	}
}

// AcquireWait waits for the resource to become available before returning.
func (l limiter) AcquireWait() {
	e := empty{}
	l <- e
}

// Release returns the resource to the available pool.
func (l limiter) Release() error {
	select {
	case <-l:
		return nil
	default:
		return fmt.Errorf("Release without an associated Acquire")
	}
}
