Модераторы: Daevaorn
  

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> lock-free буфер, нужна критика 
:(
    Опции темы
Lazin
Дата 8.9.2009, 17:05 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 3820
Регистрация: 11.12.2006
Где: paranoid oil empi re

Репутация: 41
Всего: 154



написал сегодня вот такой класс:
Код

#include <windows.h>

#include <cassert>

namespace atomic
{
    template<class T>
    __forceinline T* exchange(T* volatile* p, T* v)
    {
        return reinterpret_cast<T*>(
                   InterlockedExchangePointer( reinterpret_cast<PVOID volatile*>(p)
                                             , reinterpret_cast<PVOID>(v)
                                             ));
    }

    template<class T>
    __forceinline T* compare_exchange(T* volatile* dest, T* exch, T* cmp)
    {
        return reinterpret_cast<T*>(
            InterlockedCompareExchangePointer( reinterpret_cast<PVOID volatile*>(dest)
                                             , reinterpret_cast<PVOID>(exch)
                                             , reinterpret_cast<PVOID>(cmp) 
                                             ));
    }
}

namespace lockfree
{
    // Массив фиксированого размера
    template<class T, size_t SIZE, class Allocator>
    class Block
    {
    public:

        typedef size_t size_type;
        typedef T value_type;
        typedef T& reference_type;
        typedef T const& const_reference_type;
        typedef T* pointer_type;

        Block(Allocator const& a = Allocator())
            : alloc_(a)
            , data_(reinterpret_cast<pointer_type>(storage_))
            , end_(reinterpret_cast<pointer_type>(storage_))
            , visible_(reinterpret_cast<pointer_type>(storage_))
        {
        }

        Block(Block const& other)
            : alloc_(other.alloc_)
            , data_(reinterpret_cast<pointer_type>(storage_))
            , end_(reinterpret_cast<pointer_type>(storage_))
            , visible_(reinterpret_cast<pointer_type>(storage_))
        {
            // копирование данных
            // до того как конструктор завершит работу, можно считать, 
            // что объект используется только из одного потока
            for(int i = 0; i < other.size(); ++i)
            {
                alloc_.construct(end_, other[i]);
                end_++;
            }
            visible_ = end_;
            // если конструктор копирования одного из элементов бросит исключение,
            // деструктор сможет удалить частично инициализированый контейнер
        }

        // оператор присваивания - не потокобезопасен
        Block& operator = (Block const& other)
        {
            // содержимое массива невидимо для других потоков
            atomic::exchange(&visible_, data_);

            // разрушение старых элементов массива
            for(pointer_type p = data_; p < end_; ++p)
                alloc_.destroy(p);

            end_ = data_;

            // копирование новых данных
            try
            {
                for(pointer_type p = other.data_; p < other.end_; ++p)
                {
                    alloc_.construct(end_, *p);  // копирование
                    end_++;
                }
                atomic::exchange(&visible_, end_);  // изменения теперь видимы для других потоков
            }
            catch(...)
            {
                // конструктор копирования одного из объектов бросил исключение
                // старые данные - потеряны, новые - скопированы частично
                for(pointer_type p = data_; p < end_; ++p)
                    alloc_.destroy(p);

                end_ = data_;
                atomic::exchange(&visible_, data_);
                throw;
            }
        }

        ~Block()
        {
            for(pointer_type p = data_; p < end_; ++p)
                alloc_.destroy(p);
        }

        // возвращает размер буфера - не потокобезопасен
        size_type size() const
        {
            return visible_ - data_;
        }

        // возвращает true, если массив пуст - не потокобезопасен
        bool empty() const
        {
            return visible_ == data_;
        }

        // возвращает элемент массива - метод не потокобезопасен
        reference_type operator [] (size_type index)
        {
            assert(index < SIZE);
            return data_[index];
        }

        // возвращает элемент массива - метод не потокобезопасен
        const_reference_type operator [] (size_type index) const
        {
            return data_[index];
        }

        // возвращает ссылку на последний элемент массива - метод не потокобезопасен
        reference_type back()
        {
            return *(visible_ - 1);
        }

        // возвращает ссылку на последний элемент массива - метод не потокобезопасен
        const_reference_type back() const
        {
            return *(visible_ - 1);
        }

        // возвращает ссылку на первый элемент массива - метод не потокобезопасен
        reference_type front()
        {
            return *data_;
        }

        // возвращает ссылку на первый элемент массива - метод не потокобезопасен
        const_reference_type front() const
        {
            return *data_;
        }

        // метод добавляет элемент в массив
        // если добавить нельзя(буфер заполнен) - возвращает false
        // иначе - true
        bool push_back(const_reference_type value)
        {
            while(true)
            {
                pointer_type v = visible_;
                pointer_type e = end_;
                pointer_type n = e + 1;

                // states:
                if (n > data_ + SIZE)
                    return false;

                // атомарная проверка и обмен
                pointer_type p = atomic::compare_exchange(&end_, n, v);

                if (p == v && p == e)
                {
                    // место в массиве выделено
                    alloc_.construct(p, value);
                    atomic::exchange(&visible_, n);  // данные - видимы для других потоков
                    break;
                }
                // live lock
            }
            return true;
        }

        // удаляет последний элемент блока
        // перед тем как удалить - копирует его в value, используя оператор присваивания value_type
        // если элементов нет - возвращает false
        // иначе - true
        bool pop_back(reference_type value)
        {
            while(true)
            {
                pointer_type v = visible_;
                pointer_type e = end_;
                pointer_type n = e - 1;

                // states:
                if (n < data_)
                    return false;

                pointer_type p = atomic::compare_exchange(&end_, n, v);

                if (p == v && p == e)
                {
                    value = *n;
                    alloc_.destroy(n);
                    atomic::exchange(&visible_, n);
                    break;
                }
            }
            return true;
        }

    private:

        Allocator alloc_;
        char storage_[SIZE*sizeof(T)];
        pointer_type data_;
        mutable volatile pointer_type end_;
        mutable volatile pointer_type visible_;
    };

}

это буфер, методы push_back и pop_back которого можно вызывать из разных потоков
в принципе, код проходит все тесты, но, есть сомнения
главное сомнение - интерфейс, здесь можно наделать ошибок, например один поток вызвал back(), а другой в это время pop_back ну и тд
я думал сделать какой-нибудь другой интерфейс, что-бы, если какой-то код работает с контейнером конкурентно, то всякие небезопасные методы нельзя было вызвать
может какой-нибудь объект посредник создавать?
PM MAIL Skype GTalk   Вверх
SenkraD
Дата 9.9.2009, 10:58 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 933
Регистрация: 3.2.2006
Где: Украина::Киев

Репутация: 2
Всего: 23



Lazin,  я не давно таким же занимался по  надобности и также думал на такой же проблеммой -
как вариант можно использовать scope_lock, но тогда придётся отказатся то возвратов ссылок (эту траблу
можно сгладить если использовать smart pointer'ы).

а для полного счастья, как мне кажется,  тут нужен семафор, но я не представляю как это можно положить на автоматику


--------------------
 Имеющий язык - да не убоится спросить! 
user posted image
PM MAIL ICQ   Вверх
  
Ответ в темуСоздание новой темы Создание опроса
Правила форума "С++:Общие вопросы"
Earnest Daevaorn

Добро пожаловать!

  • Черновик стандарта C++ (за октябрь 2005) можно скачать с этого сайта. Прямая ссылка на файл черновика(4.4мб).
  • Черновик стандарта C (за сентябрь 2005) можно скачать с этого сайта. Прямая ссылка на файл черновика (3.4мб).
  • Прежде чем задать вопрос, прочтите это и/или это!
  • Здесь хранится весь мировой запас ссылок на документы, связанные с C++ :)
  • Не брезгуйте пользоваться тегами [code=cpp][/code].
  • Пожалуйста, не просите написать за вас программы в этом разделе - для этого существует "Центр Помощи".
  • C++ FAQ

Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, Earnest Daevaorn

 
1 Пользователей читают эту тему (1 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | C/C++: Общие вопросы | Следующая тема »


 




[ Время генерации скрипта: 0.0565 ]   [ Использовано запросов: 21 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.