semaphore.go 1.5 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152
  1. // Package semaphore implements the semaphore resiliency pattern for Go.
  2. package semaphore
  3. import (
  4. "errors"
  5. "time"
  6. )
  7. // ErrNoTickets is the error returned by Acquire when it could not acquire
  8. // a ticket from the semaphore within the configured timeout.
  9. var ErrNoTickets = errors.New("could not acquire semaphore ticket")
  10. // Semaphore implements the semaphore resiliency pattern
  11. type Semaphore struct {
  12. sem chan struct{}
  13. timeout time.Duration
  14. }
  15. // New constructs a new Semaphore with the given ticket-count
  16. // and timeout.
  17. func New(tickets int, timeout time.Duration) *Semaphore {
  18. return &Semaphore{
  19. sem: make(chan struct{}, tickets),
  20. timeout: timeout,
  21. }
  22. }
  23. // Acquire tries to acquire a ticket from the semaphore. If it can, it returns nil.
  24. // If it cannot after "timeout" amount of time, it returns ErrNoTickets. It is
  25. // safe to call Acquire concurrently on a single Semaphore.
  26. func (s *Semaphore) Acquire() error {
  27. select {
  28. case s.sem <- struct{}{}:
  29. return nil
  30. case <-time.After(s.timeout):
  31. return ErrNoTickets
  32. }
  33. }
  34. // Release releases an acquired ticket back to the semaphore. It is safe to call
  35. // Release concurrently on a single Semaphore. It is an error to call Release on
  36. // a Semaphore from which you have not first acquired a ticket.
  37. func (s *Semaphore) Release() {
  38. <-s.sem
  39. }
  40. // IsEmpty will return true if no tickets are being held at that instant.
  41. // It is safe to call concurrently with Acquire and Release, though do note
  42. // that the result may then be unpredictable.
  43. func (s *Semaphore) IsEmpty() bool {
  44. return len(s.sem) == 0
  45. }