C++ thread lambda captured object can read but cannot erase - c++

I'm new in c++ so my problem could be very simple but I cannot solve it.
In my constructor I want to start detached thread which will be looping with class variable and removing old data.
userCache.hpp
struct UserCacheItem {
long m_expires;
oatpp::Object<User> m_user;
UserCacheItem(const long expires, const oatpp::Object<User> &user);
};
class UserCache {
private:
std::map<std::string, std::shared_ptr<UserCacheItem> > m_userCacheItem;
public:
UserCache();
void cacheUser(std::string payload, std::shared_ptr<UserCacheItem> &userCacheItem);
};
userCache.cpp
UserCache::UserCache()
{
std::thread thread([this]() mutable {
while (true){
auto curTimestamp = std::chrono::seconds(std::chrono::seconds(std::time(nullptr))).count();
for(auto &elem : m_userCacheItem){
if (curTimestamp > elem.second->m_expires){
std::cout << "Erasing element: " << elem.second->m_expires << std::endl;
m_userCacheItem.clear();
}
}
std::cout << "Cache size: " << m_userCacheItem.size() << " Current timestamp: " << curTimestamp << std::endl;
std::this_thread::sleep_for(std::chrono::seconds(10));
};
});
thread.detach();
}
When I reach line m_userCacheItem.clear(); I get segmentation fault. Ofcourse if if block is false line with cout cache sizie is printed properly.
So I can read my variable but I cannot modify it :(
Where I'm making error?

You cannot modify the map while you're iterating it
for(auto &elem : m_userCacheItem){
if (curTimestamp > elem.second->m_expires){
std::cout << "Erasing element: " << elem.second->m_expires << std::endl;
m_userCacheItem.clear();
}
}
If you want to erase an element, use std::map::erase:
for(auto & it = m_userCacheItem.begin(); it != m_userCacheItem.end();) {
if(condition) {
it = m_userCacheItem.erase(it);
} else {
++it;
}
}

Related

Why does incrementing this Deque's iterator write garbage to a struct that has been copied from it?

I am having an interesting problem. This C++ code is meant to simulate a job scheduler with different approaches to how they are scheduled. In my code meant to simulate SFS (shortest job first) scheduling, the struct contained in current_task has garbage written to each of its members, particularly processId and cpuBursts, which each assume a value that is around 134 million (for the first struct that is assigned to current_task, the processId is 100 and cpuBursts is 10. These values are populated correctly in the deque returned from the function that reads the input file). Each of the other members is zeroed out. I have attached some images containing my GDB debugging session in CLion.
My environment is the most recent version of CLion configured to use WSL with Ubuntu 20.04.2 LTS and G++-9. My language level is set to C++17.
Note that the for loop around line 65 is commented out; I did this to see if merely getting and operating on an iterator was causing my problem. Line 72 also causes this same issue.
#include <iostream>
#include <deque>
#include <fstream>
#include <sstream>
#include <optional>
#include <unistd.h>
#include <algorithm>
using namespace std;
enum State {
NEW,
WAITING,
READY,
RUNNING,
TERMINATED
};
typedef struct {
int processId;
int arrival_time;
int cpuBursts;
int priority;
State state;
string str() {
stringstream ss;
ss << "[Task] ID: " << processId << ", Arrival Time: " << arrival_time << ", CPU Bursts: "<< cpuBursts << ", Priority: " << priority << ", State: " << state;
return ss.str();
}
} Task;
//Prototypes
deque<Task> read_input();
int main();
void FCFS(deque<Task>);
void SJF(deque<Task>);
//Functions
int main() {
auto v = read_input();
// FCFS(deque<Task>(v));
SJF(deque<Task>(v));
return 0;
}
void SJF(deque<Task> tasks) {
//Note: This one doesn't bother considering arrival times since the order of execution and start times would be the same.
bool running = true; // Keep iterating?
int t = 0; // Time
Task* current_task = nullptr; // The current task
deque<Task> task_queue;
for (auto &task : tasks) {
// Set each task's state to ready. No point, really
task.state = READY;
}
cout << "Beginning Shortest Job First (SJF) execution of " << tasks.size() << " tasks." << endl;
while (running) {
// Populate the task queue with any new tasks if necessary
/*for (const auto &task : tasks) {
if (task.arrival_time == t) {
cout << "Task with ID " << task.processId << " arrived at t = " << t << endl;
task_queue.push_back(task);
}
}*/
auto it = tasks.begin()++;
// Sort the task queue by shortest time
sort(task_queue.begin(), task_queue.end(), [] (const Task& lhs, const Task& rhs) {
return lhs.cpuBursts < rhs.cpuBursts;
});
// The current_task will only be nullptr if this is the very first iteration at t = 0
// or a task had just finished executing on the previous iteration.
if (current_task == nullptr) {
// Since the tasks are sorted by CPU burst requirements, the front of the queue contains the shortest.
Task new_current_task = task_queue.front();
task_queue.pop_front();
cout << "Task with ID " << new_current_task.processId << " has begun execution at t = " << t << endl;
// Set the current task to RUNNING and update current_task.
new_current_task.state = RUNNING;
current_task = &new_current_task;
}
if (current_task->cpuBursts > 0) {
current_task->cpuBursts -= 1;
cout << "Task with ID " << current_task->processId << " has " << current_task->cpuBursts << " CPU bursts remaining." << endl;
} else {
cout << "Task with ID " << current_task->processId << " has completed execution at t = " << t << endl;
current_task->state = TERMINATED;
if (task_queue.empty()) {
cout << "SJF execution is complete." << endl;
running = false;
}
current_task = nullptr;
}
t++;
}
}
void FCFS(deque<Task> tasks) {
//Note: This one doesn't bother considering arrival times since the order of execution and start times would be the same.
bool running = true; // Keep iterating?
int t = 0; // Time
Task* current_task = nullptr; // The current task
auto vec_iterator = tasks.begin(); // Iterator used to navigate the deque containing the tasks
for (auto &task : tasks) {
// Set each task's state to ready. No point, really
task.state = READY;
}
cout << "Beginning First Come First Serve (FCFS) execution of " << tasks.size() << " tasks.";
while (running) {
// The current_task will only be nullptr if this is the very first iteration at t = 0
// or a task had just finished executing on the previous iteration.
if (current_task == nullptr) {
Task new_current_task = *(vec_iterator++); // Get the next task from the iterator; increment iterator by 1
cout << "Task with ID " << new_current_task.processId << " has begun execution at t = " << t << endl;
// Set the current task to RUNNING and update current_task.
// State really isn't used for anything in FCFS so it doesn't really matter.
new_current_task.state = RUNNING;
current_task = &new_current_task;
}
if (current_task->cpuBursts > 0) {
current_task->cpuBursts -= 1;
cout << "Task with ID " << current_task->processId << " has " << current_task->cpuBursts << " CPU bursts remaining." << endl;
} else {
cout << "Task with ID " << current_task->processId << " has completed execution at t = " << t << endl;
current_task->state = TERMINATED;
if (vec_iterator == tasks.end() && current_task->state == TERMINATED) {
cout << "FCFS Execution complete." << endl;
running = false;
}
current_task = nullptr;
}
t++;
}
}
deque<Task> read_input() {
ifstream infile("input.txt");
string line;
deque<Task> tasks;
if (!infile.is_open()) {
cerr << "Couldn\'t find or access input.txt." << endl;
return tasks;
}
while (getline(infile, line)) {
Task t;
istringstream iss(line);
if (!(iss >> t.processId >> t.arrival_time >> t.cpuBursts >> t.priority)) {
cout << "Skipping line due to text..." << endl;
continue;
}
t.state = NEW;
tasks.push_back(t);
}
return tasks;
}
The watcher of interest is current_task and its members. At this point, everything is fine.
This is right towards the end of the first iteration. It maintains its correct state until right after the 2nd iteration starts.
Without fail, these garbage values are always around 134 million. I doubt this is significant though. This same behavior is exhibited when the for loop on lines 65-70 is uncommented and run and line 72 is commented out, so I have isolated this to the iterators, as far as I can tell.
Here you’re making a copy of the object:
Task new_current_task = *(vec_iterator++);
Task new_current_task = task_queue.front();
Then you put a pointer it into your current_task, and exit the scope. The object is destroyed. You have a dangling pointer to a destroyed object. Dereferencing it can do anything, in this case the memory is reused for something else and you see garbage.
The changes you make are also for a temporary object, not the one in the deque anyway. You need to use references, or some other way of keeping the object alive.
Note that you’re also calling the methods without references, so the deques also are copied, including the objects inside. Might not be what you want either.

about : Scope of exception object in C++ : why don't I get the copy?

In question about scope of exception it is stated by Aj. that throw and catch clauses will create copies of the exception (unless reference is used I guess)
I tried myself a small toy code and I don't understand the result. here :
//g++ 7.4.0
#include <iostream>
using namespace std;
struct Some_error {
Some_error(float code):err_code(code){ cout << "Some_error(" << err_code << ")\n"; }
~Some_error() { cout << "~Some_error(" << err_code << ")\n"; }
Some_error(const Some_error& o):err_code(o.err_code+0.1) { cout << "Some_error(copy::" << err_code << ")\n"; }
Some_error(Some_error&& o):err_code(std::move(o.err_code)+.01){ cout << "Some_error(move::" << err_code << ")\n"; }
int get_code() const { return err_code; }
private : float err_code;
};
int do_task() {
if ( false ) return 42; else throw Some_error {1};
cout << "end do_task\n" ;
}
void taskmaster(){
try { auto result = do_task(); cout << "the answer is " << result << "\n" ; }
catch (Some_error e) { cout << "catch Some_error : " << e.get_code() << "\n" ; }
cout << "end taskmaster\n" ;
}
int main() { taskmaster(); }
the trace I get is as follows :
Some_error(1)
Some_error(copy::1.1)
catch Some_error : 1
~Some_error(1.1)
~Some_error(1)
end taskmaster
Now first, as I used no reference here, according to Aj., I would expect 2 copies to happen.
And second, there was a copy, that set err_code to 1.1, but the display is still 1.
Remark: just to be complete, I changed the catch to : catch(Some_error& e),
and then the trace looks fine to me :
Some_error(1)
catch Some_error : 1
~Some_error(1)
end taskmaster
I would expect 2 copies to happen.
Why? Only one copy is made by the catch block. Where would the second copy happen?
set err_code to 1.1, but the display is still 1.
Because get_code returns an int, so the floating point value gets truncated.

How to print a map

I am trying to print a map in an organized way. My map is defined like this:
map<std::string,std::vector<message *> > data;
where message is a struct like this:
struct message{
static unsigned int last_id;
unsigned int id;
std::string msg;
std::string timestamp;
message(const std::string& recvbuf_msg,const std::string& a_timestamp) :
msg(recvbuf_msg), timestamp(a_timestamp), id(++last_id)
{
}
};
I tried this way of printing it:
std::cout << (data[username]).at(0)->msg << std::endl;
But it gives a debug error when reaching that function, how can i solve it?
Error R6010 - abort() has been called suggests that either there is no entry for key username in the map, or the vector of messages for that user is empty. You need to make sure the containers are nonempty before accessing elements. It is a good idea to use iterators, here is an example of how to print the messages for all usernames:
for(auto mapIt = data.cbegin(); mapIt != data.cend(); ++mapIt)
{
std::cout << "printing data for " << mapIt->first << ":" << std::endl;
for(auto vectIter = mapIt->second.cbegin(); vectIter != mapIt->second.cend(); ++vectIter)
{
std::cout << (*vectIter)->msg << ", " << (*vectIter)->timestamp << ", "
<< (*vectIter)->id << std::endl;
}
}
The code uses auto, so if you are not using a C++11 compliant compiler, you will have to write the iterator types yourself.

Avoid using container to call a list of functions?

I have a list of functions that return bools. I want to iterate through the list of functions and write a message for each one "Test 1 passed", "Test 2 failed" etc.
My current solution is to create a vector of function pointers, push back each function and then loop through the vector. Code below. Is there a way to avoid the container without repeating the generic message (pass/fail) code for each test (imagine there would be hundreds of tests). It feels as if the vector is unnecessary or that there must be a more elegant solution for this.
typedef bool (*Tests)();
std::vector<Tests> tests;
tests.push_back(FASTA_FILE_READER_TEST);
tests.push_back(EXACT_MATCH_TEST);
for (int i = 0; i < tests.size(); i++) {
std::cout << "Test " << i + 1
<< (tests[i]() ? " PASSED" : " FAILED")
<< std::endl;
}
Is there anything stopping you using an array?
#include <iostream>
bool FASTA_FILE_READER_TEST() { return false; }
bool EXACT_MATCH_TEST() { return false; }
int main()
{
typedef bool (*Tests)();
Tests tests[] = {FASTA_FILE_READER_TEST, EXACT_MATCH_TEST};
for (int i = 0; i < sizeof(tests)/sizeof(Tests); i++) {
std::cout << "Test " << i + 1
<< (tests[i]() ? " PASSED" : " FAILED")
<< std::endl;
}
}
You could use a function to do that:
template<typename Functor>
void test(Functor& functor){
static int i = 0;
bool ret = functor();
if(ret){
std::cout << "Test " << i++ << " passed" << std::endl;
} else {
std::cout << "Test " << i++ << " failed" << std::endl;
}
}
void main(){
test(FASTA_FILE_READER_TEST);
test(EXACT_MATCH_TEST);
}
If you can use C++11 features:
#include <array>
#include <iterator>
#include <algorithm>
#include <iostream>
typedef bool (*Test)();
std::array<Test, 2> tests {{ FASTA_FILE_READER_TEST, EXACT_MATCH_TEST }};
void TestAll()
{
size_t i = 1;
std::for_each(std::begin(tests), std::end(tests),
[&i](Test& t)
{
std::cout << "Test " << i++ << (t() ? " PASSED" : " FAILED") << std::endl;
});
}
Demo.
It's another way of doing what you've already got (and your way is just fine, IMO). If the extra capacity a vector might have set aside bothers you, you can call shrink_to_fit() on it when you're done pushing back.
Create a class for each test. Then one static instance of each class.
Contructors of classes runs tests.
This of course may cause problems, because tests are executed before main() function is called.

C++ STL map with custom comparator storing null pointers

I'm trying to write a copy constructor for an object managing a STL map containing pointers, where the key is a string. However, when I attempt to insert new values in the map, the pointers are set to NULL:
// ...
for(std::map<std::string, data_base*, order>::const_iterator it = other.elements.begin();
it != other.elements.end(); ++it){
data_base *t = it->second->clone();
std::cout << "CLONE: " << std::hex << t << std::endl;
elements[it->first] = t;
std::cout << "INSERTED: " << std::hex << elements[it->first] << std::endl;
}
// ...
other is the object being copied and elements the map. The clone() method returns a pointer to a new object (via new).
Running the code above I get something like:
CLONE: 0xcfbbc0
INSERTED: 0
I'm not a very experienced programmer and this issue is probably simple to fix, but I didnt find any solution to it searching around.
Thanks a lot for your time.
I don't see any problem with this code, other than maybe
std::map<std::string, data_base*, order>::const_iterator it
Here order gives the key comparator to use to sort the pairs contained in the map (often implemented as a tree).
Maybe you're doing something wrong in it, making your [] operator don't find the right ke, making your last line logging a new pair with a null ptr.
First, try without that order, using the default key-comparator (std::less), then if it don't work, post your order definition and the map declaration. If it's not enough, just provide a simple complete program that reproduce the problem.
I just wrote a simple similar test, using the default key-comparator :
#include <map>
#include <string>
#include <iostream>
struct Data
{
int k;
Data* clone() { return new Data(); }
};
typedef std::map< std::string, Data* > DataMap;
DataMap data_map;
int main()
{
data_map[ "hello" ] = new Data();
data_map[ "world" ] = new Data();
DataMap other_map;
for( DataMap::const_iterator it = data_map.begin(); it != data_map.end(); ++it)
{
Data*t = it->second->clone();
std::cout << "CLONE: " << std::hex << t << std::endl;
other_map[it->first] = t;
std::cout << "INSERTED: " << std::hex << other_map[it->first] << std::endl;
}
std::cin.ignore();
return 0;
}
On VS2010SP1, this outputs :
CLONE: 00034DD0
INSERTED: 00034DD0
CLONE: 00035098
INSERTED: 00035098
So it should be the problem, or maybe you're doing something wrong before.
Try this out, to help debug the issue. I'd recommend double-checking that the order function is correct. You can remove it to use std::less<T>, which is known to work.
// ...
typedef std::map<std::string, data_base*, order> string_db_map;
for(string_db_map::const_iterator it = other.elements.begin();
it != other.elements.end();
++it)
{
data_base *t = it->second->clone();
std::cout << "CLONE: " << std::hex << t << std::endl;
std::pair<string_db_map::iterator, bool) result = elements.insert(
string_db_map::value_type( it->first, t));
if ( !result.second )
{
std::cout << "element['" << it->first << "'] was already present, and replaced." << std::endl;
}
std::coud << "INSERTED [iterator]: " << std::hex << (*result.first).second << std::endl;
std::cout << "INSERTED [indexed]: " << std::hex << elements[it->first] << std::endl;
}
// ...