Full_Int
Full_Int

Reputation: 429

Atomic compare and swap in assembler OS independent

I am writing OS independent lockless queue, queue works fine (with default windows\linux atomic CAS), but I want to make queue work without any change in linux and windows. So I gues I should write my own CAS in assembler, or there is other ways to achieveit ?

So far I have this code:

global _CAS

_CAS:

    mov eax, [esp+4]    
    mov edx, [esp+8]    
    mov ecx, [esp+12]   

    ;XCHG eax, edx  <- this function works fine
     CMPXCHG  ecx, eax, ebx 

    ret

I know that I still have to add lock before CMPXCHG and it should work with structures, but for now I'm more interested in making it working.

I compile using nasm-2.10.05 with this command nasm.exe -f elf -o cas.o cas.asm and I get this error testas\cas.asm:14: error: invalid combination of opcode and operands

Upvotes: 3

Views: 3203

Answers (2)

Dietrich Epp
Dietrich Epp

Reputation: 213308

With C11 or newer, you should be using <stdatomic.h>, which has broad support these days:

#include <stdatomic.h>

See atomic_compare_exchange. I won't try and summarize the exact semantics here.

Without C11, you can do it with intrinsics on both platforms, no need for assembly:

#ifdef _MSC_VER
# include <intrin.h>
# define CAS(ptr, oldval, newval) \
    _InterlockedCompareExchange(ptr, newval, oldval)
#elif __GNUC__
# if __GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 1)
#  error "requires GCC 4.1 or greater"
# endif
# define CAS(ptr, oldval, newval) \
    __sync_val_compare_and_swap(ptr, oldval, newval)
#else
# error "CAS not supported on this platform"
#endif

Both of these support the following interface:

/* If the current value of *ptr is oldval, then write newval
   into *ptr.  Returns the initial value of *ptr.  */
long CAS(long *ptr, long oldval, long newval);

See MSDN documentation, GCC documentation. This should also work on Clang with no modification, since Clang advertises __GNUC__ compatibility with its predefined macros.

Upvotes: 14

Related Questions