Ошибка утверждения отладки: нижний индекс вне диапазона с std::vector

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

Ошибка:

Ошибка утверждения отладки! Программа: .... Файл: c:\program files\microsoft visual studio 10.0\vc\include\vector Строка: 1440 Выражение: Строковый индекс вне допустимого диапазона

Что делает программа:

Есть две темы:

Тема 1:

Первый поток ищет (между прочим) изменения в текущем окне, используя GetForegroundWindow(), проверка происходит не в цикле, а при срабатывании события WH_MOUSE_LL. Данные разбиваются на структуры фиксированного размера, чтобы их можно было отправить на сервер по TCP. Первый поток и записывает данные (заголовок окна) в std::list в текущей структуре.

if(change_in_window)
{
    GetWindowTextW(hActWin,wTitle,256);
    std::wstring title(wTitle);
    current_struct->titles.push_back(title);
}

Тема 2:

Второй поток ищет еще не отправленные структуры и помещает их содержимое в буферы char, чтобы их можно было отправить по TCP. Пока я не знаю точно, где ошибка, судя по типу ошибки это было делать либо со строкой, либо со списком, и это единственный код из всего моего приложения, использующего списки/строки (остальные — обычные массивы). Кроме того, комментирование блока if, как указано в комментариях к коду, предотвращает возникновение ошибки.

  BOOL SendStruct(DATABLOCK data_block,bool sycn)
    {
    [..]

                int _size = 0;
// Important note, when this if block is commented the error ceases to exist, so it has something to do with the following block
                if(!data_block.titles.empty()) //check if std::list is empty
                {

                    for (std::list<std::wstring>::iterator itr  = data_block.titles.begin(); itr != data_block.titles.end() ; itr++) {
                        _size += (((*itr).size()+1) * 2); 
                    } //calculate size required. Note the +1 is for an extra character between every title
                    wchar_t* wnd_wbuffer = new wchar_t[_size/2](); //allocate space
                    int _last = 0;
    //loop through every string and every char of a string and write them down
                    for (std::list<std::wstring>::iterator itr = data_block.titles.begin(); itr != data_block.titles.end(); itr++)
                    {
                        for(unsigned int i = 0; i <= (itr->size()-1); i++)
                        {

                            wnd_wbuffer[i+_last] = (*itr)[i] ;
                        }
                        wnd_wbuffer[_last+itr->size()] = 0x00A6; // separator
                        _last += itr->size()+1;
                    }

                    unsigned char* wnd_buffer = new unsigned char[_size];
                    wnd_buffer = (unsigned char*)wnd_wbuffer;
                    h_io->header_w_size = _size;
                    h_io->header_io_wnd = 1;
                    Connect(mode,*header,conn,buffer_in_bytes,wnd_buffer,_size);
                    delete wnd_wbuffer;
                }
                else
            [..]
                return true;
            }

Моя попытка синхронизации потоков: есть указатель на первый созданный блок данных (db_main), указатель на текущий блок данных (db_cur)

//datablock format
    typedef struct _DATABLOCK
        {
            [..]
            int logs[512];
            std::list<std::wstring> titles;
            bool bPrsd; // has this datablock been sent true/false
            bool bFull; // is logs[512] full true/false
            [..]
            struct _DATABLOCK *next;
        } DATABLOCK;    


//This is what thread 1 does when it needs to register a mouse press and it is called like this:
    if(change_in_window)
    {
        GetWindowTextW(hActWin,wTitle,256);
        std::wstring title(wTitle);
        current_struct->titles.push_back(title);
    }
    RegisterMousePress(args);
    [..]
//pseudo-code to simplify things , although original function does the exact same thing. 
    RegisterMousePress()
        {
            if(it_is_full)
            {
                db_cur->bFull= true;
                if(does db_main exist)
                {
                    db_main = new DATABLOCK;
                    db_main = db_cur;
                    db_main->next = NULL;
                }
                else
                {
                    db_cur->next = new DATABLOCK;
                    db_cur = db_cur->next;
                    db_cur->next = NULL;

                }
                SetEvent(eProcessed); //tell thread 2 there is at least one datablock ready
            }
            else
            {
            write_to_it();
            }
        }
//this is actual code and entry point of thread 2 and my attempy at synchronization
    DWORD WINAPI InitQueueThread(void* Param)
    {
        DWORD rc;
        DATABLOCK* k;
        SockWClient writer;
        k = db_main;
        while(true)
        {
            rc=WaitForSingleObject(eProcessed,INFINITE);
            if (rc== WAIT_OBJECT_0)
            {   
                do
                {
                    if(k->bPrsd)
                    {
                        continue;
                    }
                    else
                    {   
                        if(!k)
                        {break;}
                        k->bPrsd = TRUE;
    #ifdef DEBUG_NET
                        SendStruct(...);
    #endif

                    }
                    if(k->next == NULL || k->next->bPrsd ==TRUE || !(k->next->bFull))
                    {
                        ResetEvent(eProcessed);
                        break;
                    }

                } while (k = k->next); // next element after each loop
            }
        }
        return 1;

    }

Подробности:

Теперь что-то заставляет меня поверить, что ошибка не там, потому что ошибка подстроки очень редка. Я смог воспроизвести это только со 100% вероятностью, когда нажимал Mouse_Down+Wnd+Tab для прокрутки окон и удерживал ее нажатой в течение некоторого времени (хотя это, безусловно, случалось и в других случаях). Я не публикую весь код, потому что он немного велик и путаница неизбежна. Если ошибки здесь нет, я отредактирую сообщение и добавлю больше кода.

заранее спасибо


person ᴘᴀɴᴀʏɪᴏᴛɪs    schedule 23.04.2012    source источник
comment
У вас есть какая-то синхронизация вашего объекта списка? Потому что я ничего не вижу в вашем коде.   -  person mkaes    schedule 23.04.2012
comment
Вы имеете в виду предотвращение одновременного доступа к объекту обоими потоками?   -  person ᴘᴀɴᴀʏɪᴏᴛɪs    schedule 23.04.2012
comment
Больше похоже на то, что оба потока одновременно изменяют объект.   -  person juanchopanza    schedule 23.04.2012


Ответы (2)


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

Если вне отправленной функции нет мьютекса или семафора, скорее всего, проблема в этом.

Все расчеты размера кажутся действительными для Windows, хотя я не пытался его запускать… и <= … -1 вместо < в i <= (itr->size()-1) и 2 вместо sizeof (wchar_t) в new wchar_t[_size/2](); немного странные.

person Potatoswatter    schedule 23.04.2012
comment
Спасибо за ваш ответ, я отредактировал вопрос и добавил детали моей попытки синхронизации, вы можете посмотреть на них - person ᴘᴀɴᴀʏɪᴏᴛɪs; 23.04.2012

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

Он состоит из одного мьютекса (используемого для защиты данных, удерживаемого в любое время, когда вы к ним обращаетесь) и условной переменной (= Событие в терминах Windows), которая будет передавать информацию о новых данных потребителю.

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

С потребителем сложнее — он должен получить мьютекс, проверить, не стали ли доступны новые данные, затем дождаться события с помощью функции SignalObjectAndWait, которая временно освобождает мьютекс, затем обработать вновь полученные данные, а затем освободить мьютекс.

person jpalecek    schedule 23.04.2012
comment
Спасибо за ваш ответ. Я пытаюсь применить то, что вы предложили, но я не уверен, как это сделать с помощью потоков Windows. Я знаю, что мне нужно вызвать CreateMutex, чтобы создать мьютекс и освободить его с помощью ReleaseMutex, но как мне его "получить"? Кроме того, с чем я должен позвонить SignalObjectAndWait, чего я жду и что я сигнализирую? - person ᴘᴀɴᴀʏɪᴏᴛɪs; 26.04.2012