В настоящее время я разрабатываю протокол маршрутизации на C ++ для использования в сетях, устойчивых к сбоям. Для этого ему необходимо распространять копии сообщений (а не самого сообщения) по сети. Чтобы проверить его производительность в простом моделировании, я абстрагировал большинство факторов, в результате чего пакеты моделировались простыми структурами, содержащими случайное значение данных, идентификатор, источник и место назначения.
Чтобы этот протокол работал, я определил следующие переменные класса:
std::list<packet> buffer
— этот список содержит все пакеты, которые этот узел имеет для передачи.std::list<packet*> toTransmit
— при каждой сетевой возможности между 2 или более узлами каждый узел определяет, какие пакеты следует передавать, поэтому этот отдельный список указателей сохраняетсяstd::list<packet*>::iterator priority_ix
— передача происходит в две фазы, пакеты с первым приоритетом передаются, с priority_ix
отслеживание местоположения в toTransmit
список.std::list<packet*>::iterator normal_ix
— только после передачи всех приоритетных пакетов может начаться нормальная передача, отслеживаемая этим итератором.Из-за того, что узел получает приоритетный пакет, он должен немедленно переслать, priority_ix
может быть сброшено к началу списка, в то время как обычные передачи должны продолжаться с того места, где он был прерван, поэтому необходимы два отдельных итератора.
Теперь о странном поведении. Обычно я использую ix = mylist.erase(ix)
удалить элемент, на который я сейчас указываю, после этого не имея недействительного итератора. Это всегда работало для меня, однако что-то не так в одном конкретном методе в моем коде. Этот метод работает с полученными ACK-сообщениями, что может привести к удалению пакета, на который указывает один или оба этих итератора.
void Node::receiveAck(u_int packet_id) {
std::list<packet>::iterator ix = std::find(buffer.begin(), buffer.end(), packet_id);
if(ix != buffer.end()) {
if((**priority_ix) == packet_id && (**normal_ix) == packet_id) {
priority_ix = toTransmit.erase(priority_ix);
normal_ix = priority_ix;
} else if((**priority_ix) == packet_id) {
priority_ix = toTransmit.erase(priority_ix);
}
else if((**normal_ix) == packet_id) {
normal_ix = toTransmit.erase(normal_ix);
}
else {
toTransmit.remove(&(*ix));
}
buffer.erase(ix);
}
}
Этому методу СЛЕДУЕТ делать то, что он проверяет, указывают ли два итератора на удаляемый пакет, и проверяет их перемещение перед удалением пакета из обоих toTransmit
а также buffer
, Кстати, если кому-то интересно, я написал метод оператора == для сравнения пакетов с u_ints, позволяющий std::find
найти правильный пакет в buffer
Я подтвердил, что он действительно вводит правильные операторы if с ix
имея правильное значение.
Однако вместо этого происходит то, что при вызове std::list::erase
функция, он увеличивает итератор до следующего допустимого местоположения, но фактически не удаляет элемент из списка, в результате чего недопустимые указатели сохраняются в toTransmit
вызывая SEGFAULTs где-то позже.
Я пытался обойти проблему, просто делая ++priority_ix
и / или ++normal_ix
в операторе if с последующим toTransmit.remove(&(*ix))
после всех заявлений. Это очищает элементы от toTransmit
Однако, если увеличенные итераторы указывали на последний элемент в toTransmit
, он будет указывать на тот же элемент после увеличения, а не на toTransmit.end()
, снова приводя к SEGFAULTs где-то еще в коде.
Странно, однако, что buffer.erase(ix)
Кажется, работает нормально. Я очень разочарован этой проблемой, так как часто использую итераторы и никогда не сталкиваюсь с подобным поведением. Любая помощь или указания, где посмотреть в коде, очень ценится.
Задача ещё не решена.
Других решений пока нет …