On x86-32, the 64-bit functions use instructions unavailable before the Pentium MMX.
On non-Linux ARM, the 64-bit functions use instructions unavailable before the ARMv6k core.
On both ARM and x86-32, it is the caller's responsibility to arrange for 64-bit alignment of 64-bit words accessed atomically.
The first word in a global variable or in an allocated struct or slice can be relied upon to be 64-bit aligned.
-j
---j
// https://golang.org/src/sync/waitgroup.go?s=1857:1892#L20
type WaitGroup struct {
noCopy noCopy
// 64-bit value: high 32 bits are counter, low 32 bits are waiter count.
// 64-bit atomic operations require 64-bit alignment, but 32-bit
// compilers do not ensure it. So we allocate 12 bytes and then use
// the aligned 8 bytes in them as state.
state1 [12]byte
sema uint32
}
func (wg *WaitGroup) state() *uint64 {
if uintptr(unsafe.Pointer(&wg.state1))%8 == 0 {
return (*uint64)(unsafe.Pointer(&wg.state1))
} else {
return (*uint64)(unsafe.Pointer(&wg.state1[4]))
}
}
--
You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
Never mind; I just realized that a WaitGroup is not necessarily at the start of an allocation or global variable.
On Saturday, February 4, 2017 at 11:03:11 AM UTC+8, Matt Harden wrote:Never mind; I just realized that a WaitGroup is not necessarily at the start of an allocation or global variable.
Not very get it.
Do you mean WaitGroup will be embedded in other types?
So we can't use the 64bit atomic functions for all exported types in libraries?
On Fri, Feb 3, 2017 at 7:01 PM Matt Harden <matt....@gmail.com> wrote:Doesn't the statement "32-bit compilers to not ensure [64 bit alignment at the start of an allocation]" contradict the sync/atomic statement "The first word in a global variable or in an allocated struct or slice can be relied upon to be 64-bit aligned."?On Fri, Feb 3, 2017 at 6:44 AM Ian Lance Taylor <ia...@golang.org> wrote:On Fri, Feb 3, 2017 at 5:38 AM, T L <tapi...@gmail.com> wrote:
> Why does WaitGroup.state method check 64-bit alignment at run time?
> Why not make the state field as the first word of WaitGroup struct?
>
> // https://golang.org/src/sync/waitgroup.go?s=1857:1892#L20
>
> type WaitGroup struct {
>
> noCopy noCopy
>
> // 64-bit value: high 32 bits are counter, low 32 bits are waiter count.
>
> // 64-bit atomic operations require 64-bit alignment, but 32-bit
>
> // compilers do not ensure it. So we allocate 12 bytes and then use
>
> // the aligned 8 bytes in them as state.
Doesn't this comment explain the problem?
Ian
--
You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
--
You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts+unsubscribe@googlegroups.com.
On Sat, Feb 4, 2017 at 5:49 AM, T L <tapi...@gmail.com> wrote:On Saturday, February 4, 2017 at 11:03:11 AM UTC+8, Matt Harden wrote:Never mind; I just realized that a WaitGroup is not necessarily at the start of an allocation or global variable.
Not very get it.
Do you mean WaitGroup will be embedded in other types?
So we can't use the 64bit atomic functions for all exported types in libraries?You can, but must make sure that they are aligned properly. Either like WaitGroup does, or, for example, by using the common idiom of factory functions that do the allocation (or by just putting it on the user to make sure).
The above program prints 64-bit aligned addresses on the playground, but that's a 64 bit platform (with 32 bit pointers). On a 32 bit platform x.j is not guaranteed to be 64-bit aligned. The same would be true if we substituted a WaitGroup for int64 in that example.
--
You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts+unsubscribe@googlegroups.com.
The spec says this: https://golang.org/ref/spec#Size_and_alignment_guaranteesNow, I can't really read from this whether this applies to fields or not. If it does, it would seem to me, that, indeed, WaitGroup wouldn't need to use the trick it does.For the example you name: It isn't particularly telling, as on nacl (at least on x86{,-64}, don't know a lot about ARM) pointers have 32 bits and so does int, meaning both rddeadline and wrdeadline are indeed 64-bit aligned.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.
--
You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts+unsubscribe@googlegroups.com.
I find this an inappropriate answer. Yes, they need care, but it should at least be possible to figure out how to use them from reading documentation (carefully). I tend to agree, that neither the spec nor the documentation of the atomic package is particularly helpful about these questions; at least I fail to read them in a way that unambiguously answers them.Without an authoritative and unambiguous description about what is guaranteed it is impossible to implement this robustly and if the spec isn't sufficiently unambiguous, it's natural to ask questions. Saying "you are not supposed to use them, unless you know how to use them" is, in context of all of this, just shutting people out.FWIW, I read the spec, Dave Cheney's blog post about padding, the therein referenced #599 and the atomic-documentation, I find the whole situation still somewhat confusing. To try to interpret all of this and answer OPs question:I believe, that fields do not necessarily need to be properly aligned (explaining 599). However, *structs* have a necessary alignment, as required by their fields. So, if you dotype S struct {A uint32B uint64}then there is no guarantee, that B is 64-bit aligned. The spec tells us, that unsafe.Alignof(s.A) will return 4 ("The functions Alignof and Sizeof take an expression x of any type and return the alignment or size, respectively, of a hypothetical variable v as if v was declared via var v = x", s.A is of type uint32 and such a variable has alignment 4), unsafe.Alignof(s.B) will return 8 and thus, unsafe.Alignof(s) will return 8 ("For a variable x of struct type: unsafe.Alignof(x) is the largest of all the values unsafe.Alignof(x.f) for each field f of x, but at least 1").This means, that the compiler needs to align S on 64 bits. Now, #599 tells us, that on some archs s.B will *not* be 64 bit aligned. This is in accordance with the spec though, as confusing as that seems; the spec says only, how a variable of the same type as s.B must be aligned, not how s.B itself must be aligned (this is, where the confusion stems from). So a compiler might or might not insert padding.What all of this means, I believe, is* The atomic-documentation (maybe?) tells you, that the first field in a struct can be relied upon to be aligned. It says "the first word", though, and from what I can tell, there is nothing preventing a compiler to insert arbitrary padding at the beginning of a struct, technically speaking.* Thus you can guarantee that s.B is 8-byte aligned, by switching the fields around* Thus, your question about the embedding of expvar.Float seems to be "yes, it's safe", as the struct itself must be 64-bit aligned, even if embedded, and the 64-bit field is the only one contained
* Your question about WaitGroup seems to be answerable by referring to the fact, that technically the noCopy field could misalign the data. But I tend to agree, that this reading would allow us to use a uint64, if we switch noCopy and state.
Dave Cheney's blog post about padding wouldn't create technical problems here, because noCopy isn't the last field, thus gets no storage allocated…* Also, all of that reading seems to suggest that there really is no reliable, guaranteed way, to have multiple fields with a guaranteed alignment. So, if you need multiple variables to be accessed by atomics, you are out of luck.
Now, all of that being said, my reading could also be wrong. In any case, in my opinion, the fact that there is considerable guesswork involved, seems to suggest, that the spec is either ambiguous or insufficient to actually allow safe usage of atomics. It would be nice, if it could be a bit more explicit about what happens to fields.
I understand, though, that we don't want to restrict too much the flexibility of a compiler to insert padding. But maybe a guarantee about the first field (not "word", what does that even mean?) of a struct being aligned, even the first fields (plural), if they all have the same alignment as the struct, would be clear enough *and* compatible with what compilers are already doing.
On Sat, Feb 4, 2017 at 3:31 PM, Lars Seipel <lars....@gmail.com> wrote:
On Sat, Feb 04, 2017 at 01:30:49AM -0800, T L wrote:
> Get it. This is quite bug prone to write programs on 64bit OSes and the
> programs are expected to run on 32bit OSes too.
Well, there's a reason the sync/atomic package docs have this statement
right at the top:
> 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.
--
You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.
--
You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.
Wait, I don't think you've explained why expvar.Float would be aligned even if embedded in another struct. I don't think that's guaranteed. The size and alignment guarantees in the spec only make guarantees about variables, not struct fields (even if they are themselves structs).
On Sat, Feb 4, 2017 at 7:33 AM, Axel Wagner
<axel.wa...@googlemail.com> wrote:
>
> I believe, that fields do not necessarily need to be properly aligned
> (explaining 599). However, *structs* have a necessary alignment, as required
> by their fields. So, if you do
> type S struct {
> A uint32
> B uint64
> }
> then there is no guarantee, that B is 64-bit aligned. The spec tells us,
> that unsafe.Alignof(s.A) will return 4 ("The functions Alignof and Sizeof
> take an expression x of any type and return the alignment or size,
> respectively, of a hypothetical variable v as if v was declared via var v =
> x", s.A is of type uint32 and such a variable has alignment 4),
> unsafe.Alignof(s.B) will return 8 and thus, unsafe.Alignof(s) will return 8
> ("For a variable x of struct type: unsafe.Alignof(x) is the largest of all
> the values unsafe.Alignof(x.f) for each field f of x, but at least 1").
The spec does not say that unsafe.Alignof(s.B) will return 8. In
fact, on 32-bit targets, with the gc toolchain, it will return 4.
I think the spec and docs do provide enough information to use
sync/atomic correctly. "The first word in a global variable or in an
allocated struct or slice can be relied upon to be 64-bit aligned."
That is sufficient to ensure that your code works correctly. It does
mean that certain kinds of operations don't work. sync.WaitGroup
plays the games it does to avoid introducing an extra allocation.
For the numeric types, the following sizes are guaranteed:
type size in bytes byte, uint8, int8 1 uint16, int16 2 uint32, int32, float32 4 uint64, int64, float64, complex64 8 complex128 16
The following minimal alignment properties are guaranteed:
x
of any type: unsafe.Alignof(x)
is at least 1.
x
of struct type: unsafe.Alignof(x)
is the largest of
all the values unsafe.Alignof(x.f)
for each field f
of x
, but at least 1.
x
of array type: unsafe.Alignof(x)
is the same as
unsafe.Alignof(x[0])
, but at least 1.
A struct or array type has size zero if it contains no fields (or
elements, respectively) that have a size greater than zero. Two distinct
zero-size variables may have the same address in memory.
The second is at the end of sync/atomic docs:
On x86-32, the 64-bit functions use instructions unavailable before the Pentium MMX.
On non-Linux ARM, the 64-bit functions use instructions unavailable before the ARMv6k core.
On both ARM and x86-32, it is the caller's responsibility to arrange for 64-bit
alignment of 64-bit words accessed atomically. The first word in a global
variable or in an allocated struct or slice can be relied upon to be
64-bit aligned.
I feel the two are not enough to remove all my confusions.
So could you help me remove the following confusions:
1. What does the "allocated struct or slice" mean?
Currently, I think it means the structs or slices created by new, or the structs or slices escape to heap.
Is my understanding right?
2. Are local 8-bytes variables 64bit aligned on 32bit OSes?
I found there are many usages of the 64bit functions of atomic package being used on local 8-bytes variables in go source.
So I think the answer is yes. Right?
3. Are expvar.Int and expvar.Float safe to be embedded in other structs on 32bit OSes?
I think the answer is no. Is my opinion right?
the sync/atomic docs, https://golang.org/pkg/sync/atomic/, says in the end of the docs
On x86-32, the 64-bit functions use instructions unavailable before the Pentium MMX.
On non-Linux ARM, the 64-bit functions use instructions unavailable before the ARMv6k core.
On both ARM and x86-32, it is the caller's responsibility to arrange for 64-bit alignment of 64-bit words accessed atomically.
The first word in a global variable or in an allocated struct or slice can be relied upon to be 64-bit aligned.
The last line says the first word in a global variable or in an allocated struct or slice is 64-bit aligned for sure.
But what does an allocated struct or slice means? A struct or slice allocated on heap, not stack?
On Thursday, February 2, 2017 at 12:03:59 AM UTC+8, T L wrote:the sync/atomic docs, https://golang.org/pkg/sync/atomic/, says in the end of the docsOn x86-32, the 64-bit functions use instructions unavailable before the Pentium MMX.
On non-Linux ARM, the 64-bit functions use instructions unavailable before the ARMv6k core.
On both ARM and x86-32, it is the caller's responsibility to arrange for 64-bit alignment of 64-bit words accessed atomically.
Does the "ARM" here include ARMv8 (64-bit)?
The first word in a global variable or in an allocated struct or slice can be relied upon to be 64-bit aligned.
The last line says the first word in a global variable or in an allocated struct or slice is 64-bit aligned for sure.
But what does an allocated struct or slice means? A struct or slice allocated on heap, not stack?
--
You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts+unsubscribe@googlegroups.com.
Arm means arm as in linux/arm.
-j
I find this an inappropriate answer. Yes, they need care, but it should at least be possible to figure out how to use them from reading documentation (carefully). I tend to agree, that neither the spec nor the documentation of the atomic package is particularly helpful about these questions; at least I fail to read them in a way that unambiguously answers them.Without an authoritative and unambiguous description about what is guaranteed it is impossible to implement this robustly and if the spec isn't sufficiently unambiguous, it's natural to ask questions. Saying "you are not supposed to use them, unless you know how to use them" is, in context of all of this, just shutting people out.FWIW, I read the spec, Dave Cheney's blog post about padding, the therein referenced #599 and the atomic-documentation, I find the whole situation still somewhat confusing. To try to interpret all of this and answer OPs question:
I believe, that fields do not necessarily need to be properly aligned (explaining 599). However, *structs* have a necessary alignment, as required by their fields. So, if you dotype S struct {A uint32B uint64}
then there is no guarantee, that B is 64-bit aligned. The spec tells us, that unsafe.Alignof(s.A) will return 4 ("The functions Alignof and Sizeof take an expression x of any type and return the alignment or size, respectively, of a hypothetical variable v as if v was declared via var v = x", s.A is of type uint32 and such a variable has alignment 4), unsafe.Alignof(s.B) will return 8 and thus, unsafe.Alignof(s) will return 8 ("For a variable x of struct type: unsafe.Alignof(x) is the largest of all the values unsafe.Alignof(x.f) for each field f of x, but at least 1").This means, that the compiler needs to align S on 64 bits. Now, #599 tells us, that on some archs s.B will *not* be 64 bit aligned. This is in accordance with the spec though, as confusing as that seems; the spec says only, how a variable of the same type as s.B must be aligned, not how s.B itself must be aligned (this is, where the confusion stems from). So a compiler might or might not insert padding.What all of this means, I believe, is* The atomic-documentation (maybe?) tells you, that the first field in a struct can be relied upon to be aligned. It says "the first word", though, and from what I can tell, there is nothing preventing a compiler to insert arbitrary padding at the beginning of a struct, technically speaking.* Thus you can guarantee that s.B is 8-byte aligned, by switching the fields around* Thus, your question about the embedding of expvar.Float seems to be "yes, it's safe", as the struct itself must be 64-bit aligned, even if embedded, and the 64-bit field is the only one contained* Your question about WaitGroup seems to be answerable by referring to the fact, that technically the noCopy field could misalign the data. But I tend to agree, that this reading would allow us to use a uint64, if we switch noCopy and state. Dave Cheney's blog post about padding wouldn't create technical problems here, because noCopy isn't the last field, thus gets no storage allocated…* Also, all of that reading seems to suggest that there really is no reliable, guaranteed way, to have multiple fields with a guaranteed alignment. So, if you need multiple variables to be accessed by atomics, you are out of luck.Now, all of that being said, my reading could also be wrong. In any case, in my opinion, the fact that there is considerable guesswork involved, seems to suggest, that the spec is either ambiguous or insufficient to actually allow safe usage of atomics. It would be nice, if it could be a bit more explicit about what happens to fields. I understand, though, that we don't want to restrict too much the flexibility of a compiler to insert padding. But maybe a guarantee about the first field (not "word", what does that even mean?) of a struct being aligned, even the first fields (plural), if they all have the same alignment as the struct, would be clear enough *and* compatible with what compilers are already doing.On Sat, Feb 4, 2017 at 3:31 PM, Lars Seipel <lars....@gmail.com> wrote:On Sat, Feb 04, 2017 at 01:30:49AM -0800, T L wrote:
> Get it. This is quite bug prone to write programs on 64bit OSes and the
> programs are expected to run on 32bit OSes too.
Well, there's a reason the sync/atomic package docs have this statement
right at the top:
> 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.
--
You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.
the sync/atomic docs, https://golang.org/pkg/sync/atomic/, says in the end of the docs
On x86-32, the 64-bit functions use instructions unavailable before the Pentium MMX.
On non-Linux ARM, the 64-bit functions use instructions unavailable before the ARMv6k core.
On both ARM and x86-32, it is the caller's responsibility to arrange for 64-bit alignment of 64-bit words accessed atomically.
The first word in a global variable or in an allocated struct or slice can be relied upon to be 64-bit aligned.