Стек с блокировкой вращения и барьеры памяти (C ++)

У меня есть блокировка вращения реализации:

class Spinlock {
public:
    void Lock() {
        while (true) {
            if (!_lock.test_and_set(std::memory_order_acquire)) {
                return;
            }
        }
    }

    void Unlock() {
        _lock.clear(std::memory_order_release);
    }

private:
    std::atomic_flag _lock;
};

Я использую класс SpinLock в:

class SpinlockedStack {
public:
    SpinlockedStack() : _head(nullptr) {
    }

    ~SpinlockedStack() {
        while (_head != nullptr) {
            Node* node = _head->Next;
            delete _head;
            _head = node;
        }
    }

    void Push(int value) {
        _lock.Lock();
        _head = new Node(value, _head);
        _lock.Unlock();
    }

    bool TryPop(int& value) {
        _lock.Lock();

        if (_head == nullptr) {
            value = NULL;
            _lock.Unlock();
            return false;
        }

        Node* node = _head;
        value = node->Value;
        _head = node->Next;

        delete node;

        _lock.Unlock();

        return true;
    }

private:
    struct Node {
        int Value;
        Node* Next;

        Node(int value, Node* next) : Value(value), Next(next) {
        }
    };

    Node* _head;
    Spinlock _lock;
};

Я понимаю, что надо ставить барьеры памяти. Я могу использовать атомарные переменные:

struct Node {
    int Value;
    std::atomic<Node*> Next;

    Node(int value) : Value(value) {
    }
};

std::atomic<Node*> _head;
Spinlock _lock;
...

void Push(int value) {
    _lock.Lock();

    Node* currentHead = _head.load(std::memory_order_acquire);

    Node* newHead = new Node(value);
    newHead->Next.store(currentHead, std::memory_order_relaxed);

    _head.store(newHead, std::memory_order_release);

    _lock.Unlock();
}

bool TryPop(int& value) {
    _lock.Lock();

    Node* currentHead = _head.load(std::memory_order_acquire);

    if (currentHead == nullptr) {
        value = NULL;
        _lock.Unlock();
        return false;
    }

    value = currentHead->Value;
    _head.store(currentHead->Next.load(std::memory_order_relaxed), std::memory_order_release);

    delete currentHead;

    _lock.Unlock();

    return true;
}

Я также могу использовать atomic_thread_fence ():

struct Node {
    int Value;
    Node* Next;

    Node(int value) : Value(value) {
    }
};

Node* _head;
Spinlock _lock;

...

void Push(int value) {
    _lock.Lock();

    Node* currentHead = _head;

    std::atomic_thread_fence(std::memory_order_acquire);

    Node* newHead = new Node(value);

    newHead->Next = currentHead;

    std::atomic_thread_fence(std::memory_order_release);

    _head = newHead;

    _lock.Unlock();
}

bool TryPop(int& value) {
    _lock.Lock();

    std::atomic_thread_fence(std::memory_order_acquire);

    Node* currentHead = _head;

    if (currentHead == nullptr) {
        value = NULL;
        _lock.Unlock();
        return false;
    }

    value = currentHead->Value;

    std::atomic_thread_fence(std::memory_order_acquire);

    Node* nextNead = currentHead->Next;

    std::atomic_thread_fence(std::memory_order_release);

    _head = nextNead;

    delete currentHead;

    _lock.Unlock();

    return true;
}

Мои вопросы:

  1. Ставлю ли я барьеры памяти?
  2. Что лучше использовать в этом случае (атомарные переменные или atomic_thread_fence) и почему?

person sribin    schedule 21.06.2015    source источник


Ответы (1)


Получение блокировки уже устанавливает необходимые вам гарантии памяти.

Когда поток освобождает блокировку, он должен записать атомарный флаг. Это гарантирует, что когда следующий поток получит блокировку и увидит запись во флаг, что получающий поток гарантированно увидит все записи, которые выполнял освобождающий поток перед записью во флаг.

Кстати, вы должны использовать что-то вроде RAII, чтобы убедиться, что ваша блокировка снимается при любых обстоятельствах.

Также вы должны инициализировать вашу блокировку с помощью ATOMIC_FLAG_INIT, иначе она находится в неопределенном состоянии.

person Voo    schedule 21.06.2015