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

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> C++0X atomic, асинхронное удаление из lock_free stack 
:(
    Опции темы
Леопольд
Дата 3.12.2010, 18:47 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 943
Регистрация: 17.6.2009

Репутация: 10
Всего: 13



Цитата(azesmcar @  3.12.2010,  15:44 Найти цитируемый пост)
я теряюсь в твоих исходниках, уже не понимаю которая версия правильная, которая нет.
вот правильная:
http://forum.vingrad.ru/act-ST/f-92/t-3162.../p-2258868.html

здесь она же, но bез relacy модификаций
http://liveworkspace.org/code/81347b6f4ba2...09c75a0fbcc31a5

Добавлено @ 18:54
Цитата(azesmcar @  3.12.2010,  17:35 Найти цитируемый пост)
А у тебя там случайно не bad_alloc вылетает? 
bыло bы неплохо smile Но оно просто падает.
Цитата
*** glibc detected *** /home/andrey/proj/try_c++0x/bin/Debug/try_c++0x: malloc(): memory corruption (fast): 0x09a0b9a0 ***


Вот, уронил (2 потока вставляют, 2 выкидывают): http://liveworkspace.org/code/34a83f493245...44fb349a193814d


Это сообщение отредактировал(а) Леопольд - 4.12.2010, 09:01


--------------------
вопросов больше чем ответов
PM MAIL   Вверх
Леопольд
Дата 4.12.2010, 09:16 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 943
Регистрация: 17.6.2009

Репутация: 10
Всего: 13



Цитата(Леопольд @  3.12.2010,  18:47 Найти цитируемый пост)
Но оно просто падает.
Подумал что можно попроbовать аллокатор написать. В lock free это, наверное, очень сложная штука (а может просто нереальная). Память фрагментируется совершенно непредсказуемо. Может в этом и проbлема...

Решил попроbовать malloc из libatomic-ops-dev
http://manpages.ubuntu.com/manpages/lucid/...c-malloc.3.html
Отпишусь, когда bудут результаты.

судя по описанию, не подходит
Попроbую почитать это когда время bудет (автор Maged M. Michael).

Цитата
Async-signal-safety: Due to the use of locking in cur-
rent implementations of malloc and free, they are not consid-
ered async-signal-safe [9], i.e., signal handlers are prohibited
from using them.



Это сообщение отредактировал(а) Леопольд - 4.12.2010, 10:10


--------------------
вопросов больше чем ответов
PM MAIL   Вверх
azesmcar
Дата 8.12.2010, 09:19 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


uploading...
****


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

Репутация: 81
Всего: 211



Леопольд

Был в небольшом отпуске..вернемся к нашим баранам, на свежую голову и думается легче. Проблема то лежит на самом видном месте
Проверка
Код

if (m_threads_on_lap.fetch_sub(1) - 1 == 0)

Эквивалентна проверке
Код

if (m_threads_on_lap.fetch_sub(1) == 1)

Но почему тут проверяется на единицу? При входе в pop значение инкрементируется 1 раз, а тут декрементируется (тоже один раз). Т.е. После операции fetch_sub(1) переменная указывает на количество потоков в функции pop на данный момент (не считая текущего потока). А значит проверка на 1 означает, что там есть еще один поток, который может создать проблемы smile 
Исправлять естественно вот так
Код

if (m_threads_on_lap.fetch_sub(1) == 0)


PM   Вверх
Леопольд
Дата 8.12.2010, 19:21 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 943
Регистрация: 17.6.2009

Репутация: 10
Всего: 13



azesmcar, fetch_sub возвращает предыдущее значение. Т.е. 1 в данном случае. Если только тут где-то ABA спряталась. Но я не вижу.

Скорее всего, проbлема в аллокации памяти. По отдельности push и pop выполняются bез нареканий, но если смешать...

Везде пишут что стандартные malloc и free раbотают на мьютексе.

Это сообщение отредактировал(а) Леопольд - 8.12.2010, 19:39


--------------------
вопросов больше чем ответов
PM MAIL   Вверх
azesmcar
Дата 8.12.2010, 19:30 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


uploading...
****


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

Репутация: 81
Всего: 211



Цитата(Леопольд @  8.12.2010,  19:21 Найти цитируемый пост)
azesmcar, fetch_sub возвращает предыдущее значение. Т.е. 1 в данном случае. 

Да? Не помню..посмотрел в документации, действительно так и есть..

Цитата(Леопольд @  8.12.2010,  19:21 Найти цитируемый пост)
Скорее всего, проbлема в аллокации памяти.

Там не может быть проблем, в мире полно работающих структур с использованием операторов new и delete. Проблема как я уже говорил в обращении к удаленному участку памяти, надо просто ее найти.

Добавлено через 59 секунд
Цитата(Леопольд @  8.12.2010,  19:21 Найти цитируемый пост)
Везде пишут что стандартные malloc и free раbотают на мьютексе.

каким образом это может помешать работать алгоритму? Это может сказаться на производительности, это может лишить структуру статуса lock-free, но никак не должно мешать ей работать правильно.
PM   Вверх
Леопольд
Дата 8.12.2010, 19:40 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 943
Регистрация: 17.6.2009

Репутация: 10
Всего: 13



Цитата(azesmcar @  8.12.2010,  19:30 Найти цитируемый пост)
каким образом это может помешать работать алгоритму? Это может сказаться на производительности, это может лишить структуру статуса lock-free, но никак не должно мешать ей работать правильно. 

Согласен, алгоритм просто перестаёт bыть lock free.

Вероятнее всего, проbлема во фрагментации памяти. Если выделять память bлоками одинакового размера, то перестаёт падать:
http://liveworkspace.org/code/3e95623f8da7...1b146a08dd3a6e7


Это сообщение отредактировал(а) Леопольд - 8.12.2010, 19:43


--------------------
вопросов больше чем ответов
PM MAIL   Вверх
azesmcar
Дата 8.12.2010, 19:49 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


uploading...
****


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

Репутация: 81
Всего: 211



Цитата(Леопольд @  8.12.2010,  19:40 Найти цитируемый пост)
Вероятнее всего, проbлема во фрагментации памяти. Если выделять память bлоками одинакового размера, то перестаёт падать:

меня все таки не покидает чувство, что ищем то, чего на самом деле нет.. smile 
завтра на работе с утра покопаюсь поглубже..
PM   Вверх
azesmcar
  Дата 9.12.2010, 08:11 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


uploading...
****


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

Репутация: 81
Всего: 211



Леопольд

Провел несколько тестов. Что-то здесь не так.. smile пример из книги тоже не работает. Довольно странно, надо поработать над вопросом поглубже.

Добавлено через 1 минуту и 22 секунды
имеется ввиду на gcc 4.5, к сожалению другой библиотеки для тестов у меня нет.
PM   Вверх
azesmcar
Дата 9.12.2010, 09:51 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


uploading...
****


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

Репутация: 81
Всего: 211



Леопольд

Нашел..в общем аллокатор тут не причем, точнее причем, но не он источник проблемы. Это ABA smile 
Поигрался с алокатором, в итоге
Код

int *p = new int(0);
std::cout << p << std::endl;
delete p;
p = new int(0);
std::cout << p << std::endl;

практически всегда выводит одно и тоже, т.е. если запрашивать память после удаления, системный аллокатор скорее всего вернет ту же область памяти, которую недавно освободил. Это прекрасно объясняет почему разделение push и pop избавляют от проблемы а также то, почему без delete -а все работает - аллокатор не может вернуть старый адрес, так-как тот еще не освобожден.
Позже попробую исправить.

Это сообщение отредактировал(а) azesmcar - 9.12.2010, 09:52
PM   Вверх
azesmcar
Дата 9.12.2010, 10:34 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


uploading...
****


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

Репутация: 81
Всего: 211



К сожалению далеко не все еще реализовано в GCC smile 
пробую исправить добавлением счетчика-а, но std::atomic<used-defined-type> еще не реализован.
Код

struct node;
struct node_ptr
{
    node* ptr;
    unsigned cnt;
};
struct node
{
    node_ptr next;
    std::shared_ptr<T> data;
    node(const T& d, const node_ptr& n): next(n), data(std::make_shared<T>(d)) {}
};
std::atomic<node_ptr> m_head;
std::atomic<node_ptr> m_deleted_head;
...

идея в том, чтобы инкрементировать значение счетчика, тем самым делая указатели на тот же участок памяти разным в разное время. DCAS по идее должен быть Lock-free на всех современных платформах.

Это сообщение отредактировал(а) azesmcar - 9.12.2010, 10:38
PM   Вверх
Леопольд
Дата 9.12.2010, 11:01 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 943
Регистрация: 17.6.2009

Репутация: 10
Всего: 13



Цитата(azesmcar @  9.12.2010,  09:51 Найти цитируемый пост)
аллокатор не может вернуть старый адрес, так-как тот еще не освобожден.
Боюсь не уловил смысл. Вроде бы, если память ещё не освобождена, то аллокатор должен выделить новый кусок, разве нет? 
Если я правильно понял, то мьютекс здесь смог бы помочь.

А вот по поводу ABA, это, наверное оно самое! Надо понять только как smile


Это сообщение отредактировал(а) Леопольд - 9.12.2010, 11:01


--------------------
вопросов больше чем ответов
PM MAIL   Вверх
azesmcar
Дата 9.12.2010, 11:05 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


uploading...
****


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

Репутация: 81
Всего: 211



Цитата(Леопольд @  9.12.2010,  11:01 Найти цитируемый пост)
Боюсь не уловил смысл. Вроде бы, если память ещё не освобождена, то аллокатор должен выделить новый кусок, разве нет? 

Посмотри этот пример
Код

int *p = new int(0);
std::cout << p << std::endl;
delete p;
p = new int(0);
std::cout << p << std::endl;

выделяется память, возвращается адрес
память освобождается и при следующем выделение памяти аллокатор снова возвращает тот же самый указатель, т.е. выделяет память на том же участке. Тут и возникает ABA, CAS думает, что это тот же указатель, а он другой. В качестве решения можно как-то уникально идентифицировать каждую аллокацию используя счетчик, но тут сложность с реализацией (писал выше об этом). Можно сделать через std::atomic<long long> - его размер 8 байтов, туда можно поместить и счетчик и указатель. Не сильно переносимо конечно, но для теста сойдет smile 

Цитата(Леопольд @  9.12.2010,  11:01 Найти цитируемый пост)
Если я правильно понял, то мьютекс здесь смог бы помочь.

Не понял причем тут мутекс? Это уже блокировка, мы про неблокирующие алгоритмы говорим.


Это сообщение отредактировал(а) azesmcar - 9.12.2010, 11:09
PM   Вверх
Леопольд
Дата 9.12.2010, 11:27 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 943
Регистрация: 17.6.2009

Репутация: 10
Всего: 13



Цитата(azesmcar @  9.12.2010,  11:05 Найти цитируемый пост)
Посмотри этот пример
Это я понял. Я про то предложение, которое процитировал.

Цитата(azesmcar @  9.12.2010,  11:05 Найти цитируемый пост)
Тут и возникает ABA, CAS думает, что это тот же указатель, а он другой. 
Может один и тот же адрес дважды попасть в список на удаление? Вроде нет, он ведь висит там до тех пор, пока память не будет освобождена. Т.е. это возможно только если память может быть выделена на тот же адрес до того как будет освобождена, это было бы довольно странное поведение...
Может ли один и тот же адрес попасть в стек дважды? После чего дважды перекочует в очередь на удаление. По моему, тоже нет, по той же причине.

Не могу понять, где здесь ABA. Но оно вероятнее чем баги в gcc. У меня, кстати, тоже только gcc 4.5.1 под рукой, и на работе и дома.

Цитата(azesmcar @  9.12.2010,  11:05 Найти цитируемый пост)
Не понял причем тут мутекс? Это уже блокировка, мы про неблокирующие алгоритмы говорим.
Это я про стандартные malloc и free. Пока гуглил, не раз попадалось что в gcc они не lock free, т.е. работают с мьютексом.


Можно попробовать проверить, надо добавить std::set<void *> и блокировки при работе с ним. Выделил память, запихнул туда адрес, если он уже там, значит ABA имеет место быть. Освободил память, выкинул адрес из сета. Только вот получится ли воспроизвести ошибку? Блокировка может сделать так, что ошибка пропадёт навсегда. smile

P.S. на работе сроки поджимают, буду сюда заглядывать, но гораздо реже чем раньше.

Это сообщение отредактировал(а) Леопольд - 9.12.2010, 12:06


--------------------
вопросов больше чем ответов
PM MAIL   Вверх
azesmcar
Дата 9.12.2010, 16:07 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


uploading...
****


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

Репутация: 81
Всего: 211



Леопольд

Проблема та, которую я описывал еще в первом или во втором предположении smile 

Вот код
Код

std::shared_ptr<T> pop()
{
    std::shared_ptr<T> ret;
    // увеличиваем количество потоков в функции pop
    m_threads_in_pop.fetch_add(1);

    // убираем элемент из списка
    node *head = m_head.load();
    while (head && !m_head.compare_exchange_weak(head, head->next));

    // если элемент получен, т.е. список был не пуст
    if(head)
    {
        // устанавливаем возвращаемое значение
        ret.swap(head->data);
        // добавляем элемент в начало списка элементов на удаление
        head->next = m_deleted_head.load();
        while (!m_deleted_head.compare_exchange_weak(head->next, head));
    }
    // если это последний поток, т.е. больше в функции pop потоков на данный момент нет
    if(m_threads_in_pop.fetch_sub(1) == 1)
    {
        // проверяем, что никто не менял m_deleted_head с того момента, как мы добавили туда элемент
        if (m_deleted_head.compare_exchange_strong(head, 0))
        {
            // удаляем все элементы, подлежащие удалению
            while (head)
            {
                node * next = head->next;
                delete head;
                head = next;
            }
        }
    }
    return ret;
}

Представь, что 1 поток останавливается на выполнении строки
Код

if (m_deleted_head.compare_exchange_strong(head, 0))

к этому моменту количество потоков в функции pop уже декрементировано и равно 0.
до того, как произойдет сравнение другой поток может изменить этот список, на что ты вполне логично ответил
Цитата

Тот, кто смог её выполинть, гарантированно владеет списком указателей, с которыми больше никто не работает. Поэтому вся работа с указателями происходит до того как уменьшается счётчик. Я и имя ему такое дал, что бы с гонками ассоциировалось. Чистит хвосты самый нерадивый. 

т.е. если кто-то изменял список, то проверка
Код

if (m_deleted_head.compare_exchange_strong(head, 0))

не пройдет.

это верно, разве что, если другой поток не изменил этот список, в конце добавив туда элемент с тем же адресом smile 
представь, что твой код остановился на выполнении этой проверки, в head загужен узел A, а потом другой поток удалил несколько элементов, очистил за ними память, добавил еще один, удалил, и последний удаленный имеет тот же адрес, который на данный момент загружен в head в твоем первом потоке. Это возможно, так-как когда твой первый поток остановится на выполнении проверки, счетчик равен нулю, твой узел (A) в списке to_be_deleted и другой поток волен удалить этот элемент.

Вроде бы так..надо подумать еще раз попозже, на свежую голову smile 


Это сообщение отредактировал(а) azesmcar - 9.12.2010, 16:25
PM   Вверх
Леопольд
Дата 9.12.2010, 18:58 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 943
Регистрация: 17.6.2009

Репутация: 10
Всего: 13



Наверное, это оно. Попроbую оbойти...

Это сообщение отредактировал(а) Леопольд - 9.12.2010, 19:05


--------------------
вопросов больше чем ответов
PM MAIL   Вверх
Ответ в темуСоздание новой темы Создание опроса
Правила форума "С++:Общие вопросы"
Earnest Daevaorn

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

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

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

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


 




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


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

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