Skip to content

Commit

Permalink
Upload first release
Browse files Browse the repository at this point in the history
  • Loading branch information
John Doe committed Jun 20, 2022
1 parent ccbdcf1 commit 3cded04
Show file tree
Hide file tree
Showing 4 changed files with 204 additions and 0 deletions.
89 changes: 89 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
# Repeater

A Go library, for easy creation of repeating function calls. Use it for things like checking updates and others periodic actions.

It is also supports aggregation of several repeaters in one manager (see [`MultiRepeater`](#multirepeater))

## Exmple usage

### StartRepeater

```Go
package main

import (
"time"

"github.com/nickname76/repeater"
)

func main() {
stop := repeater.StartRepeater(time.Second, func() {
println(1)
time.Sleep(time.Second)
println(2)
time.Sleep(time.Second)
println(3)
})

time.Sleep(time.Second * 10)

println("STOPPIN")
stop()
}

```

### MultiRepeater

```Go
package main

import (
"time"

"github.com/nickname76/repeater"
)

func main() {
mr := repeater.NewMultiRepeater[int]()

mr.StartRepeater(1, time.Second, func() {
println(1)
})

mr.StartRepeater(2, time.Second, func() {
println(2)
})

created := mr.StartRepeater(2, time.Second, func() {
println(2)
})
if !created {
println("oops, using same id with the second one repeater")
}

mr.StartRepeater(3, 0, func() {
println(3)
time.Sleep(time.Second)
println(33)
})

time.Sleep(time.Second * 1)

stopped := mr.StopRepeater(1)
if stopped {
println("repeater with id 1 is now stopped")
}

time.Sleep(time.Second * 2)

println("Now stopping repeaters with ids 2 and 3")

mr.StopAllRepeaters()

println("All repeaters are now stopped")

}

```
3 changes: 3 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module github.com/nickname76/repeater

go 1.18
64 changes: 64 additions & 0 deletions multirepeater.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package repeater

import (
"sync"
"time"
)

// Holds any amount of repeaters. Uses StartRepeater. Use this to manage several repeaters by ids. Use NewMultiRepeater to instance MultiRepeater.
type MultiRepeater[ID comparable] struct {
repeaters map[ID]func()
mux sync.Mutex
}

// Instances MultiRepeater. Pass ID type you'd like to use to manage repeaters.
func NewMultiRepeater[ID comparable]() *MultiRepeater[ID] {
return &MultiRepeater[ID]{
repeaters: make(map[ID]func()),
mux: sync.Mutex{},
}
}

// Works almost same as original StartRepeater, but delegates stop function managment to MultiRepeater and requires id for repeater.
// Returns `true` on success, and `false` if given id is occupied.
func (mr *MultiRepeater[ID]) StartRepeater(id ID, frequency time.Duration, fnToCall func()) bool {
mr.mux.Lock()
if mr.repeaters[id] != nil {
mr.mux.Unlock()
return false
}
mr.repeaters[id] = StartRepeater(frequency, fnToCall)
mr.mux.Unlock()
return true
}

// Stops repeater by ID. Return `true` on success, and `false` if given id is not found.
func (mr *MultiRepeater[ID]) StopRepeater(id ID) bool {
mr.mux.Lock()
if mr.repeaters[id] == nil {
mr.mux.Unlock()
return false
}
mr.repeaters[id]()
delete(mr.repeaters, id)
mr.mux.Unlock()
return true
}

// Stops all repeaters managed by this MultiRepeater. Waits until all repeaters are stopped.
func (mr *MultiRepeater[ID]) StopAllRepeaters() {
wg := &sync.WaitGroup{}

mr.mux.Lock()
wg.Add(len(mr.repeaters))
for _, stop := range mr.repeaters {
go func(stop func()) {
stop()
wg.Done()
}(stop)
}
mr.repeaters = make(map[ID]func())
mr.mux.Unlock()

wg.Wait()
}
48 changes: 48 additions & 0 deletions repeater.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package repeater

import (
"time"
)

// Start repeating fnToCall with given frequency, frequency can be 0, if so, ticker won't be used.
// Use returned function to stop repeater, this function waits until repeater is stopped.
func StartRepeater(frequency time.Duration, fnToCall func()) (stop func()) {
if fnToCall == nil {
panic("fnToCall must not be nil")
}

stopCh := make(chan struct{})
waitCh := make(chan struct{})

go func() {
defer close(waitCh)

if frequency != 0 {
ticker := time.NewTicker(frequency)

for {
select {
case <-stopCh:
ticker.Stop()
return
case <-ticker.C:
fnToCall()
}
}
} else {
for {
select {
case <-stopCh:
return
default:
fnToCall()
}
}
}
}()

return func() {
close(stopCh)
<-waitCh
}
}

0 comments on commit 3cded04

Please sign in to comment.