Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Hi Kasper,
First of all, I know I mentioned it often but PLEASE try to use earlie return, it is a standard pattern and makes code much faster to reason about, also it shows where functions end without scrolling to the end of them. Keep in mind that code is never only read by oneself, and even oneself benefits massively from having easy to read code in a couple of weeks time
Secondly: Your .Iter function gets you in major trouble, as it passes a complex object and then marshals it for as many times as there are clients on individual go routines. That is not only inefficient, but also causes more potential datarace headaches than needed. I have changed it to a global broadcast function that does the marshaling once and then passes the bytes to the individual ws connections. Bytes are straight forward and safe to copy, and the expensive marshal function is only called once, in a syncronous fashion, meaning it is protected by the mutex that should still be RLocked in the function that calls Broadcast. Also it is easier to read than the function passing + channel operation construct
Finally we can remove the very expensive Copier calls and library. Copier uses reflections inside and should really only ever be used as a very last resort. (or even never, as it could potentially make things worse like it did here)
Also then the Mutex on the individual entries can be removed, as in every situation one would need to lock the parent mutex on .entries anyways. Removing this spares you from several dozends of locks and unlocks that are redundant.
As a final point: using RWMutexes instead of the normal ones can give some advantages (there can be multiple RLocks at the same time but only ever one Lock used for writing)
Hope this helps, let me know if you have more questions