_ReadWriteBarrier 

Microsoft Specific

Effectively blocks an optimization of reads and writes to global memory.


void _ReadWriteBarrier(void);

Intrinsic Architecture

_ReadWriteBarrier

x86, IPF, x64

Header file <intrin.h>

Blocking the optimization of reads and writes to global memory can be useful to ensure the state of global variables at a particular point in your code for multithreaded applications.

The Visual C++ compiler is free to perform any optimization that preserves the meaningful outputs of the program in single-threaded execution. These barriers are provided to block optimization of reads and writes at specific points in a program. This is similar to marking that memory "volatile" only more performant (when usable) because it only forces reads and writes to complete at specific points in a program, rather than globally. The optimizer is free to optimize accesses to these variables between the barriers. These optimizations cannot occur if the variable is declared volatile.

_ReadWriteBarrier forces memory reads and writes to complete at the point of the call. This ensures that the code generated does not rely on the values of those variables after the call to _ReadWriteBarrier and that any pending writes to those variables is done at the time of the call. After a call to _ReadWriteBarrier, other threads can freely read or modify the variables without fear that the memory might still be read or modified by the thread with the barrier. The call to _ReadWriteBarrier is referred to as a read-write memory barrier. Other memory barriers include _ReadBarrier and _WriteBarrier. _ReadWriteBarrier combines the effects of both _ReadBarrier and _WriteBarrier.

Note   In past versions of the compiler, _ReadWriteBarrier was enforced only locally and did not affect functions up the call tree. In Visual C++ 2005, _ReadWriteBarrier is enforced all the way up the call tree. The results of the following example applies only to the current Visual C++ compiler.

For performance reasons, not all variables are affected by memory barriers. Only global memory reads and writes are subject to read-write barriers. Local variables are not accessible to other threads and therefore do not need to be protected.

Memory is considered global (as applicable to memory barriers) if the data satisfies one of the following conditions:

  • It is a global variable.

  • It is a local variable used in a __try, __except, or __finally block (or a catch block if asynchronous exception handling is used). For more information, see the /EHa compiler option.

  • It is a local volatile variable.

  • It is a local variable whose address escapes current function in some way. For example, the variable is passed by reference to another function or its address is assigned to a global variable.

  • It is accessed indirectly through a pointer p and *p satisfies one of the previous conditions. The most typical such case is *p where p is a global variable or parameter.

Reads and writes of the memory that is not global according the definition given above are not affected by memory barriers. The optimizer can move such reads and writes across these barriers. The foregoing discussion applies to all memory barriers, including those associated with the interlocked intrinsics. The documentation for each intrinsic indicates whether it operates as a memory barrier.

// intrinsics_readwritebarrier.c
// compile with: /O2 -DNO_BARRIER
// This code contains an error--dereferencing a null pointer--
// which will be optimized away as a useless assignment.
// Omit the NO_BARRIER command line to activate the Write Barrier.
// With the barrier activated, the assignment is not optimized away
// and causes an access violation.

#include <windows.h> // for EXCEPTION_ACCESS_VIOLATION
#include <excpt.h>
#include <stdio.h>
#include <intrin.h>

#pragma intrinsic(_ReadWriteBarrier)

int x = 0;

__declspec(noinline) int f(int* p)
{
    x = *p;
#ifndef NO_BARRIER
    _ReadWriteBarrier();
#endif
    x = 7;
    return x;
}


// If code is EXCEPTION_ACCESS_VIOLATION it should be the
// attempt to read from the NULL pointer we passed in, so
// we handle the exception.
int filter(unsigned int code, struct _EXCEPTION_POINTERS *ep)
{
    if (code == EXCEPTION_ACCESS_VIOLATION)
    {
        printf_s("AV\n");
        return EXCEPTION_EXECUTE_HANDLER;
    }

    // If not what we were looking for, we don't want to handle it.
    return EXCEPTION_CONTINUE_SEARCH;
}

int main()
{
    int nRet = 0;

    __try
    {
        // Should only return if the first assignment is
        // optimized away.
        nRet = f(NULL);
        printf_s("Assignment was optimized away!\n");
    }
    __except(filter(GetExceptionCode(), GetExceptionInformation()))
    {
        // We get here if an Access violation occurred.
        printf_s("Access Violation: assignment was not optimized away.\n");
    }
}

Output

Assignment was optimized away!
Show: