-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
John Doe
committed
Jun 20, 2022
1 parent
ccbdcf1
commit 3cded04
Showing
4 changed files
with
204 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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") | ||
|
||
} | ||
|
||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
module github.com/nickname76/repeater | ||
|
||
go 1.18 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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() | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} | ||
} |