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

Disallow comparisons #72

Closed
abhinav opened this issue May 11, 2020 · 1 comment · Fixed by #74
Closed

Disallow comparisons #72

abhinav opened this issue May 11, 2020 · 1 comment · Fixed by #74

Comments

@abhinav
Copy link
Collaborator

abhinav commented May 11, 2020

Currently, it's possible to do,

x := atomic.NewInt32(1)
y := atomic.NewInt32(2)

fmt.Println(*x == *y)

This accesses the value of the integer in a non-atomic way.

We should consider disabling this by adding a _ [0]func() field to the
atomic structs. This would add no runtime cost and disable struct-level
comparison of the values. (Ref: https://github.com/go4org/mem/blob/3dbcd07079b881f9bedb802b4099856c1e267fa1/mem.go#L42.)

Worth considering that this could constitute a breaking change. An argument
for in favor of the change is that the comparison was incorrect usage in the
first place, so it's not breaking to disallow it.

@prashantv
Copy link
Collaborator

Not allowing comparisons is a good idea.

An argument for in favor of the change is that the comparison was incorrect usage in the first place, so it's not breaking to disallow it.

Agree.

abhinav added a commit that referenced this issue May 12, 2020

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
It is currently possible to make non-atomic comparisons of atomic
values:

    x := atomic.NewInt32(1)
    y := atomic.NewInt32(1)
    fmt.Println(*x == *y)

This is undesirable because it loads the value in a non-atomic way. The
correct method is,

    x := atomic.NewInt32(1)
    y := atomic.NewInt32(1)
    fmt.Println(x.Load() == y.Load())

To prevent this, disallow comparison of atomic values by embedding an
uncomparable array into atomics. The empty array adds no runtime cost,
and does not increase the in-memory size of the structs. Inspired by
[go4.org/mem#RO][1].

  [1]: https://github.com/go4org/mem/blob/3dbcd07079b881f9bedb802b4099856c1e267fa1/mem.go#L42

Note that the Value struct, which embeds `"sync/atomic".Value` was also
changed. This will break usages in the following form, but that's
acceptable because unkeyed struct literals are among the exceptions
stated in the [Go 1 compatibility expectations][2].

    import (
      syncatomic "sync/atomic"
      "go.uber.org/atomic"
    )

    atomic.Value{syncatomic.Value{..}}

  [2]: https://golang.org/doc/go1compat#expectations

Resolves #72
abhinav added a commit that referenced this issue May 12, 2020
It is currently possible to make non-atomic comparisons of atomic
values:

    x := atomic.NewInt32(1)
    y := atomic.NewInt32(1)
    fmt.Println(*x == *y)

This is undesirable because it loads the value in a non-atomic way. The
correct method is,

    x := atomic.NewInt32(1)
    y := atomic.NewInt32(1)
    fmt.Println(x.Load() == y.Load())

To prevent this, disallow comparison of atomic values by embedding an
uncomparable array into atomics. The empty array adds no runtime cost,
and does not increase the in-memory size of the structs. Inspired by
[go4.org/mem#RO][1].

  [1]: https://github.com/go4org/mem/blob/3dbcd07079b881f9bedb802b4099856c1e267fa1/mem.go#L42

Note that the Value struct, which embeds `"sync/atomic".Value` was also
changed. This will break usages in the following form, but that's
acceptable because unkeyed struct literals are among the exceptions
stated in the [Go 1 compatibility expectations][2].

    import (
      syncatomic "sync/atomic"
      "go.uber.org/atomic"
    )

    atomic.Value{syncatomic.Value{..}}

  [2]: https://golang.org/doc/go1compat#expectations

Resolves #72
abhinav added a commit that referenced this issue May 12, 2020
It is currently possible to make non-atomic comparisons of atomic
values:

    x := atomic.NewInt32(1)
    y := atomic.NewInt32(1)
    fmt.Println(*x == *y)

This is undesirable because it loads the value in a non-atomic way. The
correct method is,

    x := atomic.NewInt32(1)
    y := atomic.NewInt32(1)
    fmt.Println(x.Load() == y.Load())

To prevent this, disallow comparison of atomic values by embedding an
uncomparable array into atomics. The empty array adds no runtime cost,
and does not increase the in-memory size of the structs. Inspired by
[go4.org/mem#RO][1].

  [1]: https://github.com/go4org/mem/blob/3dbcd07079b881f9bedb802b4099856c1e267fa1/mem.go#L42

Note that the Value struct, which embeds `"sync/atomic".Value` was also
changed. This will break usages in the following form, but that's
acceptable because unkeyed struct literals are among the exceptions
stated in the [Go 1 compatibility expectations][2].

    import (
      syncatomic "sync/atomic"
      "go.uber.org/atomic"
    )

    atomic.Value{syncatomic.Value{..}}

  [2]: https://golang.org/doc/go1compat#expectations

Resolves #72
abhinav added a commit that referenced this issue May 12, 2020
It is currently possible to make non-atomic comparisons of atomic
values:

    x := atomic.NewInt32(1)
    y := atomic.NewInt32(1)
    fmt.Println(*x == *y)

This is undesirable because it loads the value in a non-atomic way. The
correct method is,

    x := atomic.NewInt32(1)
    y := atomic.NewInt32(1)
    fmt.Println(x.Load() == y.Load())

To prevent this, disallow comparison of atomic values by embedding an
uncomparable array into atomics. The empty array adds no runtime cost,
and does not increase the in-memory size of the structs. Inspired by
[go4.org/mem#RO][1].

  [1]: https://github.com/go4org/mem/blob/3dbcd07079b881f9bedb802b4099856c1e267fa1/mem.go#L42

Note that the Value struct, which embeds `"sync/atomic".Value` was also
changed. This will break usages in the following form, but that's
acceptable because unkeyed struct literals are among the exceptions
stated in the [Go 1 compatibility expectations][2].

    import (
      syncatomic "sync/atomic"
      "go.uber.org/atomic"
    )

    atomic.Value{syncatomic.Value{..}}

  [2]: https://golang.org/doc/go1compat#expectations

Resolves #72
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 a pull request may close this issue.

2 participants