Ignore stale read errors from the race detector

965 views
Skip to first unread message

mikew...@gmail.com

unread,
Feb 22, 2016, 2:29:59 PM2/22/16
to golang-nuts

I've been trying to find a way to ignore errors from the golang race detector. In particular I have a stats loop that does stale reads from for a variable that is updated constantly. In this case I do not care if the value of the read is stale and I do not want to use atomic operations or locking to remove this race condition for performance reasons. Does anyone know if there is a way to add something to the lines of code that read this value to tell the race detector to ignore errors coming from a specific line of code?

Ian Lance Taylor

unread,
Feb 22, 2016, 2:44:46 PM2/22/16
to mikew...@gmail.com, golang-nuts
There is no way to disable the race detector, except to not use it.
This is intentional. The tools make no particular promises about
behavior in the presence of races. You are assuming a certain kind of
behavior, but it's not guaranteed to work.

Ian

Damian Gryski

unread,
Feb 22, 2016, 3:46:31 PM2/22/16
to golang-nuts, mikew...@gmail.com


On Monday, February 22, 2016 at 8:29:59 PM UTC+1, mikew...@gmail.com wrote:

I've been trying to find a way to ignore errors from the golang race detector. In particular I have a stats loop that does stale reads from for a variable that is updated constantly. In this case I do not care if the value of the read is stale and I do not want to use atomic operations or locking to remove this race condition for performance reasons. Does anyone know if there is a way to add something to the lines of code that read this value to tell the race detector to ignore errors coming from a specific line of code?

Nigel Tao

unread,
Feb 24, 2016, 7:07:29 PM2/24/16
to Damian Gryski, golang-nuts, mikew...@gmail.com
See also the "an aggressive compiler might delete the entire go
statement" and "there is no guarantee that the write to done will ever
be observed by main" examples at https://golang.org/ref/mem

Marvin Stenger

unread,
Feb 27, 2016, 8:29:03 AM2/27/16
to golang-nuts, dgr...@gmail.com, mikew...@gmail.com
What about these lines from runtime/chan.go for example:

    // Fast path: check for failed non-blocking operation without acquiring the lock.
    //
    // After observing that the channel is not closed, we observe that the channel is
    // not ready for sending. Each of these observations is a single word-sized read
    // (first c.closed and second c.recvq.first or c.qcount depending on kind of channel).
    // Because a closed channel cannot transition from 'ready for sending' to
    // 'not ready for sending', even if the channel is closed between the two observations,
    // they imply a moment between the two when the channel was both not yet closed
    // and not ready for sending. We behave as if we observed the channel at that moment,
    // and report that the send cannot proceed.
    //
    // It is okay if the reads are reordered here: if we observe that the channel is not
    // ready for sending and then observe that it is not closed, that implies that the
    // channel wasn't closed during the first observation.
    if !block && c.closed == 0 && ((c.dataqsiz == 0 && c.recvq.first == nil) ||
        (c.dataqsiz > 0 && c.qcount == c.dataqsiz)) {
        return false
    }

Although single word-sized reads/writes can lead to data races, we have hardware guarantees for them and therefore semantics.

Ian Lance Taylor

unread,
Feb 27, 2016, 5:39:31 PM2/27/16
to Marvin Stenger, golang-nuts, Damian Gryski, Mike Wiederhold
On Sat, Feb 27, 2016 at 5:29 AM, Marvin Stenger
<marvin.s...@gmail.com> wrote:
>
> What about these lines from runtime/chan.go for example:

The runtime package is always compiled by a known compiler, and is
permitted to know how that specific compiler behaves. If the compiler
changes, while still staying within spec, the runtime package will
change too. You can't argue about the language's memory model based
on the code in the runtime package.

Ian

haof...@gmail.com

unread,
Jun 7, 2017, 8:47:02 AM6/7/17
to golang-nuts, dgr...@gmail.com, mikew...@gmail.com
"there is no guarantee that the write to done will ever 
be observed by main", i am wondering why the write to done will ever 
be observed by main in detail?

Ian Lance Taylor

unread,
Jun 7, 2017, 10:12:55 AM6/7/17
to haof...@gmail.com, golang-nuts, Damian Gryski, Mike Wiederhold
On Tue, Jun 6, 2017 at 7:48 PM, <haof...@gmail.com> wrote:
>
> "there is no guarantee that the write to done will ever
> be observed by main", i am wondering why the write to done will ever
> be observed by main in detail?

I'm sorry, I don't understand the question.

The program in question, from https://golang.org/ref/mem, has a race
condition. The documentation is saying that the main function may or
may not see the store to "done" by the goroutine.

Ian

Jeff Learman

unread,
Jun 10, 2022, 1:23:06 PM6/10/22
to golang-nuts
Unlike C, Go has no `volatile` keyword.  Therefore, the compiler is allowed to cache a value that is never updated in the scope that it sees, and there's no way for us to tell it not to do that.  Issues like this aren't really data races, but they are caught by the race detector.

Jeff Learman

unread,
Jun 10, 2022, 1:23:06 PM6/10/22
to golang-nuts
Consider skipping specific tests when the race detector is active.  Alternatively (and dangerously, unless you understand the consequences), provide two implementations; one that is safe and is used when race detection is enabled, and another that is unsafe, and use build flags to control it:   testing - Can I skip a specific test when using the race detector? - Stack Overflow

Marvin Renich

unread,
Jun 11, 2022, 1:08:22 PM6/11/22
to golan...@googlegroups.com
* Jeff Learman <jeff.l...@gmail.com> [220610 13:23]:
> Unlike C, Go has no `volatile` keyword. Therefore, the compiler is allowed
> to cache a value that is never updated in the scope that it sees, and
> there's no way for us to tell it not to do that.

Actually, sync/atomic does essentially the same thing that volatile does
in C. Volatile ensures that the variable is not register-optimized,
that reads and writes to it are not optimized away when local code can
be proven to not observe those actions, and that reads and writes of
properly aligned values whose size is the machine word size happen
atomically (it also ensures proper alignment). sync/atomic does just
that.

For Microsoft C/C++, for architectures other than arm, volatile, by
default, also prevents reordering of reads and writes to non-volatile
variables around a read or write to a volatile variable. The ISO
definition of volatile explicitly does not require this, favoring the
std::atomic<T> as the mechanism to effect this.

There was discussion a number of years back (8 or more?) about whether
the Go Memory Model explicitly specified, implicitly specified, or did
not specify that sync/atomic provided a guarantee about not reordering
non-atomic reads and writes around atomic operations. The current
version of the memory models says under "Advice" near the beginning of
the document:

To serialize access, protect the data with channel operations or
other synchronization primitives such as those in the sync and
sync/atomic packages.

This sounds to me like an explicit declaration.

> Issues like this aren't
> really data races, but they are caught by the race detector.

Yes, they are. While amd64 and arm64 architectures will ensure, at the
CPU level, that a single instruction read or write to properly aligned
memory will always read or write the whole value atomically, not all
architectures do (especially many RISC architectures). Also, without
explicit memory barrier instructions, the amd64 (and I think arm64)
instructions will not guarantee observability to other cores.

You really should read the link Nigel gave below.

The OP said "I do not want to use atomic operations or locking to remove
this race condition for performance reasons." I believe the OP should
try sync/atomic. It should not give any statistically significant
performance difference from using the volatile keyword in C, as I would
expect C to use a memory barrier for volatile accesses so that other
cores and other hardware on the bus can observe them.

...Marvin
Reply all
Reply to author
Forward
0 new messages