Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
190 views
in Technique[技术] by (71.8m points)

c - Achieve window function InterlockedExchange in Linux

In this link achieve GCC cas function for version 4.1.2 and earlier I ask a question to use compare_and_swap function to achieve the Built-in function __sync_fetch_and_add here is my final code, run well in x86 and x64 (tested on CentOS 5.0 32bit and CentOS 7 64bit ).


Here is my code:

#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>

static unsigned long  count = 0;

int sync_add_and_fetch(int* reg, int oldval, int incre) 
{
    register char result;
#ifdef __i386__
    __asm__ volatile ("lock; cmpxchgl %3, %0; setz %1" 
                     : "=m"(*reg), "=q" (result) 
                     : "m" (*reg), "r" (oldval + incre), "a" (oldval) 
                     : "memory");
    return result;
#elif defined(__x86_64__)
    __asm__ volatile ("lock; cmpxchgq %3, %0; setz %1" 
                     : "=m"(*reg), "=q" (result) 
                     : "m" (*reg), "r" (newval + incre), "a" (oldval) 
                     : "memory");
    return result;
#else
    #error:architecture not supported and gcc too old
#endif

}


void *test_func(void *arg)
{
    int i = 0;
    int result = 0;
    for(i = 0; i < 2000; ++i)
    {
        result = 0;
        while(0 == result)
        {
            result = sync_add_and_fetch((int *)&count, count, 1);
        }
    }

    return NULL;
}

int main(int argc, const char *argv[])
{
    pthread_t id[10];
    int i = 0;

    for(i = 0; i < 10; ++i){
        pthread_create(&id[i], NULL, test_func, NULL);
    }

    for(i = 0; i < 10; ++i){
        pthread_join(id[i], NULL);
    }
    //10*2000=200000
    printf("%u
", count);

    return 0;
}

Now I have another question, how to implement function InterlockedExchange in Linux, InterlockedExchange just like the code above, have a __i386__ and __x86_64__ version. Just use the code above the parameter type not match, and maybe the assembly code will be rewritten.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

Exchange is easy; you don't have to return any status or anything. It xchg always swaps, so you just have to return the old value and/or a status like for cmpxchg.

static inline
int sync_xchg(int *p, int newval)
{
    // xchg mem,reg has an implicit lock prefix
    asm volatile("xchg %[newval], %[mem]"
        : [mem] "+m" (*p), [newval] "+r" (newval)  // read/write operands
        :
        : "memory" // compiler memory barrier, giving us seq-cst memory ordering
    );
    return newval;
}

It might be better to use int32_t, but int is 32 bits in all relevant ABIs. Also, this asm works for any size of integer. GCC will pick a 16-bit, 32-bit, or 64-bit register, and that will imply that operand-size for xchg. (You only need a suffix like addl when there's no register operand, e.g. lock; addl $1, (%0))

Anyway, I tested this and it compiles correctly with gcc4.1.2 on the Godbolt compiler explorer. I don't have access to gcc4.1.1, but hopefully "+m" and [named] asm memory operands weren't new in that release.

+m makes it much easier to write a bulletproof swap function. e.g. no risk of gcc choosing two different memory locations for the same variable as an input vs. as an output.

gcc -O3 output from gcc4.1.2:

# 32-bit: -m32 -fomit-frame-pointer
sync_xchg(int*, int):
    movl    4(%esp), %edx
    movl    8(%esp), %eax
    xchg %eax, (%edx)
    ret                   # returns in EAX


# 64-bit: just -O3
sync_xchg(int*, int):
    xchg %esi, (%rdi)
    movl    %esi, %eax
    ret                    # returns in EAX

I also checked that this asm actually assembles, using a newer gcc and clicking the 11010 binary button on Godbolt. Fun fact: because GAS accepts xchg mem,reg or xchg reg,mem, you can compile this function with AT&T or Intel syntax (-masm=intel). Your other functions won't work, though.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...