diff --git a/idle/idle.go b/idle/idle.go new file mode 100644 index 0000000..4d7825a --- /dev/null +++ b/idle/idle.go @@ -0,0 +1,68 @@ +// Package idle helps to gracefully shutdown idle servers +package idle + +import ( + "sync/atomic" + "time" +) + +type Idler interface { + Enter() + Exit() + Wait() + Tick() + Chan() <-chan struct{} +} + +type idler struct { + lastTick atomic.Pointer[time.Time] + c chan struct{} + active atomic.Int64 +} + +func (i *idler) Enter() { + i.active.Add(1) +} + +func (i *idler) Exit() { + i.Tick() + i.active.Add(-1) +} + +func CreateIdler(timeout time.Duration) Idler { + i := &idler{} + i.c = make(chan struct{}) + i.Tick() + go func() { + for { + if i.active.Load() != 0 { + time.Sleep(timeout) + } else { + t := *i.lastTick.Load() + now := time.Now() + dur := t.Add(timeout).Sub(now) + if dur == dur.Abs() { + time.Sleep(dur) + } else { + break + } + } + + } + close(i.c) + }() + return i +} + +func (i *idler) Tick() { + now := time.Now() + i.lastTick.Store(&now) +} + +func (i *idler) Wait() { + <-i.c +} + +func (i *idler) Chan() <-chan struct{} { + return i.c +} diff --git a/idle/idle_test.go b/idle/idle_test.go new file mode 100644 index 0000000..bce4c51 --- /dev/null +++ b/idle/idle_test.go @@ -0,0 +1,11 @@ +package idle + +import ( + "testing" + "time" +) + +func TestIdlerChan(t *testing.T) { + i := CreateIdler(1 * time.Second) + <-i.Chan() +}