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

Introduce sync.Map wrapper with generics support #29452

Merged
merged 3 commits into from
Nov 30, 2023

Conversation

giorio94
Copy link
Member

sync.Map provides a map-like API that is safe for concurrent use by
multiple goroutines without additional locking or coordination. Its
main drawback, though, is that predates generics support in Go, which
means that all methods operate on keys/values of any type. Hence, it
is not type safe, and requires annoying type conversions.

This PR introduces a thin generic wrapper around it, allowing
consumers to specify the exact type of keys and values. Code comments
from the standard library are copied (and slightly adapted where
necessary) for users' convenience.

Additionally, it converts current usages over to it, and extends the
lock-check.sh linting script to additionally flag usages of sync.Map.

sync.Map provides a map-like API that is safe for concurrent use by
multiple goroutines without additional locking or coordination. Its
main drawback, though, is that predates generics support in Go, which
means that all methods operate on keys/values of any type. Hence, it
is not type safe, and requires annoying type conversions.

This commit introduces a thin generic wrapper around it, allowing
consumers to specify the exact type of keys and values. Code comments
from the standard library are copied (and slightly adapted where
necessary) for users' convenience.

Signed-off-by: Marco Iorio <[email protected]>
Following the introduction of the generic sync.Map wrapper, let's switch
current usages over to it, to benefit from the explicit key/value types.

Signed-off-by: Marco Iorio <[email protected]>
Extend the lock-check.sh linting script to additionally flag usages of
the sync.Map type, and suggest the usage of the generic wrapper.

Signed-off-by: Marco Iorio <[email protected]>
@giorio94 giorio94 added area/misc Impacts miscellaneous areas of the code not otherwise owned by another area. release-note/misc This PR makes changes that have no direct user impact. labels Nov 28, 2023
@giorio94
Copy link
Member Author

/test

@giorio94
Copy link
Member Author

Cilium Cluster Mesh upgrade failure looks caused by #29078. Rerunning

@giorio94 giorio94 marked this pull request as ready for review November 28, 2023 16:57
@giorio94 giorio94 requested review from a team as code owners November 28, 2023 16:57
Copy link
Contributor

@learnitall learnitall left a comment

Choose a reason for hiding this comment

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

Great idea! I am wondering if we need the MapCmpValues struct in addition to the Map struct here. What was the reasoning behind the split? From my understanding, if the CompareAnd{Swap,Delete} methods were attached to the Map struct instead, I don't think there would be any difference to users, since the corresponding sync.Map methods can already support taking a value with type any.

@giorio94
Copy link
Member Author

giorio94 commented Nov 29, 2023

Great idea! I am wondering if we need the MapCmpValues struct in addition to the Map struct here. What was the reasoning behind the split? From my understanding, if the CompareAnd{Swap,Delete} methods were attached to the Map struct instead, I don't think there would be any difference to users, since the corresponding sync.Map methods can already support taking a value with type any.

Yeah, I agree that the MapCmpValues struct looks a bit weird, and I don't like its name (but I couldn't come up with anything really better -- MapWithComparableValues could be an alternative but looks long, and ComparableMap sounds misleading -- I'm open to suggestions).

Overall the reasoning behind the split is that the CompareAnd{Swap,Delete} methods require also the values to be comparable (in addition to keys being comparable), and it seemed reasonable to enforce that through the generics constraints. Indeed, while the original sync.Map supports taking a value with type any also for these functions, it would then panic if values are not comparable. OTOH requiring values to be always comparable for all methods looks a no go, as it would limit the usability of the abstraction, preventing for instance to store references to functions.

Another alternative could be simply dropping the MapCmpValues struct (and the associated CompareAnd{Swap,Delete}) methods for the moment, as we are not using them anyway in any part of the code.

WDYT?

Copy link
Member

@qmonnet qmonnet left a comment

Choose a reason for hiding this comment

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

I only took a brief look at the changes in the agent, focused more on the script. Looks good to me, thanks

contrib/scripts/lock-check.sh Show resolved Hide resolved
Copy link
Contributor

@marseel marseel left a comment

Choose a reason for hiding this comment

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

Looks good!
I would probably drop MapCmpValues though.

@maintainer-s-little-helper maintainer-s-little-helper bot added the ready-to-merge This PR has passed all tests and received consensus from code owners to merge. label Nov 29, 2023
@giorio94 giorio94 added the dont-merge/discussion A discussion is ongoing and should be resolved before merging, regardless of reviews & tests status. label Nov 29, 2023
@learnitall
Copy link
Contributor

Great idea! I am wondering if we need the MapCmpValues struct in addition to the Map struct here. What was the reasoning behind the split? From my understanding, if the CompareAnd{Swap,Delete} methods were attached to the Map struct instead, I don't think there would be any difference to users, since the corresponding sync.Map methods can already support taking a value with type any.

Yeah, I agree that the MapCmpValues struct looks a bit weird, and I don't like its name (but I couldn't come up with anything really better -- MapWithComparableValues could be an alternative but looks long, and ComparableMap sounds misleading -- I'm open to suggestions).

Overall the reasoning behind the split is that the CompareAnd{Swap,Delete} methods require also the values to be comparable (in addition to keys being comparable), and it seemed reasonable to enforce that through the generics constraints. Indeed, while the original sync.Map supports taking a value with type any also for these functions, it would then panic if values are not comparable. OTOH requiring values to be always comparable for all methods looks a no go, as it would limit the usability of the abstraction, preventing for instance to store references to functions.

Another alternative could be simply dropping the MapCmpValues struct (and the associated CompareAnd{Swap,Delete}) methods for the moment, as we are not using them anyway in any part of the code.

WDYT?

Ah I didn't realize that it would panic 🤦. I'm on the same page now.

IMO, keeping the extra struct is probably a good idea. We could remove it for now, since no code uses it, but then we may have to reimplement it in the future anyways. Not a dealbreaker thoughl

@giorio94
Copy link
Member Author

IMO, keeping the extra struct is probably a good idea. We could remove it for now, since no code uses it, but then we may have to reimplement it in the future anyways. Not a dealbreaker thoughl

Yeah, I agree. Let's keep it there for the moment, we can always drop it afterwards.

@giorio94 giorio94 removed the dont-merge/discussion A discussion is ongoing and should be resolved before merging, regardless of reviews & tests status. label Nov 29, 2023
@youngnick youngnick added this pull request to the merge queue Nov 29, 2023
Merged via the queue into cilium:main with commit 573c7a6 Nov 30, 2023
43 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area/misc Impacts miscellaneous areas of the code not otherwise owned by another area. ready-to-merge This PR has passed all tests and received consensus from code owners to merge. release-note/misc This PR makes changes that have no direct user impact.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants