atomic.Value : inconsistent types

451 views
Skip to first unread message

Stephen Illingworth

unread,
Nov 16, 2021, 7:25:15 AM11/16/21
to golang-nuts
When using atomic.Values it is important that the type being stored is consistent. Trying to store a different type in an atomic.Value will cause a panic

panic: sync/atomic: store of inconsistently typed value into Value

I've found that the way around this is to create a new instance of atomic.Value whenever I have reason to believe that the type to be stored has changed. In my program this can happen because I am storing an interface in the atomic.Value and the implementation of the interface may change.

Choosing the moment to create the new atomic.Value however can be tricky so I am now trying the following method:

1) Check the type of the existing stored value and the type of the new value
2) If they are the same then atomic.Value.Store() can be used
3) If they are not the same, then create a new instance of atomic.Value and store new value in that
4) Give the new atomic.Value the name of the old atomic.Value

This method means I no longer have to worry about when I create a new instance of atomic.Value - the new instance is created when the type check fails.

Example code.


Now, this obviously requires knowledge of how interface{} is represented internally, which isn't great, but in principle is this a correctly working solution? (It seems to be to me but that doesn't really mean anything)

Does returning an atomic.Value from a function count as "copying" after first use?

Is this going to bite me in the future?

Robert Engels

unread,
Nov 16, 2021, 8:00:57 AM11/16/21
to Stephen Illingworth, golang-nuts
I believe your code is broken. You need to use a lock when checking and making the switch (see double locking problem) or there would be no need to use atomics. 

If you run it under the race detector I am guessing it will fail. 

On Nov 16, 2021, at 6:25 AM, Stephen Illingworth <stephen.i...@gmail.com> wrote:


--
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.
To view this discussion on the web visit https://groups.google.com/d/msgid/golang-nuts/8893475a-ec61-4f78-9dc0-b80dda9e675an%40googlegroups.com.

Axel Wagner

unread,
Nov 16, 2021, 8:20:16 AM11/16/21
to Stephen Illingworth, golang-nuts
On Tue, Nov 16, 2021 at 1:25 PM Stephen Illingworth <stephen.i...@gmail.com> wrote:
I've found that the way around this is to create a new instance of atomic.Value whenever I have reason to believe that the type to be stored has changed.

That seems to be counter to the idea behind `atomic.Value`. Creating and setting the new `atomic.Value` requires synchronization. If you need synchronization anyway, it seems better to just use that synchronization to serialize *all* read/writes.
 
In my program this can happen because I am storing an interface in the atomic.Value and the implementation of the interface may change.

I think the best way to address that might be to store a pointer to an interface-type. That's similar to how you want to proceed if you want to get an interface-typed reflect.Value, so you do `reflect.ValueOf(&r).Elem()` (if `r` is an `io.Reader`, for example).
 
Choosing the moment to create the new atomic.Value however can be tricky so I am now trying the following method:

1) Check the type of the existing stored value and the type of the new value
2) If they are the same then atomic.Value.Store() can be used
3) If they are not the same, then create a new instance of atomic.Value and store new value in that
4) Give the new atomic.Value the name of the old atomic.Value

4 is, FTR, the part where this breaks. This assignment is not atomic.


This method means I no longer have to worry about when I create a new instance of atomic.Value - the new instance is created when the type check fails.

Example code.


Now, this obviously requires knowledge of how interface{} is represented internally, which isn't great, but in principle is this a correctly working solution? (It seems to be to me but that doesn't really mean anything)

Does returning an atomic.Value from a function count as "copying" after first use?

Is this going to bite me in the future?

--

Stephen Illingworth

unread,
Nov 16, 2021, 9:33:37 AM11/16/21
to golang-nuts
I've found that the way around this is to create a new instance of atomic.Value whenever I have reason to believe that the type to be stored has changed.

That seems to be counter to the idea behind `atomic.Value`. Creating and setting the new `atomic.Value` requires synchronization. If you need synchronization anyway, it seems better to just use that synchronization to serialize *all* read/writes.

Yes. I can see that now. The creation of the new atomic.Value could happen any time.

I've been leaning on the race detector and this technique has never triggered anything but that's probably just good fortune.
 
In my program this can happen because I am storing an interface in the atomic.Value and the implementation of the interface may change.

I think the best way to address that might be to store a pointer to an interface-type. That's similar to how you want to proceed if you want to get an interface-typed reflect.Value, so you do `reflect.ValueOf(&r).Elem()` (if `r` is an `io.Reader`, for example).

How about just a 'container' type for the interface.

https://play.golang.org/p/WSXVjVHj1Ya

For what I need, that does the job nicely. The type being stored in the atomic.Value isn't changing so it satisfies the atomic.Value constraints but I have the flexibility of the contained type being able to change. Any pitfalls with this?

Axel Wagner

unread,
Nov 16, 2021, 10:59:28 AM11/16/21
to Stephen Illingworth, golang-nuts
On Tue, Nov 16, 2021 at 3:33 PM Stephen Illingworth <stephen.i...@gmail.com> wrote:
How about just a 'container' type for the interface.

https://play.golang.org/p/WSXVjVHj1Ya

For what I need, that does the job nicely. The type being stored in the atomic.Value isn't changing so it satisfies the atomic.Value constraints but I have the flexibility of the contained type being able to change. Any pitfalls with this?

No, that should work fine. Though I'm not sure about "just", it requires an extra type but leads to exactly the same memory layout and everything. But yeah, if you prefer that, it's completely fine.
 

--
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.

Stephen Illingworth

unread,
Nov 16, 2021, 11:16:04 AM11/16/21
to golang-nuts

How about just a 'container' type for the interface.

https://play.golang.org/p/WSXVjVHj1Ya

For what I need, that does the job nicely. The type being stored in the atomic.Value isn't changing so it satisfies the atomic.Value constraints but I have the flexibility of the contained type being able to change. Any pitfalls with this?

No, that should work fine. Though I'm not sure about "just", it requires an extra type but leads to exactly the same memory layout and everything. But yeah, if you prefer that, it's completely fine.

I'm happy with it. It feels reasonably clean and there are no special conditions. Although I should refactor the code so that I don't need atomics at all and communicate over channels. But this just happens to be how the code has developed.

Thanks for the help.

Axel Wagner

unread,
Nov 16, 2021, 11:18:56 AM11/16/21
to Stephen Illingworth, golang-nuts
I actually thought about it and your way is strictly better, as it supports CAS (if all interface types involved do), which the pointer-method doesn't.

--
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.
Reply all
Reply to author
Forward
0 new messages