Skip to content

proposal: atomic keyword #14977

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

Closed
OneOfOne opened this issue Mar 26, 2016 · 4 comments
Closed

proposal: atomic keyword #14977

OneOfOne opened this issue Mar 26, 2016 · 4 comments

Comments

@OneOfOne
Copy link
Contributor

  • disclaimer: I'm horrible at writing essays and proposals, so take this with a grain of salt.

Abstract

With the growing number of processor cores, many developers use atomics rather than mutex/cond when they can.

Background

For primitive types, we have sync/atomic, which provides Load/Store/Swap/CAS, however, for other types we only have atomic.Value, which only provides Load/Store and the added overhead of runtime type assertion since it uses interface{}

Proposal

Here comes the atomic keyword (more like chan, but it can be something else really):

v := make(atomic *SomeStruct)
ov := v.Swap(&SomeStruct)
if v.CompareAndSwap(nil, &SomeStruct) { }

v2 := v.Load()
v.Store(v3)
// or maybe?
v2 := v // implicit v.Load()?
v := v3 // implicit v.Store(v3)

Implementation

It'd be implemented much like atomic.Value internally, probably using a spinlock for CAS and Swap operations, with the added runtime magic to handle types directly instead of using interface{}.

Ideas? Feedback?

@randall77
Copy link
Contributor

You can implement those operations using unsafe and the *Pointer varieties of sync/atomic.

type T struct {
    x, y, z int  // or whatever
}
type atomicTPtr struct {
    p *T
}
func (a *atomicTPtr) Swap(p *T) *T {
    type internalAtomicTPtr struct {
        p unsafe.Pointer
    }
    x := (*internalAtomicTPtr)(unsafe.Pointer(a))
    y := unsafe.Pointer(p)
    z := atomic.SwapPointer(&x.p, y)
    return (*T)(z)
}

So I'm not convinced this is worth a new keyword. The above code is tricky, but anyone (successfully) messing with atomics is an expert.

@OneOfOne
Copy link
Contributor Author

@randall77 I have few counter points:

  1. Not everyone trying to squeeze some performance are an expert in atomics.
  2. Your example (which is great and I just learned from it and gonna impl it for runtime: lock-free channels #8899), it'd have to be copied around, also it wouldn't work for non-pointer types for CAS.
  3. One of the main selling points of go is the amazing concurrency, adding general atomic support would be a nice addition.

One more thing, It doesn't really have to be a keyword, it can be a type, for example n := atomic.ValueTo(Node{0}), just trying to get a general impl for Swap/CAS.

@minux
Copy link
Member

minux commented Mar 27, 2016

I see several problems with this proposal:

  1. we don't want to promote sync/atomic, certainly not at
    language level.

Please note the first statement after the package synopsis
for package sync/atomic says:

"These functions require great care to be used correctly.
Except for special, low-level applications, synchronization
is better done with channels or the facilities of the sync
package. Share memory by communicating; don't communicate
by sharing memory."

Making atomics types easier to use is false sense of
simplicity. Their correct use still relies on the fact that
the user knows what he/she is doing. As soon as the
user introduces two atomic variables with interrelations,
the programs becomes very very complicated, and
this kind of problems are very hard to find, and there
are no tools to help the user, short of using a formal
verifier like SPIN. The race detector won't help here.

  1. Go never has any types that have builtin methods.
  2. Adding atomic to the type system will complicate
    the type system a lot.

What about "atomic [1024]byte" that is impossible to
implement? Having atomic keyword in the spec means
we must be precise which types can be used with atomic
and that is architecture or even platform dependent. We
certainly don't want to say every platform must implement
64-bit atomics (actually it's impossible to implement 64-bit
atomics efficiently on certain platforms.)

Also, we need to be careful what it means to pass atomic
types to functions. For example,

This is well understood:
var x atomic int32
x++

But, given these function declarations:
func f1(int32)
func f2(atomic int32)
func f3(_int32)
func f4(_atomic int32)
func f5(atomic *int32)

which of the functions could you pass x to?

Actually, this form of atomic reminds me of the const modifier
in C, consider:
atomic int
atomic _int
*atomic int
*atomic *int
atomic *atomic *int
atomic *_int
atomic *_atomic int
*_atomic int
.....

@ianlancetaylor
Copy link
Contributor

This isn't going to be adopted. I'm going to close this issue.

I don't know whether there is a problem here that needs to be addressed in Go. I am quite certain that adding an atomic type qualifier is not the answer. Experience with C++ shows that few people can program correctly with atomic types, and detecting errors is very difficult. This is not a road that Go should go down. And if it does go down them, it should not do so by using a type qualifier.

@golang golang locked and limited conversation to collaborators Mar 27, 2017
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

5 participants