crossplatform.ru

Здравствуйте, гость ( Вход | Регистрация )

 
Ответить в данную темуНачать новую тему
> MSVS 2005 std::deque и boost 1.44.0 boost::mutex::scoped_lock, разрушение стека при многопоточности
Iron Bug
  опции профиля:
сообщение 1.10.2010, 9:44
Сообщение #1


Профессионал
*****

Группа: Модератор
Сообщений: 1609
Регистрация: 6.2.2009
Из: Yekaterinburg
Пользователь №: 533

Спасибо сказали: 219 раз(а)




Репутация:   12  


Чуть не рехнулась, пока искала эту багу! Вроде всё правильно, а лезет эксепшн...
Использую MSVS 2005 SP1 и boost 1.44.0.

Суть в том, что мне нужен был дек(std::deque) для буферизации данных между несколькими потоками. В этот дек пишут несколько потоков и есть один читающий поток, который периодически обрабатывает данные и сбрасывает содержимое дека.

Чтобы не тормозить работу с деком, была огранизована система работы с указателями:
- существует указатель на объект дека (он инициализируется в начале работы программы, до запуска всех потоков)
- пишущие потоки добавляют данные, используя этот указатель
- читающий поток подменяет глобальный указатель на новый созданный объект , сбрасывает накопленное содержимое и затем освобождает использованный объект

Естественно, всё это происходит под мьютексами и собирается как многопоточное приложение.

Сначала были заюзаны локи boost:mutex::scoped_lock. Я их обычно использую где ни попадя: удобны, безопасны для эксепшнов и т.п. Но вот тут и оказалась засада: при нескольких пишущих потоках вылезал эксепшн нарушения стека. Позже при тестировании выяснилось, что та же беда случается и при нескольких читающих потоках, даже при одном пишушем. Я уже думала, что у меня крыша поехала: ну нет ничего подозрительного, всё под мьютексами... потом решила наобум заменить scoped_lock на простые lock/unlock... и оно заработало!!!

Написала тестовую софтинку, бага(нарушение стека) отчётливо проявляется при установке макросов NUMBER_OF_WRITERS_1 или NUMBER_OF_WRITERS_1 в число больше единицы. При этом NUMBER_OF_WRITERS_2 и NUMBER_OF_READERS_2 могут быть абсолютно любыми - никаких проблем не возникает.
Раскрывающийся текст

#include <deque>
#include <boost/thread.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/thread/barrier.hpp>

using namespace std;
using namespace boost;

// число потоков со scoped_lock
// тест падает, если хотя бы один из них больше 1
#define NUMBER_OF_WRITERS_1        1
#define NUMBER_OF_READERS_1        1

// число потоков с lock/unlock
// может быть любое число потоков
#define NUMBER_OF_WRITERS_2        2
#define NUMBER_OF_READERS_2        2

typedef deque<long> deq_type;

// указатель на общий дек
deq_type *pdeq;

// мьютекс для защиты дека
mutex mt;

// барьеры используются для одновременного старта всех потоков
barrier barr1(NUMBER_OF_WRITERS_1+NUMBER_OF_READERS_1);
barrier barr2(NUMBER_OF_WRITERS_2+NUMBER_OF_READERS_2);

// флаг завершения работы потоков
bool stop_flag;

// заглушка
void dummy()
{
}

// пишуший поток со scoped_lock
void writer1()
{
    barr1.wait();
    while(!stop_flag)
    {
        {
            mutex::scoped_lock(mt);
            pdeq->push_front(1);
        }
        this_thread::sleep(posix_time::milliseconds(10));
    }
}

// читающий поток со scoped_lock
void reader1()
{
    deq_type *pcopy;
    barr1.wait();
    while(!stop_flag)
    {
        if(pdeq->size() > 0)    
        {
            {
                mutex::scoped_lock(mt);
                pcopy = pdeq;
                pdeq = new deq_type();
            }

            deque<long>::iterator i;
            for(i=pcopy->begin(); i!=pcopy->end(); i++)
            {
                dummy();
            }

            pcopy->clear();
            delete pcopy;
        }
    }
    this_thread::sleep(posix_time::milliseconds(10));
}

// пишуший поток со lock/unlock
void writer2()
{
    barr2.wait();
    while(!stop_flag)
    {
        mt.lock();
        pdeq->push_front(1);
        mt.unlock();
        this_thread::sleep(posix_time::milliseconds(10));
    }
}

// читающий поток со lock/unlock
void reader2()
{
    deq_type *pcopy;
    barr2.wait();
    while(!stop_flag)
    {
        if(pdeq->size() > 0)    
        {
            mt.lock();
            pcopy = pdeq;
            pdeq = new deq_type();
            mt.unlock();

            deque<long>::iterator i;
            for(i=pcopy->begin(); i!=pcopy->end(); i++)
            {
                dummy();
            }

            pcopy->clear();
            delete pcopy;
        }
    }
    this_thread::sleep(posix_time::milliseconds(10));
}

int main()
{
    thread_group tg;

    // тест со scoped_lock
    // инициализируем дек
    pdeq = new deq_type();

    stop_flag = false;
    for(int i=0; i<NUMBER_OF_WRITERS_1; i++) tg.create_thread(writer1);
    for(int i=0; i<NUMBER_OF_READERS_1; i++) tg.create_thread(reader1);

    this_thread::sleep(posix_time::seconds(1));
    stop_flag = true;
    tg.join_all();

    if(pdeq != 0)
    {
        pdeq->clear();
        delete pdeq;
    }

    // тест с lock/unlock

    // инициализируем дек
    pdeq = new deq_type();

    stop_flag = false;
    for(int i=0; i<NUMBER_OF_WRITERS_2; i++) tg.create_thread(writer2);
    for(int i=0; i<NUMBER_OF_READERS_2; i++) tg.create_thread(reader2);

    this_thread::sleep(posix_time::seconds(1));
    stop_flag = true;
    tg.join_all();

    if(pdeq != 0)
    {
        pdeq->clear();
        delete pdeq;
    }
    return 0;
}


Вроде ошибок я не вижу. Не первый год пишу многопоточные приложения... То ли баг буста, то ли STL. Нужно дополнительно проверять. Завтра соберу буст под MSVS 2010 и проверю. А дома под линём проверю...

Может, кто-то сталкивался с подобной хренью?

P.S. модераторы, поправьте в названии stl::deque на std::deque
P.P.S чуть подправила код (быстро копировала, забыла вторую инициализацию), но суть баги не меняется.

Сообщение отредактировал igor_bogomolov - 1.10.2010, 12:29
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение
Алексей1153
  опции профиля:
сообщение 1.10.2010, 10:04
Сообщение #2


фрилансер
******

Группа: Участник
Сообщений: 2928
Регистрация: 19.6.2010
Из: Обливион
Пользователь №: 1822

Спасибо сказали: 215 раз(а)




Репутация:   34  


а если ради эксперимента написать свою следилку за разлочиванием - в конструкторе принимает указатель на синхро-объект, а в деструкторе разлочивает

Если всё будет в порядке, то глюк действительно в scoped_lock имеется
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение
Iron Bug
  опции профиля:
сообщение 1.10.2010, 12:10
Сообщение #3


Профессионал
*****

Группа: Модератор
Сообщений: 1609
Регистрация: 6.2.2009
Из: Yekaterinburg
Пользователь №: 533

Спасибо сказали: 219 раз(а)




Репутация:   12  


чёрт, да что сегодня за день за такой?
стала проверять под MSVS 2010, а там вообще boost::thread падает с access violation в конструкторе!
буст тот же, 1.44.0.

причём на StackOverflow пишут, что это бага 43-го буста и что она даже была пофиксена (http://stackoverflow.com/questions/2914666...ption-in-vs2010), но вот у меня прецедент с 44-м бустом. и в списках багов буста я вроде не нашла этой баги... пороюсь в мэйл-рассылке буста, может там есть что-то насчёт этого.

версия MSVS такая:
Version 10.0.30319.1 RTMRel
Installed Version: Premium

ну и что теперь делать?
дома как раз ставлю новый 64-битный экспериментальный деб, там ваще всё пока сыро и поди тоже полезет какая-нибудь шняга...

пока неясно, куда сообщать о проблеме. буду ещё экспериментировать.

Сообщение отредактировал Iron Bug - 1.10.2010, 12:15
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение
Iron Bug
  опции профиля:
сообщение 6.10.2010, 9:43
Сообщение #4


Профессионал
*****

Группа: Модератор
Сообщений: 1609
Регистрация: 6.2.2009
Из: Yekaterinburg
Пользователь №: 533

Спасибо сказали: 219 раз(а)




Репутация:   12  


ВНИМАНИЕ, БАГА В MS STL!
Подсказали мне в листе рассылок буста, в чём проблема с deque и многопоточностью (thanks to David Ward):

Bug in std::deque. Non-conforming invalidation of iterators after pop_front()

Вкратце: не юзайте std::deque в MSVS начиная с 2005 и заканчивая 2010. Метод pop_front() иногда даёт сбой и портит итераторы элементов, к которым он не имеет никакого отношения. А в моём случае то же самое делал push_front() . Одного поля ягодки! Видимо, проявление баги зависит от таймингов: когда начинается частое обращение (даже защищённое мьютексами!) - что-то кривеет и некоторые итераторы становятся невалидными.

Мелкософт обещает стать хорошим и исправить багу в студии 2011. А пока что лучше не рисковать и вообще не юзать std::deque.

А вот с конструктором boost::thread пока неясно...

P.S. Гнусности ещё только начинаются! Теперь для начала нужно пересобрать стопицот старых, но вполне используемых проектов, чтобы избавиться от использования std::deque (а сначала ещё надо найти, на что бы такое его заменить!). Но кроме этого я вдруг обнаружила, что исходники boost также содержат этот злополучный deque и некоторые библиотеки потенциально опасны при работе под компилером MSVС версий от 7-й до 10-й. В частности, deque используется в исходниках бустовских библиотек smart_ptr (sp_collector.cpp), boost:expressive и boost::regex (command_line.cpp). И в заголовочниках некоторых библиотек буста есть #include <deque>.... Одним словом, приличная такая "жожоба", как мы выражались на одной работе.

Сообщение отредактировал Iron Bug - 6.10.2010, 12:27
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение

Быстрый ответОтветить в данную темуНачать новую тему
Теги
Нет тегов для показа


1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0




RSS Текстовая версия Сейчас: 22.10.2021, 5:23