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

Atomic wrappers using Value incorrectly fail on CompareAndSwap #129

Closed
prashantv opened this issue Feb 6, 2023 · 1 comment
Closed

Atomic wrappers using Value incorrectly fail on CompareAndSwap #129

prashantv opened this issue Feb 6, 2023 · 1 comment

Comments

@prashantv
Copy link
Collaborator

prashantv commented Feb 6, 2023

While writing a fix + tests for #126, I realized where was a larger issue with how we're handling default values for wrapper-types. We have 2 representations for a default value: the underlying atomic Value is nil, or the atomic Value has the default value. However, having 2 representations causes issues for CompareAndSwap, since we only ever compare against the default value, so we'll never match the nil value.

This simple test shows the issue:

var s atomic.String
fmt.Println(s.CompareAndSwap("", "foo"))
// Prints: false. Expect true.

This can be worse if there's a loop using CompareAndSwap (typical way to use CompareAndSwap), since it will never succeed when the old value is read from Load(),

// s is an atomic.String.
// If s has never been set, the following loop will never end

for {
  old := s.Load()
  new := transform(old)
  if s.CompareAndSwap(old, new) {
    return nil
  }
}

The same above code will work for atomic.Int32 and other wrappers around int primitives. This is only an issue with Value primitives. From my testing, this affects String and Error, added in #111.

sywhang pushed a commit that referenced this issue Feb 6, 2023
* Regenerate code to update copyright end year to 2023

* Test behaviour of default values initialized in different ways

This adds repro tests for #126 and #129

* Fix Swap and CompareAndSwap for Value wrappers

Fixes #126, #129

All atomic types can be used without initialization, e.g., `var v
<AtomicType>`. This works fine for integer types as the initialized
value of 0 matches the default value for the user-facing type.  However,
for Value wrappers, they are initialized to `nil`, which is a value that
can't be set (triggers a panic) so the default value for the user-facing
type is forced to be stored as a different value. This leads to multiple
possible values representing the default user-facing type.

E.g., an `atomic.String` with value `""` may be represented by the
underlying atomic as either `nil`, or `""`. This causes issues when we
don't handle the `nil` value correctly, causing to panics in `Swap` and
incorrectly not swapping values in `CompareAndSwap`.

This change fixes the above issues by:
 * Requiring `pack` and `unpack` function in gen-atomicwrapper as the
   only place we weren't supplying them was for `String`, and the
   branching adds unnecessary complexity, especially with added `nil`
   handling.
 * Extending `CompareAndSwap` for `Value` wrappers to try an additional
   `CompareAndSwap(nil, <new>)` only if the original `CompareAndSwap`
   fails and the old value is the zero value.
@prashantv
Copy link
Collaborator Author

Fixed in #130.

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