Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Using channels for events and closing #537

Closed
eNV25 opened this issue Jul 1, 2022 · 3 comments
Closed

Using channels for events and closing #537

eNV25 opened this issue Jul 1, 2022 · 3 comments

Comments

@eNV25
Copy link
Contributor

eNV25 commented Jul 1, 2022

This is not a bug. It's a discussion and maybe a feature request.

I contributed the ChannelEvents method that redirects events from the internal channel to a user-created channel. #465

quit := make(chan struct{})
events := make(chan tcell.Event)

go screen.ChannelEvents(events, quit)

for ev := range events {
	handleEvent(ev)
	screen.Show()
}

This works pretty well, and I use it in my application, except when using a multi-producer setup. ChannelEvents closes the channel when quit is closed, and may cause a “send on closed channel” panic on other event producers. This doesn't happen often, and if it does, it's only during shutdown, so it's not a big issue.

In Go, typically sync.WaitGroup is used in such cases.

var wg sync.WaitGroup

quit := make(chan struct{})
events := make(chan tcell.Event)

wg.Add(2)

go screen.ChannelEvents(&wg, events, quit) // calls wg.Done
go moreChannelEvents(&wg, events, quit) // calls wg.Done

go func() {
	wg.Wait()
	close(events)
}()

for ev := range events {
	handleEvent(ev)
	screen.Show()
}

In this case, each event producer would call wg.Done() instead of closing the channel or simply returning when quit is closed. Another goroutine would wait and close events when all event producers finish.

To do this in tcell would mean adding another method to Screen since it is incompatible with the existing one.

I thought another option would be exporting the channel so that the user can implement ChannelEvents in whatever way they want (I vaguely remember exporting events channel mentioned in another issue, but I couldn't find it).

I guess a workaround would be to wrap the existing ChannelEvents with another channel in the middle.

wg.Add(1)
go func() {
	defer wg.Done()
	middle := make(chan tcell.Event)
	go screen.ChannelEvents(middle, quit)
	for ev := range middle {
		select {
		case <-quit:
			return
		case events <- ev:
		}
	}
}()
@eNV25
Copy link
Contributor Author

eNV25 commented Jul 2, 2022

In Go, typically sync.WaitGroup is used in such cases.

It looks like it still sometimes panics, even with this. I am using context.WithCancel and ctx.Done and cancel() instead of quit chan struct{}, maybe that is an issue.

@eNV25
Copy link
Contributor Author

eNV25 commented Jul 2, 2022

Since I couldn't find a good solution, I just ended up using defer func() { _ = recover() }() before every send to events.

@eNV25 eNV25 closed this as completed Jul 2, 2022
@eNV25
Copy link
Contributor Author

eNV25 commented Jul 6, 2022

@gdamore

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant