Export (0) Print
Expand All
Expand Minimize
This topic has not yet been rated - Rate this topic

_InterlockedCompareExchange Intrinsic Functions

Microsoft Specific

Provides compiler intrinsic support for the Win32 Platform SDK InterlockedCompareExchange function.


long _InterlockedCompareExchange(
   long volatile * Destination,
   long Exchange,
   long Comperand
);
long _InterlockedCompareExchange_acq(
   long volatile * Destination,
   long Exchange,
   long Comperand
);
long _InterlockedCompareExchange_rel(
   long volatile * Destination,
   long Exchange,
   long Comperand
);
short _InterlockedCompareExchange16(
   short volatile * Destination,
   short Exchange,
   short Comperand
);
short _InterlockedCompareExchange16_acq(
   short volatile * Destination,
   short Exchange,
   short Comperand
);
short _InterlockedCompareExchange16_rel(
   short volatile * Destination,
   short Exchange,
   short Comperand
);
__int64 _InterlockedCompareExchange64(
   __int64 volatile * Destination,
   __int64 Exchange,
   __int64 Comperand
);
__int64 _InterlockedCompareExchange64_acq(
   __int64 volatile * Destination,
   __int64 Exchange,
   __int64 Comperand
);
__int64 _InterlockedCompareExchange64_rel(
   __int64 volatile * Destination,
   __int64 Exchange,
   __int64 Comperand
);

Parameters

[in, out] Destination

Pointer to the destination value. The sign is ignored.

[in] Exchange

Exchange value. The sign is ignored.

[in] Comperand

Value to compare to destination. The sign is ignored.

The return value is the initial value of the Destination pointer.

Intrinsic Architecture

_InterlockedCompareExchange

x86, IPF, x64

_InterlockedCompareExchange_acq

IPF

_InterlockedCompareExchange_rel

IPF

_InterlockedCompareExchange16

x86, IPF, x64

_InterlockedCompareExchange16_acq

IPF

_InterlockedCompareExchange16_rel

IPF

_InterlockedCompareExchange64

x86 (Pentium)*, IPF, x64

_InterlockedCompareExchange64_acq

IPF

_InterlockedCompareExchange64_rel

IPF

Note: _InterlockedCompareExchange64 is available on x86 systems running on any Pentium architecture; it is not available on 386 or 486 architectures.

Header file <intrin.h>

Note that to generate the intrinsic expansion, you need to use /Oi. /Oi is implied with /O2.

To declare one of the interlocked functions for use as an intrinsic, the function must be declared with the leading underscore and the new function must appear in a #pragma intrinsic statement. For convenience, the intrinsic versions of the functions can be declared in a #define statement to appear in the source code without the leading underscore.

_InterlockedCompareExchange performs an atomic comparison of the Destination value with the Comperand value. If the Destination value is equal to the Comperand value, the Exchange value is stored in the address specified by Destination. Otherwise, no operation is performed.

For a sample of how to use _InterlockedCompareExchange, see _InterlockedDecrement.

There are several variations on _InterlockedCompareExchange that vary based on the data types they involve and whether processor-specific acquire or release semantics is used.

While the _InterlockedCompareExchange function operates on long integer values, _InterlockedCompareExchange16 operates on short integer values and _InterlockedCompareExchange64 operates on 64-bit integer values. Because _InterlockedCompareExchange64 uses the cmpxchg8b instruction, it is not available on pre-Pentium processors, such as the 486.

The IPF-specific _InterlockedCompareExchange_acq, _InterlockedCompareExchange16_acq, and _InterlockedCompareExchange64_acq intrinsic functions are the same as the corresponding functions without the acq suffix except that the operation is performed with acquire semantics, which is useful when entering a critical section.

The _InterlockedCompareExchange_rel, _InterlockedCompareExchange16_rel, and _InterlockedCompareExchange64_rel intrinsic functions are the same as the corresponding functions without the rel suffix except that the operation is performed with release semantics, which is useful when leaving a critical section.

In Visual C++ 2005, these functions behave as read-write memory barriers. For more information, see _ReadWriteBarrier.

These routines are only available as intrinsics.

In the following example, _InterlockedCompareExchange is used for simple low-level thread synchronization. The approach has its limitations as a basis for multithreaded programming; it is presented to illustrate the typical use of the interlocked intrinsics. For best results, use the Windows API. For further information about multithreaded programming, see Writing a Multithreaded Win32 Program.

// intrinExample.cpp
// compile with: /EHsc /O2
// Simple example of using _Interlocked* intrinsics to
// do manual synchronization
//
// Add [-DSKIP_LOCKING] to the command line to disable
// the locking. This will cause the threads to execute out
// of sequence.
 
#include "windows.h"
 
#include <iostream>
#include <queue>
#include <intrin.h>
 
using namespace std;
 
// --------------------------------------------------------------------
 
// if defined, will not do any locking on shared data
//#define SKIP_LOCKING
 
// A common way of locking using _InterlockedCompareExchange.
// Please refer to other sources for a discussion of the many issues
// involved. For example, this particular locking scheme performs well 
// when lock contention is low, as the while loop overhead is small and
// locks are acquired very quickly, but degrades as many callers want
// the lock and most threads are doing a lot of interlocked spinning.
// There are also no guarantees that a caller will ever acquire the
// lock.
namespace MyInterlockedIntrinsicLock
{
    typedef unsigned LOCK, *PLOCK;
 
#pragma intrinsic(_InterlockedCompareExchange, _InterlockedExchange)
 
    enum {LOCK_IS_FREE = 0, LOCK_IS_TAKEN = 1};
 
    void Lock(PLOCK pl) 
    {
#if !defined(SKIP_LOCKING)
        // If *pl == LOCK_IS_FREE, it is set to LOCK_IS_TAKEN
        // atomically, so only 1 caller gets the lock.
        // If *pl == LOCK_IS_TAKEN,
        // the result is LOCK_IS_TAKEN, and the while loop keeps spinning.
        while (_InterlockedCompareExchange((long *)pl,
                                           LOCK_IS_TAKEN, // exchange
                                           LOCK_IS_FREE)  // comparand
               == LOCK_IS_TAKEN)
        {
            // spin!
            // call __yield() here on the IPF architecture to improve
            // performance.
        }
        // This will also work.
        //while (_InterlockedExchange(pl, LOCK_IS_TAKEN) == 
        //                             LOCK_IS_TAKEN)
        //{
        //    // spin!
        //}
 
        // At this point, the lock is acquired.
#endif
    }
 
    void Unlock(PLOCK pl) {
#if !defined(SKIP_LOCKING)
        _InterlockedExchange((long *)pl, LOCK_IS_FREE);
#endif
    }
}
 
// ------------------------------------------------------------------
 
// Data shared by threads
 
queue<int> SharedQueue;
MyInterlockedIntrinsicLock::LOCK SharedLock;
int TicketNumber;
 
// ------------------------------------------------------------------
 
DWORD WINAPI
ProducerThread(
    LPVOID unused
    )
{
    while (1) {
        // Acquire shared data. Enter critical section.
        MyInterlockedIntrinsicLock::Lock(&SharedLock);
 
        //cout << ">" << TicketNumber << endl;
        SharedQueue.push(TicketNumber++);
 
        // Release shared data. Leave critical section.
        MyInterlockedIntrinsicLock::Unlock(&SharedLock);
 
        Sleep(rand() % 20);
    }
 
    return 0;
}
 
DWORD WINAPI
ConsumerThread(
    LPVOID unused
    )
{
    while (1) {
        // Acquire shared data. Enter critical section
        MyInterlockedIntrinsicLock::Lock(&SharedLock);
 
        if (!SharedQueue.empty()) {
            int x = SharedQueue.front();
            cout << "<" << x << endl;
            SharedQueue.pop();
        }
 
        // Release shared data. Leave critical section
        MyInterlockedIntrinsicLock::Unlock(&SharedLock);
 
        Sleep(rand()%20);
    }
    return 0;
}
 
 
int main(
    void
    )
{
    const int timeoutTime = 500;
    int unused1, unused2;
    HANDLE threads[4];
 
    // The program creates 4 threads:
    // two producer threads adding to the queue
    // and two consumers taking data out and printing it.
    threads[0] = CreateThread(NULL,
                              0,
                              ProducerThread,
                              &unused1,
                              0,
                              (LPDWORD)&unused2);
 
    threads[1] = CreateThread(NULL,
                              0,
                              ConsumerThread,
                              &unused1,
                              0,
                              (LPDWORD)&unused2);
 
    threads[2] = CreateThread(NULL,
                              0,
                              ProducerThread,
                              &unused1,
                              0,
                              (LPDWORD)&unused2);
 
    threads[3] = CreateThread(NULL,
                              0,
                              ConsumerThread,
                              &unused1,
                              0,
                              (LPDWORD)&unused2);
 
    WaitForMultipleObjects(4, threads, TRUE, timeoutTime);
 
    return 0;
}

Output

<0
<1
<2
<3
<4
<5
<6
<7
<8
<9
<10
<11
<12
<13
<14
<15
<16
<17
<18
<19
<20
<21
<22
<23
<24
<25
<26
<27
<28
<29
Did you find this helpful?
(1500 characters remaining)
Thank you for your feedback

Community Additions

ADD
Show:
© 2014 Microsoft. All rights reserved.