91 lines
1.8 KiB
Go

package util
import (
"sync"
"time"
"errors"
)
type Signal struct {
isSet bool
autoReset bool
*sync.Mutex
chNotSet chan interface{} // channel is open, when signal isn't set
}
func NewSignal(isSet, autoReset bool) (s *Signal) {
s = &Signal{
Mutex: &sync.Mutex{},
isSet: isSet,
autoReset: autoReset,
chNotSet: make(chan interface{}, 0),
}
if s.isSet {
s.Lock()
defer s.Unlock()
s.isSet = true
close(s.chNotSet)
}
return
}
func (s *Signal) Set() {
// if signaled, the channel has to be closed
// we can't test if the channel is already closed (without waiting with select), so we keep track of the state
// in isSet, to avoid closing multiple times
s.Lock()
defer s.Unlock()
if s.isSet {
return
}
s.isSet = true
close(s.chNotSet)
return
}
func (s *Signal) Reset() {
// in reset state, the channel has to exist, but mustn't be recreated if already existing (already in unset state)
s.Lock()
defer s.Unlock()
if s.isSet {
// channel shouldn't exist
s.chNotSet = make(chan interface{}, 0)
s.isSet = false
}
return
}
func (s Signal) IsSet() bool {
return s.isSet
}
func (s *Signal) Wait() {
select {
case <-s.chNotSet: // when channel isn't closed, this blocks
// if autoReset, recreate channel (setting signal to off)
if s.autoReset {
s.Lock()
s.chNotSet = make(chan interface{}, 0)
s.isSet = false
s.Unlock()
}
}
return
}
func (s *Signal) WaitTimeout(timeout time.Duration) error {
select {
case <-s.chNotSet: // when channel isn't closed, this blocks
// if autoReset, recreate channel (setting signal to off)
if s.autoReset {
s.Lock()
s.chNotSet = make(chan interface{}, 0)
s.isSet = false
s.Unlock()
}
return nil
case <-time.After(timeout):
return errors.New("Timeout reached")
}
}