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

Fix/ping interval panic #192

Closed
wants to merge 7 commits into from
Closed

Fix/ping interval panic #192

wants to merge 7 commits into from

Conversation

joshzcold
Copy link
Owner

@joshzcold joshzcold commented Feb 20, 2025

  • Remove calls to close client.send channel from Hub
  • Deprecate pingInterval feature as it causes issues handling of channels between goroutines and it complicates the buzzer feature more than it needs to be. Instead of account for latency, I think its fine to have the buzzer be "first come, first serve"
  • be sure to unregister a player client from the Hub when they quit
  • fix e2e-ui make target

Fixes: #191

I believe the client should be the one to close the channel since for
friendly feud we can have a client session without it necessarily being
connected to a hub.

If we are going to close this channel (which isn't necessary in go), I
think it should happening in client.go
…yers

We shouldn't send to a go channel in a goroutine that isn't the reciever
of that channel since it can cause an instance where that channel is
closed and we have a gorountine still wanting to send to the channel.

I.E

client.writePump (owns channel should be able to close it)

playerClient.pingInterval (outside and has chances to cause panic if
sends to closed channel)

I am opting to NOT close the client.send channel manually since I believe when
the client eventually disconnects and  the room Hub also disconnects the
go-lang garbage collector will close the channel when needed.
@joshzcold joshzcold added the bug Something isn't working label Feb 20, 2025
@joshzcold joshzcold requested a review from karlromets February 20, 2025 06:00
@joshzcold joshzcold self-assigned this Feb 20, 2025
Copy link

Test Results

27 tests   27 ✅  54s ⏱️
 8 suites   0 💤
 1 files     0 ❌

Results for commit 723555a.

Copy link
Collaborator

@karlromets karlromets left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great work on fixing the panic! Making the networking less complex is probably better to make the game more robust.

An alternative approach (if latency compensation becomes important again), which I think would work as well, could have kept the pingInterval but used a select statement with a default case within its loop

This should avoid the panic without completely removing the feature:

func (p *RegisteredClient) pingInterval() error {
	log.Println("Started ping for interval", p.id)
	ticker := time.NewTicker(5 * time.Second)
	defer func() {
		ticker.Stop()
	}()
	for {
		select {
		case <-ticker.C:
			player, ok := p.room.Game.RegisteredPlayers[p.id]
			if !ok {
				log.Println("Player not found stopping ping", p.id)
				return nil // Or, better, signal an error/unregister
			}
			player.Start = time.Now()
			message, err := NewSendPing(p.id)
			if err != nil {
				return fmt.Errorf(" %w", err)
			}
			// Attempt to send, but don't block if the channel is closed.
			select {
			case p.client.send <- message: // Try to send
			case <-p.stopPing: // Handle stopPing here
				log.Println("Stop ping via channel (during send)", p.id)
				return nil
			default: // Execute immediately if neither above is ready
				log.Println("Client send channel full or closed, skipping ping", p.id)
				// Consider unregistering the client here, as it's likely disconnected.
				// p.room.Hub.unregister <- p.client
			}
		case <-p.stopPing:
			log.Println("Stop ping via channel", p.id)
			return nil
		}
	}
}

@joshzcold
Copy link
Owner Author

joshzcold commented Feb 20, 2025

Great work on fixing the panic! Making the networking less complex is probably better to make the game more robust.

An alternative approach (if latency compensation becomes important again), which I think would work as well, could have kept the pingInterval but used a select statement with a default case within its loop

This should avoid the panic without completely removing the feature:

@karlromets I tried giving this a test and I don't think it would save us on a closed channel?
https://go.dev/play/p/91rF4pNBKEV

It seems like nil or closed channels in golang are more of a design bug than anything
https://stackoverflow.com/a/24096172/9329183

In our case if we were to do pingInterval again I believe that logic would have to sit next to the initialization of client.send in client.go, so that if we close() client.send we can graciously stop the pingInterval in the same goroutine as well.

Only problem is that in client.go it seems like it would be difficult to get game context of the current player we are accounting latencies for. Im sure its possible, but at that point I was thinking that the feature isn't really all that necessary anyways.

What do you think?

Apparently possible to get the extra goroutine to sync up with eachother if they are in a WaitGroup
https://pkg.go.dev/sync#WaitGroup

@joshzcold
Copy link
Owner Author

After talking to a co-worker who works in go-lang one pattern I like was passing down a shutdown channel to the multiple goroutines so that when we need to shutdown both the client and pingInterval they will both stop before the next invocation of client.send <- comes in

@karlromets
Copy link
Collaborator

Yeah my original approach was incorrect 😅

WaitGroup would be good

Even without pingInterval, using shutdown channels in writePump and readPump would improve the websocket handling and should actually fix the issue

I tried reproducing the issue/panic on goroutines-test. I tried delays and abrupt closures, making me think this is a super subtle timing issue, or maybe something specific to how RegisterBuzzer interacts with everything

Also tried opening 8 different browser tabs, but it seems that it's much difficult to reproduce on localhost...

Also context might be relevant

@joshzcold
Copy link
Owner Author

Closing in favor of #193

@joshzcold joshzcold closed this Feb 21, 2025
@joshzcold joshzcold deleted the fix/ping_interval_panic branch February 21, 2025 15:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Ping Interval Panic: send on closed channel
2 participants