cancel
Showing results for 
Search instead for 
Did you mean: 

std::remove_if broken?

JE
Associate II

Hi everyone,

I have an odd behaviour here. I got a STM32L496 with FreeRTOS on it. The CPU is very busy (15 task running, flash size is almost 1MB). I am using the latest STM32CubeIDE 1.14.1.

Now I'd like to remove something from a std::vector, so I do the following:

 

bool NotifierStillVal(Notification *n) { return n->stillValid() == false; }
//...
auto newEnd = std::remove_if(notifications.begin(), notifications.end(), NotifierStillVal);

 

I already removed the lambda to minimise the stuff that could go wrong. Now, I obviously know that remove_if() does only resort the vector and does not actually delete something. This is why I do this afterwards:

 

for (auto it = newEnd; it != notifications.end(); ++it)
    delete *it;
notifications.erase(newEnd, notifications.end());

 

 

Now here is my debug test:

 

	volatile uint16_t s1 = notifications.size();
	volatile Notification *pre0 = s1 > 0 ? notifications[0] : nullptr;
	volatile Notification *pre1 = s1 > 1 ? notifications[1] : nullptr;

	/* move not anymore valid stuff to the end */
    auto newEnd = std::remove_if(notifications.begin(), notifications.end(), NotifierStillVal);
    	//[](Notification *n) { return n->stillValid() == false; });

	volatile Notification *post0 = s1 > 0 ? notifications[0] : nullptr;
	volatile Notification *post1 = s1 > 1 ? notifications[1] : nullptr;

	if(post0 != nullptr and post0 == post1)
	{
		printf("pre %lX %lX\n", (uint32_t)pre0, (uint32_t)pre1);
		printf("post %lX %lX\n", (uint32_t)post0, (uint32_t)post1);
		printf("sV%d\n", ((Notification *) post0)->stillValid());
		osDelay(20);
	}

 

 

And it sometime produces the following output:

 

pre 20037890 200378E8
post 200378E8 200378E8
sV1
del 200378E8 1
deleted 200378E8 in list@0 (2 1)

 

At first I have two different elements in the vector. After remove_if() I still got 2 elements in vector (.size() confirms that btw) but they point to the same object. So I already lost 20037890. 200378E8 will get freed afterwards (by the second entry) and removed from the vector (again, only the second entry), which leaves me with invalid memory (as the first entry remains in the vector) resulting in a HardFault very soon.

The observed behaviour is always the same. It looses a points, got 2 identical points in list the therefore HardFaults. std::vector<Notification *>notifications is only access within one task and is even further secured by a RW semaphore acquired as writting (single access).

Any ideas??? Many thanks in advance!
Juergen

1 ACCEPTED SOLUTION

Accepted Solutions
JE
Associate II

OK I think i fixed it myself...
Don't know why but i assumed the newEnd to end() would be a perfect remaining vector. turns out it isn't. So I reverted everything back before chatGPT brought me on the wrong trace 🙂

 

	notifications.erase(std::remove_if(notifications.begin(), notifications.end(), [](Notification *n)
			{ if(n->stillValid()) return false; delete n; return true; }), notifications.end());

 

This should work, right?

Bests,

Juergen

 

View solution in original post

3 REPLIES 3
JE
Associate II

Just for completion, here is the complete code that even produces the 'delete' messages:

 

	volatile uint16_t s1 = notifications.size();
	volatile Notification *pre0 = s1 > 0 ? notifications[0] : nullptr;
	volatile Notification *pre1 = s1 > 1 ? notifications[1] : nullptr;

	/* move not anymore valid stuff to the end */
    auto newEnd = std::remove_if(notifications.begin(), notifications.end(), NotifierStillVal);
    	//[](Notification *n) { return n->stillValid() == false; });

	volatile Notification *post0 = s1 > 0 ? notifications[0] : nullptr;
	volatile Notification *post1 = s1 > 1 ? notifications[1] : nullptr;

	if(post0 != nullptr and post0 == post1)
	{
		printf("pre %lX %lX\n", (uint32_t)pre0, (uint32_t)pre1);
		printf("post %lX %lX\n", (uint32_t)post0, (uint32_t)post1);
		printf("sV%d\n", ((Notification *) post0)->stillValid());
		osDelay(20);
	}

    /* free invalid stuff */
    Notification *del[3] = {nullptr, nullptr, nullptr};
    uint16_t d=0;
    for (auto it = newEnd; it != notifications.end(); ++it)
    {
    	del[d++] = *it;
    	printf("del %lX %d\n", (uint32_t) *it, (*it)->stillValid());
    	osDelay(20);
        delete *it;
    }

    /* remove delete stuff from vector */
    notifications.erase(newEnd, notifications.end());


    volatile uint16_t s2 = notifications.size();
    uint16_t i=0;
    for(auto note : notifications)
    {
    	if(note == del[0] or note == del[1] or note == del[2])
    		printf("deleted %lX in list@%u (%u %u)\n", (uint32_t) note, i, s1, s2);
    	i++;
		osDelay(20);
    }

 

Please note: 'del XXXXX 1' means actually that the entry is still valid und should not have been makes for deletion by remove_if...

 

JE
Associate II

Another thought: what if the output of NotifierStillVal(Notification *n) changes during execution? Would this cause this behaviour??

JE
Associate II

OK I think i fixed it myself...
Don't know why but i assumed the newEnd to end() would be a perfect remaining vector. turns out it isn't. So I reverted everything back before chatGPT brought me on the wrong trace 🙂

 

	notifications.erase(std::remove_if(notifications.begin(), notifications.end(), [](Notification *n)
			{ if(n->stillValid()) return false; delete n; return true; }), notifications.end());

 

This should work, right?

Bests,

Juergen