Skip to content

Conversation

@benjaminjkraft
Copy link
Collaborator

Alternate approach to fixing #402 -- I haven't fully checked if I think this is correct yet either but figured it was at least useful to put up as a sketch for discussion in #407.

TODO: changelog, do we want the integration test changes from #407 as well

I have:

  • Written a clear PR title and description (above)
  • Signed the Khan Academy CLA
  • Added tests covering my changes, if applicable
  • Included a link to the issue fixed, if applicable
  • Included documentation, for new features
  • Added an entry to the changelog

Copy link
Contributor

@HaraldNordgren HaraldNordgren left a comment

Choose a reason for hiding this comment

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

Sounds reasonable. However, the actual problem has been hard for me to reproduce consistently.

I would suggest re-running all the CI 10 times, and if it passes all of those then I would consider it solved, I was never able to get 10 passes in a row (for Go 1.23, 1.24, 1.25 so 30 test runs) to pass.

Easy way to re-trigger all the CI: git commit --amend --no-edit --date="now" && git push -f

if w.isClosing {
w.Lock()
isClosing := w.isClosing
w.Unlock()
Copy link
Contributor

Choose a reason for hiding this comment

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

Isn't it better to always follow this pattern?

w.Lock()
defer w.Unlock()

isClosing := w.isClosing

Copy link
Member

Choose a reason for hiding this comment

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

I don't think that will work here since the lock is happening inside a loop. The defer won't run until the entire function exits, which means that the second time through the loop the Lock() would block since the lock is already being held from the first time.

You could make a separate function that just assigns w.isClosing, and use defer in that, but that's possibly overkill for this.

Copy link
Member

@csilvers csilvers left a comment

Choose a reason for hiding this comment

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

I'm not really qualified to review, but I appreciate being explicit about what functions need the lock held!

return false
}

// Mark as closed before actually closing to prevent double-close
Copy link
Member

Choose a reason for hiding this comment

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

Is this really needed given you're in a lock for this whole function? In any case, I don't think it hurts.

Comment on lines +147 to +170
w.Lock()
sub := w.subscriptions.get(wsMsg.ID)
if sub == nil {
w.Unlock()
return fmt.Errorf("received message for unknown subscription ID '%s'", wsMsg.ID)
}
if sub.hasBeenUnsubscribed {
if sub.closed {
// Already closed, ignore message
w.Unlock()
return nil
}

if wsMsg.Type == webSocketTypeComplete {
sub.hasBeenUnsubscribed = true
w.subscriptions.map_[wsMsg.ID] = sub
reflect.ValueOf(sub.interfaceChan).Close()
// Server is telling us the subscription is complete
w.subscriptions.closeChannel(wsMsg.ID)
w.subscriptions.delete(wsMsg.ID)
w.Unlock()
return nil
}

// Forward the data to the subscription channel.
// We release the lock while calling the forward function to avoid holding
// the lock while doing potentially slow user code.
w.Unlock()
Copy link
Member

Choose a reason for hiding this comment

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

In this case, I do agree that having to remember to unlock in all the exit paths is fragile. It may make sense to pull all this code out into its own function so you can use defer to close.

w.Lock()
defer w.Unlock()
w.subscriptions.closeChannel(subscriptionID)
w.subscriptions.delete(subscriptionID)
Copy link
Member

Choose a reason for hiding this comment

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

The old Unsubscribe didn't delete, if I'm reading the code right. Is this a bugfix?

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

Successfully merging this pull request may close these issues.

4 participants