硬核C++实现64位操作系统 Atomic(4)

Aug 24, 2020

atomic用来实现1 2 4 8 16字节数据的原子操作

具体到汇编代码就是LOCK CMPXCHG

其中CMPXCHG16b并不是所有64位的CPU都支持

为了简化实现,CAS操作使用GCC Builtin函数实现

首先定义一个自定义类型的atomic

template <class T>
class atomic
{
public:
    void store(T v)
    {
        asm volatile("movq %1, %%rax \n\t"
                     "movq %%rax, %0 \n\t"
                     "mfence \n\t"
                     : "=m"(this->val)
                     : "m"(v)
                     : "memory", "rax");
    }

    T load()
    {
        T tmp;
        asm volatile("movq %1, %%rdi \n\t"
                     "movq %%rdi, %0 \n\t"
                     : "=m"(tmp)
                     : "m"(this->val)
                     : "memory", "rdi");
        return tmp;
    }

    bool operator==(T v)
    {
        return this->load() == v;
    }

    bool compare_exchange(T &old_val, T new_val) 
    requires(sizeof(T) == 1)
    {
        return __sync_bool_compare_and_swap_1(
        (uint8_t *)&this->val, 
        *((uint8_t *)&old_val), 
        *((uint8_t *)&new_val));
    }

    bool compare_exchange(T &old_val, T new_val) 
    requires(sizeof(T) == 2)
    {
        return __sync_bool_compare_and_swap_2(
        (uint16_t *)&this->val,
        *((uint16_t *)&old_val),
        *((uint16_t *)&new_val));
    }

    bool compare_exchange(T &old_val, T new_val)
    requires(sizeof(T) == 4)
    {
        return __sync_bool_compare_and_swap_4(
        (uint32_t *)&this->val,
        *((uint32_t *)&old_val), 
        *((uint32_t *)&new_val));
    }

    bool compare_exchange(T &old_val, T new_val)
    requires(sizeof(T) == 8)
    {
        return __sync_bool_compare_and_swap_8(
        (uint64_t *)&this->val, 
        *((uint64_t *)&old_val), 
        *((uint64_t *)&new_val));
    }

    bool compare_exchange(T &old_val, T new_val)
    requires(sizeof(T) == 16)
    {
        return __sync_bool_compare_and_swap_16(
        (uint128_t *)&this->val, 
        *((uint128_t *)&old_val), 
        *((uint128_t *)&new_val));
    }

private:
    alignas(T) T val;
};

数据成员

只需要一个val作为保存值

alignas(T) T val;

注意val需要自然对齐,内存地址需要是sizeof(T)的整数倍,否则MOV指令不保证是原子操作(对于LOCAL CPU而言)

接口

atomic只提供load store CAS三个接口

对于每一个size,使用requires(sizeof(T) == 16)进行overload

(这里需要C++20 concept)

数值类型接口

数值类型需要提供operator++ – += -= fetch_add fetch_sub等操作

参考libc++标准库实现

实现在基类atomic_number中,然后具体类型的子类继承atomic_number实现

template <class T>
struct atomic_number
{
    atomic_number() : val(0) {}
    atomic_number(T v) : val(v) {}

    T fetch_add(T v)
    {
        asm volatile("lock; addq %1, %0 \n\t"
                     : "=m"(this->val)
                     : "r"((uint64_t)v)
                     : "memory");
        return (T)this->val;
    }

    T fetch_sub(T v)
    {
        asm volatile("lock; subq %1, %0 \n\t"
                     : "=m"(this->val)
                     : "r"((uint64_t)v)
                     : "memory");
        return (T)this->val;
    }

    void operator+=(T v)
    {
        this->fetch_add(v);
    }

    void operator-=(T v)
    {
        this->fetch_sub(v);
    }
    
    void store(T v)
    {
        asm volatile("movq %1, %0 \n\t"
                     "mfence \n\t"
                     : "=m"(this->val)
                     : "r"((uint64_t)v)
                     : "memory");
    }

    T load()
    {
        uint64_t tmp;
        asm volatile("movq %1, %%rdi \n\t"
                     "movq %%rdi, %0 \n\t"
                     : "=m"(tmp)
                     : "m"(this->val)
                     : "memory", "rdi");
        return (T)tmp;
    }

    bool operator==(T v)
    {
        return this->load() == v;
    }

private:
    alignas(T) T val;
};

类型特化

对各个数值类型进行特化即可

template <>
struct atomic<uint128_t> : public atomic_number<uint128_t>
{
    atomic() noexcept = default;
    ~atomic() noexcept = default;
    atomic(const atomic &) = delete;
    atomic &operator=(const atomic &) = delete;
    atomic &operator=(const atomic &) volatile = delete;

    atomic(uint128_t val) noexcept : atomic_number(val) {}
};

至此 atomic类就实现完了

ABA问题

ABA问题与CAS没有任何关系,需要自行实现counter进行避免

Great! You've successfully subscribed.
Great! Next, complete checkout for full access.
Welcome back! You've successfully signed in.
Success! Your account is fully activated, you now have access to all content.