map::erase sometimes does not erase the entry from the map - c++

I'm having a sporadic problem right now where map::erase() says it worked, but the map sometimes still has the element in it.
auto iterator = device_map.begin(); // std::map<std::string,Device*>
size_t nDeleted = 0;
while (iterator != map.end()) {
Device* device = device_map[iterator->first];
if (device->done()) {
device->close();
cout << "Erasing " << iterator->first << endl;
nDeleted = device_map.erase(iterator->first);
delete device;
}
++iterator;
}
This is called in a std::thread every 500ms. I've tested it by printing the contents of the map before and after the loop, like this:
cout << "device_map = ";
for (std::map<string, Device*>::iterator it = device_map.begin(); it != device_map.end(); ++it) {
cout << it->first << " = " << it->second << "; ";
}
cout << endl;
Sometimes, even if nDeleted == 1, the contents of device_map is the same before and after unmapping the element (except that *device == nullptr because of the delete).
I've also put a breakpoint and device_map.size() is indeed unchanged after calling device_map.erase() does not work.
I have tried this other version of map::erase with the same results:
auto iterator = device_map.begin(); // std::map<std::string,Device*>
size_t nDeleted = 0;
while (iterator != map.end()) {
Device* device = device_map[iterator->first];
if (device->done()) {
device->close();
cout << "Erasing " << iterator->first << endl;
iterator = device_map.erase(iterator);
delete device;
} else {
++iterator;
}
}
Is there something that I'm doing wrong?
Thank you,
Fred

As #Jeffrey suggested, the issue was with another thread accessing the std::map object while map::erase was being called. Adding a mutex solved the problem.

Related

Design pattern to create smart references to elements in a vector

Because references in a vector point to locations of memory and not the abstract element, it can cause a few problems when altering the memory of the vector.
If a reference points to an element in a vector, and then that element is shuffled to another spot in the vector, the reference doesn't track the element, and will point to incorrect data after the shuffle.
If a element is invalidated, you can still access that elements contents without any safety checks, if you declared a references before invalidating the element.
If the vector resizes, all current references may be invalidated.
I wrote an example program that demonstrates all three problems.
#include <iostream>
#include <vector>
struct entity { //Simple struct of data.
bool alive;
float data;
};
class manager {
std::vector<entity> vec;
size_t count; // Amount of currently alive entities
public:
//Reserves initial_amount of entities, all set to dead, count set to 0.
manager(size_t initial_amount) : vec(initial_amount, { false, 0.0f }), count(0) {}
entity& create(float f) {
vec[count] = {true, f};
return vec[count++];
}
void refresh() { //Two iterators, one starts at the front of the vector, the other at
size_t front = 0; //count. The front iterator searches for dead entities and swaps them
size_t back = count; //with alive entities from the back iterator. For each swap we decrement
//count by 1, with the final result being all alive entities are between
while(true) { //0 and count.
for( ; true; ++front) {
if (front > back) return;
if (!vec[front].alive) break;
}
for( ; true; --back) {
if (vec[back].alive) break;
if (back <= front) return;
}
std::swap(vec[back], vec[front]);
--count;
++front;
--back;
}
}
void grow(size_t n) {
vec.resize(n);
}
void print() { //Prints all alive entities.
for (size_t index = 0; index < count; index++)
std::cout << vec[index].data << " ";
std::cout << std::endl;
}
};
int main() {
using namespace std;
manager c(10);
entity& d1 = c.create(5.5);
entity& d2 = c.create(10.5);
entity& d3 = c.create(7.5);
// Correct behavior
cout << d1.data << endl; // 5.5
cout << d2.data << endl; // 10.5
cout << d3.data << endl; // 7.5
cout << endl;
d2.alive = false; // "Kill" the entity
c.refresh(); // removes all dead entities. (this will swap d2's and d3's data in the vector,
// but wont change the locations they point to)
// Oh no! d2 and d3 still point to the same locations in the vector and now their data
// is incorrect after the swap, also d2 is dead maybe that should just be an error.
cout << d1.data << endl; // 5.5
cout << d2.data << endl; // 7.5
cout << d3.data << endl; // 10.5
cout << endl;
c.print(); // Correct behavior, prints only alive entities.
cout << endl;
d3.data = 6.5; // Trying to change the value of d3, which should still be alive.
c.print(); // Error, because d3 still points to the 3rd slot the intended value hasn't been changed.
cout << endl;
c.grow(10000);
cout << d1.data << endl; // After resize all these references are invalidated,
cout << d2.data << endl; // and using them is undefined behavior.
cout << d3.data << endl;
return 0;
}
Is there a design pattern to create a smart reference or proxy type that solves these problems? An object that will track its elements position in the vector, does specific behavior if the element is alive or dead, and stay valid after a resize?
I'm fine with the implementation of the smart/proxy reference to not be an actual reference, could be a pointer, integer index, or whatever. But this is specifically for elements in a vector, not a linked-list, map, etc.
With std::vector<std::shared_ptr<entity>>, you may have the security you want:
class manager {
std::vector<std::shared_ptr<entity>> vec;
public:
//Reserves initial_amount of entities
explicit manager(size_t initial_amount) { vec.reserve(initial_amount); }
std::weak_ptr<entity> create(float f) {
vec.push_back(std::make_unique<entity>(entity{true, f}));
return vec.back();
}
void refresh() {
vec.erase(std::remove_if(vec.begin(), vec.end(),
[](const auto& ent) {return !ent->alive;}),
vec.end());
}
void grow(size_t n) { vec.reserve(n); }
void print() { //Prints all alive entities.
for (const auto& ent : vec)
std::cout << ent->data << " ";
std::cout << std::endl;
}
};
And then the test:
int main() {
manager c(10);
auto d1 = c.create(5.5);
auto d2 = c.create(10.5);
auto d3 = c.create(7.5);
// Correct behavior
if (auto e = d1.lock()) std::cout << e->data << std::endl; else std::cout << "Die\n"; // 5.5
if (auto e = d2.lock()) std::cout << e->data << std::endl; else std::cout << "Die\n"; // 10.5
if (auto e = d3.lock()) std::cout << e->data << std::endl; else std::cout << "Die\n"; // 7.5
std::cout << std::endl;
if (auto e = d2.lock()) e->alive = false; // "Kill" the entity
c.refresh(); // removes all dead entities.
if (auto e = d1.lock()) std::cout << e->data << std::endl; else std::cout << "Die\n"; // 5.5
if (auto e = d2.lock()) std::cout << e->data << std::endl; else std::cout << "Die\n"; // Die
if (auto e = d3.lock()) std::cout << e->data << std::endl; else std::cout << "Die\n"; // 10.5
std::cout << std::endl;
c.print(); // Correct behavior, prints only alive entities.
std::cout << std::endl;
if (auto e = d3.lock()) e->data = 6.5; // Trying to change the value of d3,
// which should still be alive.
c.print();
std::cout << std::endl;
c.grow(10000);
if (auto e = d1.lock()) std::cout << e->data << std::endl; else std::cout << "Die\n"; // 5.5
if (auto e = d2.lock()) std::cout << e->data << std::endl; else std::cout << "Die\n"; // Die
if (auto e = d3.lock()) std::cout << e->data << std::endl; else std::cout << "Die\n"; // 6.5
}
Demo

map/set iterator not dereferencable. Multimap container isse

I'm geting this error message map/set iterator not dereferencable When trying to get value by key in multimap. In this code I'm trying to show nonoriented graph represented by adjacency list (vector<Vertex*> vertexList)
void NonOrGraph::show() {
cout << endl;
multimap<int, int> used;
for (int i = 0; i < vertexList.size(); i++) {
if (vertexList[i]->adjMap.empty()) {
cout << vertexList[i]->index << " isolated";
} else {
for(map<Vertex*, int>::iterator it = vertexList[i]->adjMap.begin();
it != vertexList[i]->adjMap.end();
it++)
{
int from = vertexList[i]->index;
int to = it->first->index;
int weight = it->second;
used.insert(pair<int, int>(from, to));
if (used.find(to)->second != from) {
cout << from << " <--(" << weight << ")--> " << to << endl;
}
}
}
}
cout << "\n\n";
}
The problem is likely here:
if (used.find(to)->second != from) {
If to is not in used, used.find() will return used.end(), which you then dereference. It is undefined behavior to dereference the end() iterator, which in this case manifests by giving you a runtime error.
You have to check the iterator against end() first:
std::multimap<int, int>::iterator multi_it = used.find(to);
if (multi_it != used.end() && multi_it->second != from) {
// ^^^^^^^^^^^^^^^^^^^^
// now, it's safe to dereference

Possible bug in std::unordered_map

I was trying to find the culprit behind a segfault. My debugger told be that there was no data for variable that the error was at. Every 10 seconds, there is a little script in my C++ code that runs. It does "garbage collection" and deletes some "sessions" that are probably dead.
To perform this efficiently, I use a timestamp -- when was the data last accessed. If the data is more than 10 seconds old, it is dead. There is a keepalive command that triggers every 4 seconds on the client.
To perform this GC, I loop through an std::unordered_map and substract the current time since epoch from the time stored as the value in that pair. If the time is too large, I add it to an std::vector that holds the keys to be deleted (yeah, I know it can be optimized to skip this step).
The problem that I was facing is that it loops right the first time. However, thereafter, I get a segfault, which points to the iterator value to be greater than size of the map.
Just switching back over to a standard std::map fixed the entire problem!
I shall attach the function that does all of this. All of the code is available at http://github.com/yash101/DrawingPad
Now, the code [{sourcedir}/source/Session.cxx]:
void SessionHost::cron()
{
while(true)
{
std::this_thread::sleep_for(std::chrono::seconds(10));
if(DEBUG)
{
std::cout << "Cron has started!" << std::endl;
}
while(!locky_thingy.try_lock_for(std::chrono::milliseconds(MUTEX_TIMEOUT)))
{}
int timethrough = 0;
std::vector<std::string> del;
for(std::map<std::string, long>::iterator ite = timestamp.begin(); ite != timestamp.end(); ++ite)
{
timethrough++;
std::cout << "Time through: " << timethrough << std::endl;
std::string curkey = ite->first;
long curval = ite->second;
std::cout << "Key: " << curkey << std::endl;
if(DEBUG)
{
std::cout << "Checking " << curkey << " with old ts of " << curval << std::endl;
}
u_int64_t curtm = std::chrono::duration_cast<std::chrono::milliseconds> (std::chrono::system_clock::now().time_since_epoch()).count();
if(DEBUG)
{
std::cout << "Current time: " << curtm << std::endl;
}
if(curtm - curval > SESSION_TIMEOUT)
{
if(DEBUG)
{
std::cout << "Deleted session handle: [" << curkey << "]" << std::endl;
}
del.push_back(curkey);
}
else
{
if(DEBUG)
{
std::cout << "Kept back session handle: [" << curkey << "]" << std::endl;
}
}
for(unsigned int i = 0; i < del.size(); i++)
{
timestamp.erase(del[i]);
data.erase(del[i]);
std::cout << "Erasing: " << del[i] << std::endl;
}
}
locky_thingy.unlock();
}
}
You have:
for(std::map<std::string, long>::iterator ite = timestamp.begin();
ite != timestamp.end(); ++ite)
{
// ...
for(unsigned int i = 0; i < del.size(); i++)
{
timestamp.erase(del[i]); // <--
// ...
}
}
In an unordered_map, erasing can invalidate iterators. So you can't erase while you're traversing - try to come up with a different algorithm. (I'm assuming some version of your question involves timestamp being an unordered_map - although there's no reference to this type in your code).
I think the error is here
for(unsigned int i = 0; i < del.size(); i++)
{
timestamp.erase(del[i]);
data.erase(del[i]);
std::cout << "Erasing: " << del[i] << std::endl;
}
} // <---------------- this is the end of the iterator loop
It should be moved up before the for loop so it doesn't invalidate.
} // <---------------- this is the end of the iterator loop
for(unsigned int i = 0; i < del.size(); i++)
{
timestamp.erase(del[i]);
data.erase(del[i]);
std::cout << "Erasing: " << del[i] << std::endl;
}
The data.erase might also have a fault if it is a vector.
If you have a vector you need to erase for you should mark the records and use
data.erase(std::remove_if(data.begin(), data.(end), CheckMark));
remove_if moves all valid data to the start of data, erase then erases from after the last valid.

Deleting and object in a position from a deque

I have this code:
bool tuple_compare(boost::tuple<ppa::Node*, ppa::Node*, ppa::Node*, bool> &tuple_from_done)
{
for(int i = 0; i < deque_wait.size(); i++) {
boost::tuple<ppa::Node*, ppa::Node*, ppa::Node*, bool> tuple_from_wait = deque_wait.at(i);
ppa::Node *father = boost::get<0>(tuple_from_wait);
ppa::Node *son = boost::get<0>(tuple_from_wait);
ppa::Node *second_son = boost::get<2>(tuple_from_wait);
bool has_seq = boost::get<3>(tuple_from_wait);
cout << "checking this two " << boost::get<1>(tuple_from_wait)->get_name() << " bool sequence "
<< boost::get<1>(tuple_from_wait)->node_has_sequence_object << " and this "
<< boost::get<2>(tuple_from_wait)->get_name() << " bool seq " << boost::get<2>(tuple_from_wait)->node_has_sequence_object
<< " with " << boost::get<0>(tuple_from_done)->get_name() << endl;
if(boost::get<0>(tuple_from_done)->get_name() == boost::get<1>(tuple_from_wait)->get_name()
|| boost::get<0>(tuple_from_done)->get_name() == boost::get<2>(tuple_from_wait)->get_name())
{
cout << " found in here this we need to check if there is something if the sons have a sequences!!!! " << endl;
if(boost::get<1>(tuple_from_wait)->node_has_sequence_object == true && boost::get<2>(tuple_from_wait)->node_has_sequence_object == true)
{
cout << " ding, ding, we have one ready!!!" << endl;
return true;
}
else
{
cout << "not ready yet" << endl;
}
}
}
return false;
}
Now I need to delete the object that is found in the line "ding, ding", but I don't know how to do it, I know the iterators are used well I actually have to delete this tuple from the deque_wait and move it to the deque_run, but I don't really understand those yet, so can you help me, thanks.
deque_wait.erase(deque_wait.begin() + i);
// ^^^^^^^^^^^^^^^^^^^^^^
// that's an iterator
deque supports random access iterators, which are very much like pointers (in fact, pointers are a type of random access iterator), so you can just get the begin iterator and add an integer to it to get the offset, just like you could do with a pointer.

How to detect first or last element iterating over a container?

How to do the following in more stylish/short way?
for(i=container.begin(); i!=container.end(); ++i) {
if (i!=container.begin()) {
cout << ", ";
}
cout << *i;
j=i;
if (++j==container.end()) {
cout << "!" << endl;
}
}
Solutions like foreach are acceptable (actions on first and last elements need to be configurable, though).
P.S.
There are many answers that are handling first element, but not last. Here is what I mean by handling last element:
for(i=container.begin(); i!=container.end(); ++i) {
j=i;
if (i==container.begin()) {
cout << "[" << *i << "]" << endl;
} else if (++j==container.end()) {
cout << ", (" << *i << ")" << "!" << endl;
} else {
cout << ", " << *i;
}
}
Don't you think it's very easy to handle first element outside the cycle body? The real problem is the last one! I'm sorry for not being able to clarify the important point asking the question. I think I'll just accept the top ranked answer eventually.
Boost has next / prior which can sometimes help in such situations.
for(i=container.begin(); i!=container.end(); ++i) {
if (boost::next(i) == container.end()) {
std::cout << "!" << std::endl;
}
}
Although for this specific case, I'd simply output the first element, loop from second till last while always outputting the ',' and then output the '!' after the loop has ended. (as others have suggested already)
I don't see the point in moving the special cases inside the loop, and then checking inside the loop for them....
My advice here would be: there is no point in detecting anything within this loop !
Since your special cases are at the beginning and the end of your container, it is easy to remove their processing from within the loop.
The following function will print the contents of any container class whose elements can be <<'ed to an std::ostream:
template < class Container >
void print(Container const & container)
{
typename Container::const_iterator current = container.begin();
typename Container::const_iterator const end = container.end();
if (current != end)
{
std::cout << *current;
for (++current; current != end; ++current)
{
std::cout << ", " << *current;
}
std::cout << "!" << std::endl;
}
}
In your code,
if (i==container.end()) {
cout << "!" << endl;
}
will never happen.
My own approach would be to use the container size (I think size() is now constant time for all Standard Library containers). Maintain a count in the loop and you are at the end when count == size() - 1, and at the beginning when count == 0, obviously.
As container is not defined by you, I used the simplest - vector
template <class T>
string vector_join( const vector<T>& v, const string& token ){
ostringstream result;
for (typename vector<T>::const_iterator i = v.begin(); i != v.end(); i++){
if (i != v.begin()) result << token;
result << *i;
}
return result.str();
}
//usage
cout << vector_join( container, ", " ) << "!";
Shift the ++i a bit:
i = container.begin();
while(i != container.end()) {
if (i != container.begin()) {
cout << ", ";
}
cout << *i;
if (++i == container.end()) {
cout << "!" << endl;
}
}
template < class TContainerType>
void print(TContainerType const & i_container)
{
typename TContainerTypeconst ::const_iterator current = i_container.begin();
typename TContainerTypeconst ::const_iterator const end = i_container.end();
if(current != end)
{
std::cout << *current++;
while(current != end)
std::cout << ", " << *current++;
}
std::cout << "!" << std::endl;
}
Take the second part out of the loop.
for(i=container.begin(); i!=container.end(); ++i) {
if (i != container.begin()) {
cout << ", ";
}
cout << *i;
}
cout << "!" << endl;