Skip to content

Commit

Permalink
feat: improved docs and event close logic
Browse files Browse the repository at this point in the history
  • Loading branch information
MarvinJWendt committed Sep 6, 2024
1 parent 3c3937b commit f295fac
Show file tree
Hide file tree
Showing 3 changed files with 74 additions and 3 deletions.
5 changes: 5 additions & 0 deletions .golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -172,8 +172,13 @@ linters-settings:
- d any # generic any (e.g. data)
- data any # generic data
- n any # generic any
- ch chan T # common generic channel name
- ch chan int # common generic channel name
- ch chan any # common generic channel name
- wg sync.WaitGroup # common generic WaitGroup name
- t time.Time # often used as a variable name
- f func() # often used as a callback variable name
- f func(T) # often used as a generic callback variable name
- cb func() # often used as a callback variable name
- t testing.T # default testing.T variable name
- b testing.B # default testing.B variable name
Expand Down
25 changes: 23 additions & 2 deletions event.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,12 @@ import "sync"
type Event[T any] struct {
listeners []chan T
mu sync.Mutex
closed bool
}

// New creates a new event.
func New[T any]() *Event[T] {
// Create a new event
return &Event[T]{
listeners: []chan T{},
}
Expand All @@ -21,13 +24,26 @@ func (e *Event[T]) Trigger(value T) {

for _, listener := range e.listeners {
go func(l chan T) {
l <- value
if !e.closed {
l <- value
}
}(listener)
}
}

// Listen gets called when the event is triggered.
func (e *Event[T]) Listen(f func(T)) {
// Check if the event is closed
if e.closed {
return
}

// Create listener slice if it doesn't exist
if e.listeners == nil {
e.listeners = []chan T{}
}

// Create a new channel
ch := make(chan T)

e.mu.Lock()
Expand All @@ -36,11 +52,15 @@ func (e *Event[T]) Listen(f func(T)) {

go func() {
for v := range ch {
f(v)
if !e.closed {
f(v)
}
}
}()
}

// Close closes the event and all its listeners.
// After calling this method, the event can't be used anymore and new listeners can't be added.
func (e *Event[T]) Close() {
e.mu.Lock()
defer e.mu.Unlock()
Expand All @@ -50,4 +70,5 @@ func (e *Event[T]) Close() {
}

e.listeners = nil
e.closed = true
}
47 changes: 46 additions & 1 deletion examples_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,15 @@ import (
"atomicgo.dev/event"
)

func delay() {
time.Sleep(time.Millisecond * 10)
}

type Player struct {
Name string
}

// Create a new event
var PlayerJoinEvent = event.New[Player]()

func Example_demo() {
Expand All @@ -19,15 +24,17 @@ func Example_demo() {
fmt.Printf("Player %q joined the game\n", p.Name)
})

PlayerJoinEvent.Listen(func(p Player) {
PlayerJoinEvent.Listen(func(_ Player) {
// Do something else
})

// ...

// Trigger the event somewhere - can be in a different function or package
PlayerJoinEvent.Trigger(Player{Name: "Marvin"})
delay() // delay for deterministic output
PlayerJoinEvent.Trigger(Player{Name: "Bob"})
delay() // delay for deterministic output
PlayerJoinEvent.Trigger(Player{Name: "Alice"})

// Keep the program alive
Expand All @@ -38,3 +45,41 @@ func Example_demo() {
// Player "Bob" joined the game
// Player "Alice" joined the game
}

func ExampleEvent_Close() {
// Create a new event
exampleEvent := event.New[int]()

// Listen to the event
exampleEvent.Listen(func(v int) {
fmt.Println(v)
})

// Trigger the event
exampleEvent.Trigger(1)
delay() // delay for deterministic output
exampleEvent.Trigger(2)
delay() // delay for deterministic output
exampleEvent.Trigger(3)

// Time for listeners to process the event
delay()

// Close the event
exampleEvent.Close()

// Trigger the event again
exampleEvent.Trigger(4)
delay() // delay for deterministic output
exampleEvent.Trigger(5)
delay() // delay for deterministic output
exampleEvent.Trigger(6)

// Keep the program alive
time.Sleep(time.Second)

// Output:
// 1
// 2
// 3
}

0 comments on commit f295fac

Please sign in to comment.