No need for mutex, race conditions not always bad, do they? - c++

I'm getting this crazy idea that mutex synchronization can be omitted in some cases when most of us would typically want and would use mutex synchronization.
Ok suppose you have this case:
Buffer *buffer = new Buffer(); // Initialized by main thread;
...
// The call to buffer's `accumulateSomeData` method is thread-safe
// and is heavily executed by many workers from different threads simultaneously.
buffer->accumulateSomeData(data); // While the code inside is equivalent to vector->push_back()
...
// All lines of code below are executed by a totally separate timer
// thread that executes once per second until the program is finished.
auto bufferPrev = buffer; // A temporary pointer to previous instance
// Switch buffers, put old one offline
buffer = new Buffer();
// As of this line of code all the threads will switch to new instance
// of buffer. Which yields that calls to `accumulateSomeData`
// are executed over new buffer instance. Which also means that old
// instance is kinda taken offline and can be safely operated from a
// timer thread.
bufferPrev->flushToDisk(); // Ok, so we can safely flush
delete bufferPrev;
While it's obvious that during buffer = new Buffer(); there can still be uncompleted operations that add data on previous instance. But since disk operations are slow we get natural kind of barrier.
So how do you estimate the risk of running such code without mutex synchronisation?
Edit
It's so hard these days to ask a question in SO without getting mugged by couple of angry guys for no reason.
Here is my correct in all terms code:
#include <cassert>
#include "leveldb/db.h"
#include "leveldb/filter_policy.h"
#include <iostream>
#include <boost/asio.hpp>
#include <boost/chrono.hpp>
#include <boost/thread.hpp>
#include <boost/filesystem.hpp>
#include <boost/lockfree/stack.hpp>
#include <boost/lockfree/queue.hpp>
#include <boost/uuid/uuid.hpp> // uuid class
#include <boost/uuid/uuid_io.hpp> // streaming operators etc.
#include <boost/uuid/uuid_generators.hpp> // generators
#include <CommonCrypto/CommonDigest.h>
using namespace std;
using namespace boost::filesystem;
using boost::mutex;
using boost::thread;
enum FileSystemItemType : char {
Unknown = 1,
File = 0,
Directory = 4,
FileLink = 2,
DirectoryLink = 6
};
// Structure packing optimizations are used in the code below
// http://www.catb.org/esr/structure-packing/
class FileSystemScanner {
private:
leveldb::DB *database;
boost::asio::thread_pool pool;
leveldb::WriteBatch *batch;
std::atomic<int> queue_size;
std::atomic<int> workers_online;
std::atomic<int> entries_processed;
std::atomic<int> directories_processed;
std::atomic<uintmax_t> filesystem_usage;
boost::lockfree::stack<boost::filesystem::path*, boost::lockfree::fixed_sized<false>> directories_pending;
void work() {
workers_online++;
boost::filesystem::path *item;
if (directories_pending.pop(item) && item != NULL)
{
queue_size--;
try {
boost::filesystem::directory_iterator completed;
boost::filesystem::directory_iterator iterator(*item);
while (iterator != completed)
{
bool isFailed = false, isSymLink, isDirectory;
boost::filesystem::path path = iterator->path();
try {
isSymLink = boost::filesystem::is_symlink(path);
isDirectory = boost::filesystem::is_directory(path);
} catch (const boost::filesystem::filesystem_error& e) {
isFailed = true;
isSymLink = false;
isDirectory = false;
}
if (!isFailed)
{
if (!isSymLink) {
if (isDirectory) {
directories_pending.push(new boost::filesystem::path(path));
directories_processed++;
boost::asio::post(this->pool, [this]() { this->work(); });
queue_size++;
} else {
filesystem_usage += boost::filesystem::file_size(iterator->path());
}
}
}
int result = ++entries_processed;
if (result % 10000 == 0) {
cout << entries_processed.load() << ", " << directories_processed.load() << ", " << queue_size.load() << ", " << workers_online.load() << endl;
}
++iterator;
}
delete item;
} catch (boost::filesystem::filesystem_error &e) {
}
}
workers_online--;
}
public:
FileSystemScanner(int threads, leveldb::DB* database):
pool(threads), queue_size(), workers_online(), entries_processed(), directories_processed(), directories_pending(0), database(database)
{
}
void scan(string path) {
queue_size++;
directories_pending.push(new boost::filesystem::path(path));
boost::asio::post(this->pool, [this]() { this->work(); });
}
void join() {
pool.join();
}
};
int main(int argc, char* argv[])
{
leveldb::Options opts;
opts.create_if_missing = true;
opts.compression = leveldb::CompressionType::kSnappyCompression;
opts.filter_policy = leveldb::NewBloomFilterPolicy(10);
leveldb::DB* db;
leveldb::DB::Open(opts, "/temporary/projx", &db);
FileSystemScanner scanner(std::thread::hardware_concurrency(), db);
scanner.scan("/");
scanner.join();
return 0;
}
My question is: Can I omit synchronization for batch which I'm not using yet? Since it's thread-safe and it should be enough to just switch buffers before actually committing any results to disk?

You have a serious misunderstanding. You think that when you have a race condition, there are some specific list of things that can happen. This is not true. A race condition can cause any kind of failure, including crashes. So absolutely, definitely not. You absolutely cannot do this.
That said, even with this misunderstanding, this is still a disaster.
Consider:
buffer = new Buffer();
Suppose this is implemented by first allocating memory, then setting buffer to point to that memory, and then calling the constructor. Other threads may operate on the unconstructed buffer. boom.
Now, you can fix this. But it's just one the many ways I can imagine this screwing up. And it can screw up in ways that we're not clever enough to imagine. So, for all that is holy, do not even think of doing this ever again.

Related

Thread with expensive operations slows down UI thread - Windows 10, C++

The Problem: I have two threads in a Windows 10 application I'm working on, a UI thread (called the render thread in the code) and a worker thread in the background (called the simulate thread in the code). Ever couple of seconds or so, the background thread has to perform a very expensive operation that involves allocating a large amount of memory. For some reason, when this operation happens, the UI thread lags for a split second and becomes unresponsive (this is seen in the application as a camera not moving for a second while the camera movement input is being given).
Maybe I'm misunderstanding something about how threads work on Windows, but I wasn't aware that this was something that should happen. I was under the impression that you use a separate UI thread for this very reason: to keep it responsive while other threads do more time intensive operations.
Things I've tried: I've removed all communication between the two threads, so there are no mutexes or anything of that sort (unless there's something implicit that Windows does that I'm not aware of). I have also tried setting the UI thread to be a higher priority than the background thread. Neither of these helped.
Some things I've noted: While the UI thread lags for a moment, other applications running on my machine are just as responsive as ever. The heavy operation seems to only affect this one process. Also, if I decrease the amount of memory being allocated, it alleviates the issue (however, for the application to work as I want it to, it needs to be able to do this allocation).
The question: My question is two-fold. First, I'd like to understand why this is happening, as it seems to go against my understanding of how multi-threading should work. Second, do you have any recommendations or ideas on how to fix this and get it so the UI doesn't lag.
Abbreviated code: Note the comment about epochs in timeline.h
main.cpp
#include "Renderer/Headers/Renderer.h"
#include "Shared/Headers/Timeline.h"
#include "Simulator/Simulator.h"
#include <iostream>
#include <Windows.h>
unsigned int __stdcall renderThread(void* timelinePtr);
unsigned int __stdcall simulateThread(void* timelinePtr);
int main() {
Timeline timeline;
HANDLE renderHandle = (HANDLE)_beginthreadex(0, 0, &renderThread, &timeline, 0, 0);
if (renderHandle == 0) {
std::cerr << "There was an error creating the render thread" << std::endl;
return -1;
}
SetThreadPriority(renderHandle, THREAD_PRIORITY_HIGHEST);
HANDLE simulateHandle = (HANDLE)_beginthreadex(0, 0, &simulateThread, &timeline, 0, 0);
if (simulateHandle == 0) {
std::cerr << "There was an error creating the simulate thread" << std::endl;
return -1;
}
SetThreadPriority(simulateHandle, THREAD_PRIORITY_IDLE);
WaitForSingleObject(renderHandle, INFINITE);
WaitForSingleObject(simulateHandle, INFINITE);
return 0;
}
unsigned int __stdcall renderThread(void* timelinePtr) {
Timeline& timeline = *((Timeline*)timelinePtr);
Renderer renderer = Renderer(timeline);
renderer.run();
return 0;
}
unsigned int __stdcall simulateThread(void* timelinePtr) {
Timeline& timeline = *((Timeline*)timelinePtr);
Simulator simulator(timeline);
simulator.run();
return 0;
}
simulator.cpp
// abbreviated
void Simulator::run() {
while (true) {
// abbreviated
timeline->push(latestState);
}
}
// abbreviated
timeline.h
#ifndef TIMELINE_H
#define TIMELINE_H
#include "WorldState.h"
#include <mutex>
#include <vector>
class Timeline {
public:
Timeline();
bool tryGetStateAtFrame(int frame, WorldState*& worldState);
void push(WorldState* worldState);
private:
// The concept of an Epoch was introduced to help reduce mutex conflicts, but right now since the threads are disconnected, there should be no mutex locks at all on the UI thread. However, every 1024 pushes onto the timeline, a new Epoch must be created. The amount of slowdown largely depends on how much memory the WorldState class takes. If I make WorldState small, there isn't a noticable hiccup, but when it is large, it becomes noticeable.
class Epoch {
public:
static const int MAX_SIZE = 1024;
void push(WorldState* worldstate);
int getSize();
WorldState* getAt(int index);
private:
int size = 0;
WorldState states[MAX_SIZE];
};
Epoch* pushEpoch;
std::mutex lock;
std::vector<Epoch*> epochs;
};
#endif // !TIMELINE_H
timeline.cpp
#include "../Headers/Timeline.h"
#include <iostream>
Timeline::Timeline() {
pushEpoch = new Epoch();
}
bool Timeline::tryGetStateAtFrame(int frame, WorldState*& worldState) {
if (!lock.try_lock()) {
return false;
}
if (frame >= epochs.size() * Epoch::MAX_SIZE) {
lock.unlock();
return false;
}
worldState = epochs.at(frame / Epoch::MAX_SIZE)->getAt(frame % Epoch::MAX_SIZE);
lock.unlock();
return true;
}
void Timeline::push(WorldState* worldState) {
pushEpoch->push(worldState);
if (pushEpoch->getSize() == Epoch::MAX_SIZE) {
lock.lock();
epochs.push_back(pushEpoch);
lock.unlock();
pushEpoch = new Epoch();
}
}
void Timeline::Epoch::push(WorldState* worldState) {
if (this->size == this->MAX_SIZE) {
throw std::out_of_range("Pushed too many items to Epoch without clearing");
}
this->states[this->size] = *worldState;
this->size++;
}
int Timeline::Epoch::getSize() {
return this->size;
}
WorldState* Timeline::Epoch::getAt(int index) {
if (index >= this->size) {
throw std::out_of_range("Tried accessing nonexistent element of epoch");
}
return &(this->states[index]);
}
Renderer.cpp: loops to call Presenter::update() and some OpenGL rendering tasks.
Presenter.cpp
// abbreviated
void Presenter::update() {
camera->update();
// timeline->tryGetStateAtFrame(Time::getFrames(), worldState); // Normally this would cause a potential mutex conflict, but for now I have it commented out. This is the only place that anything on the UI thread accesses timeline.
}
// abbreviated
Any help/suggestions?
I ended up figuring this out!
So as it turns out, the new operator in C++ is threadsafe, which means that once it starts, it has to finish before any other threads can do anything. Why was that a problem in my case? Well, when an Epoch was being initialized, it had to initialize an array of 1024 WorldStates, each of which has 10,000 CellStates that need to be initialized, and each of those had an array of 16 items that needed to be initalized, so we ended up with over 100,000,000 objects needing to be initialized before the new operator could return. That was taking long enough that it caused the UI to hiccup while it was waiting.
The solution was to create a factory function that would build the pieces of the Epoch piecemeal, one constructor at a time and then combine them together and return a pointer to the new epoch.
timeline.h
#ifndef TIMELINE_H
#define TIMELINE_H
#include "WorldState.h"
#include <mutex>
#include <vector>
class Timeline {
public:
Timeline();
bool tryGetStateAtFrame(int frame, WorldState*& worldState);
void push(WorldState* worldState);
private:
class Epoch {
public:
static const int MAX_SIZE = 1024;
static Epoch* createNew();
void push(WorldState* worldstate);
int getSize();
WorldState* getAt(int index);
private:
Epoch();
int size = 0;
WorldState* states[MAX_SIZE];
};
Epoch* pushEpoch;
std::mutex lock;
std::vector<Epoch*> epochs;
};
#endif // !TIMELINE_H
timeline.cpp
Timeline::Epoch* Timeline::Epoch::createNew() {
Epoch* epoch = new Epoch();
for (unsigned int i = 0; i < MAX_SIZE; i++) {
epoch->states[i] = new WorldState();
}
return epoch;
}

How do you properly Await/Await Async in C++?

Sorry if the question is not clear. i'll try to explain it here.
I am working on a test project where two nodes will be communicating specialty packets to each other. As in: Node A will be sending a packet to Node B, and while Node B is generating it's packet, Node B Should also be processing the information from Node A. Node A should be preparing another packet for when Node B.
I've been reading into std::future but I'm not sure i understand how it works. The question i have is about the large section of commented out code. This is just psuedo code so i can try and get a better understanding about asynchronous functions in C++. I normally program in C#/Java where Asynchronous functions are fairly simple(ish). I hope the above kind of explains what i'm trying to do.
The code below might have some other errors. this is just a quick-and-dirty attempt to try and convey what i am asking.
My questions are:
What is the best way to check if Node B is ready, assuming that Node B is a separate instance of this theoretical app, running on a different device? if this makes any difference, this is being written with Linux C++.
Is this even the correct way of doing Tasks/Await/Await Async in C++? If this is incorrect, what is the proper way?
Thanks in advance.
//Excerpt from pseudo code. file would be kw_worker_delegate.h
#include <future>
#include <iostream>
#include <string>
#include <optional>
#include <system_error>
#include "kw_network.h"
class kw_worker
{
private:
/* ... */
std::future<kw_packet> *kw_packet_receive_delegate;
std::future<bool> *kw_packet_send_delegate;
bool worker_is_ready;
std::string kw_worker_adress;
network_interface *kw_network_interface;
/* Base CTOR that provides Delegates */
public:
kw_worker(std::future<kw_packet> *kwpktr, std::future<kw_packet> *kwpkts, network_interface *kwinetf, std::string kwaddr);
bool kw_worker_execute(bool isSendOrRecieve, kw_worker *active_worker, kw_worker *target_worker, std::optional<kw_packet> packet_data = std::nullopt);
};
kw_worker::kw_worker(std::future<kw_packet> *kwpktr, std::future<bool> *kwpkts, network_interface *kwinetf, std::string kwaddr)
{
&kw_packet_recieve_delegate = &kwpktr;
&kw_packet_send_delegate = &kwpkts;
&kw_network_interface = &kwintef;
kw_worker_address = kwaddr;
kw_worker_is_ready = true;
}
bool kw_Worker::kw_worker_execute(bool isSendOrRecieve, kw_worker *active_worker, kw_worker *target_worker, std::optional<kw_packet> packet_data = std::nullopt);
{
try
{
//if(isSendOrRecieve)
//{
// IS THIS CORRECT?
//
// if(!(&target_worker->worker_is_ready))
// {
// cout << "Worker B is not ready for a data submission...\n";
// do_something_or_await();
// }else{
// if(packet_data.has_value())
// kw_packet_send_delegate = std::async (&kw_network_interface->send_to, &target_worker->kw_worker_adress, packet_data);
// else throw -1;
// cout << "The data was sent to Worker B. Waiting for response on receipt of data...\n";
// &active_worker->worker_is_ready = true;
//
// //Do somewithng else...
// }
//}else{
// if(!(&active_worker->worker_is_ready))
// {
// cout << "Worker A is not ready to receive data...\n";
// do_something_or_await();
// }else{
// kw_packet_receive_delegate = std::async (&kw_network_interface->receive_from, &target_worker->kw_worker_adress);
// cout << "The data was received by Worker A. A Will now process the data...\n";
// &active_worker->kw_worker_is_ready = false;
//
// //Worker B will set it's worker_is_ready boolean value using it's send function
//
// //process the data now
// }
//}
}catch(const system_error &e)
{
cout << "There are no threads available to complete delegation. Please Try Again later";
return false;
}
catch(int)
{
cout << "Some other error occurred when getting data from worker" << &target_worker << ". Please Try Again later.";
return false;
}
return true;
}

What's the good way to pass data to a thread in c++?

I'm learning multi-thread coding using c++. What I need to do is continuously read word from keyboard, and pass it to a data thread for data processing. I used global variable word[] to pass the data. When word[0] != 0 means a new input from keyboard. And the data thread will set word[0] to 0 once it read the data. It works! But I'm not sure if it safe or not, or there are better ways to do this. Here is my code:
#include <iostream>
#include <thread>
#include <cstdio>
#include <cstring>
using namespace std;
static const int buff_len = 32;
static char* word = new char[buff_len];
static void data_thread () { // thread to handle data
while (1)
{
if (word[0]) { // have a new word
char* w = new char[buff_len];
strcpy(w, word);
cout << "Data processed!\n";
word[0] = 0; // Inform the producer that we consumed the word
}
}
};
static void read_keyboard () {
char * linebuf = new char[buff_len];
thread * worker = new thread( data_thread );
while (1) //enter "end" to terminate the loop
{
if (!std::fgets( linebuf, buff_len, stdin)) // EOF?
return;
linebuf[strcspn(linebuf, "\n")] = '\0'; //remove new line '\n' from the string
word = linebuf; // Pass the word to the worker thread
while (word[0]); // Wait for the worker thread to consume it
}
worker->join(); // Wait for the worker to terminate
}
int main ()
{
read_keyboard();
return 0;
}
The problem with this type of multi threading implementation is busy waiting. The input reader & the data consumer both are busy waiting and wasting the cpu cycles. To overcome this you need Semaphore.
Semaphore s_full(0);
Semaphore s_empty(1);
void data_processor ()
{
while (true) {
// Wait for data availability.
s_full.wait();
// Data is available to you, consume it.
process_data();
// Unblock the data producer.
s_empty.signal();
}
}
void input_reader()
{
while (true) {
// Wait for empty buffer.
s_empty.wait();
// Read data.
read_input_data();
// Unblock data com=nsumer.
s.full.signal();
}
}
In addition this solution will work only for a single data consumer thread. But for multiple data consumer threads you'll need thread safe buffer queue and proper implementation of producer - consumer problem.
See below blog links for additional information to solve this problem:
Thread safe buffer queue:
https://codeistry.wordpress.com/2018/03/08/buffer-queue-handling-in-multithreaded-environment/
Producer - consumer problem:
https://codeistry.wordpress.com/2018/03/09/unordered-producer-consumer/
There are a few problems with your approach:
This method is not scalable. What if you have more than 1 processing thread?
You would need a mutex to synchronise read-write access to the memory stored by word. At the scale of this example, not a big deal. In a "serious" application you might not have the luxury of waiting till you get the data thread stops processing. In that case, you might be tempted to remove the while(word[0]) but that is unsafe.
You fire off a "daemon" thread (not exactly but close enough) to handle your computations. Most of the time the thread is waiting for your input and cannot proceed without it. This is inefficient, and modern C++ gives you a way around it without explicitly handling raw threads using std::async paradigm.
#include <future>
#include <string>
#include <iostream>
static std::string worker(const std::string &input)
{
// assume this is a lengthy operation
return input.substr(1);
}
int main()
{
while (true)
{
std::string input;
std::getline (std::cin, input);
if (input.empty())
break;
std::future<std::string> fut= std::async(std::launch::async, &worker, input);
// Other tasks
// size_t n_stars = count_number_of_stars();
//
std::string result = fut.get(); // wait for the task to complete
printf("Output : %s\n", result.c_str());
}
return 0;
}
Something like this in my opinion is the better approach. std::async will launch a thread (if std::launch::async option is specified) and return a waitable future. The computation will continue in the background, and you can do other work in the main thread. When you need to get the result of your computation, you can get() the result of the future(btw the future can be void too).
Also there are a lot of C-isms in your C++ code. Unless there is a reason to do so, why would you not use std::string?
In modern CPP multithreading, u should be using condition_variable, mutex, and queue to handle this. the mutex prevents mutual reach to the queue and the condition variable makes the reader thread sleep until the writer writes what it write. the following is an example
static void data_thread (std::queue<char> & dataToProcess, std::mutex & mut, std::condition_variable & cv, std::atomic<bool>& finished) { // thread to handle data
std::string readData;
while (!finished)
{
{
std::unique_lock lock{mut};
cv.wait(lock, [&] { return !dataToProcess.empty() || finished; });
if (finished) {
while (!dataToProcess.empty()){
readData += dataToProcess.front();
dataToProcess.pop();
}
}
else{
readData += dataToProcess.front();
dataToProcess.pop();
}
}
std::cout << "\nData processed\n";
}
std::cout << readData;
};
static void read_keyboard () {
std::queue<char> data;
std::condition_variable cv;
std::mutex mut;
std::atomic<bool> finished = false;
std::thread worker = std::thread( data_thread, std::ref(data), std::ref(mut), std::ref(cv), std::ref(finished) );
char temp;
while (true) //enter "end" to terminate the loop
{
if (!std::cin.get(temp)) // EOF?
{
std::cin.clear();
finished = true;
cv.notify_all();
break;
}
{
std::lock_guard lock {mut};
data.push(temp);
}
cv.notify_all();
}
worker.join(); // Wait for the worker to terminate
}
int main ()
{
read_keyboard();
return 0;
}
What you are looking for is a message queue. This needs mutex and condition variable.
Here is one on github (not mine but it popped up when I searched) https://github.com/khuttun/PolyM
and another
https://www.justsoftwaresolutions.co.uk/threading/implementing-a-thread-safe-queue-using-condition-variables.html
I will get told off for posting links, but I am not going to type the entire code here and github's not going anywhere soon

Inconsistent output from c++ multithreaded program

I have the following program in C++:
// multithreading01.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include <string>
#include <iostream>
#include <process.h>
using namespace std;
bool threadFinished = false;
struct params {
string aFile;
bool tf;
};
void WriteToFile(void *p)
{
params* a = (params*)p;
cout<<a->aFile<<endl;
a->tf = true;
_endthread();
}
int main(int argc, char* argv[])
{
params *param01 = new params;
params *param02 = new params;
param01->aFile = "hello from p1";
param01->tf = false;
param02->aFile = "hello from p2";
param02->tf = false;
_beginthread(WriteToFile,0,(void *) param01);
_beginthread(WriteToFile,0,(void *) param02);
while(!param01->tf || !param02->tf)
{
}
cout << "Main ends" << endl;
system("pause");
return 0;
}
However, I am getting inconsistent outputs such as
output 1:
hello from p1
hello from p2
output 2:
hello from p1hello from p2
output 3:
hhello from p2ello from p1
How can I get a consistent output from this code? I am using Visual C++ 6.0 Standard Edition.
Read this small writeup
Like everyone mentioned in the comment, when you create threads, generally speaking, idea is to separate tasks and thusly increasing performance on modern multicore architecture CPUs which could one thread per core.
If you want to access same resource (same file in your case) from two different threads then you need to make sure that simultaneous access from two threads doesnt happen otherwise you would see the problem that you are seeing.
Your provide safe simultaneous access by protecting shared resource using some locks (e.g POSIX locks or you could chose your platform specific lock implementation).
Common mistake beginners do is that they lock the "code" not "resource".
Dont do this:
void WriteToFile(void *p)
{
pthread_mutex_lock(var); //for example only
params* a = (params*)p;
cout<<a->aFile<<endl;
a->tf = true;
_endthread();
pthread_mutex_unlock(var); //for example only
}
You should instead put a lock in your resource
struct params {
lock_t lock; //for example only not actual code
string aFile;
bool tf;
};
void WriteToFile(void *p)
{
params* a = (params*)p;
pthread_mutex_lock(a->lock); //Locking params here not the whole code.
cout<<a->aFile<<endl;
a->tf = true;
pthread_mutex_unlock(a->lock); //Unlocking params
_endthread();
}

memory leak with sockets and map

I have a socket server, everytime a new connection is made, a XClient class is instantiated and I am inserting it into a map. I am watching the memory usage through task manager. everytime a new connection is made, lets assume, the memory usage of my program increases by 800kb for example. Inside that class, there is a connected variable, which will tell me wheter this client is active or not. I created a thread to run endlessly and iterate through all the elements of my map and I'm checking if the connected variable is true or false. if it is false, I am (at least I think I am...) releasing the memory used by the previously instantiated XClient class. BUT, the memory usage is being decreased only half of the 800kb (for example, no precise values). So, when a client connects: +800kb. when client disconnects: -400kb. I think I have a memory leak? If I have 100 clients connected, that 400kb that is not being released would turn into 4000kb of non-used(?) memory, and that would be a problem.
So, here is my code.
The thread to iterate through all elements:
DWORD Update(XSockets *sockets)
{
while(true)
{
for(sockets->it = sockets->clients.begin(); sockets->it != sockets->clients.end(); sockets->it++)
{
int key = (*sockets->it).first;
if(sockets->clients[key]->connected == false) // remove the client, releasing memory
{
delete sockets->clients[key];
}
}
Sleep(100);
}
return true;
}
The code that is adding new XClients instances to my map:
bool XSockets::AcceptConnections()
{
struct sockaddr_in from;
while(true)
{
try
{
int fromLen = sizeof(from);
SOCKET client = accept(this->loginSocket,(struct sockaddr*)&from,&fromLen);
if(client != INVALID_SOCKET)
{
srand(time(NULL));
int clientKey = rand();
XClient* clientClass = new XClient(inet_ntoa(from.sin_addr),clientKey,client);
this->clients.insert(make_pair(clientKey,clientClass));
}
Sleep(100);
}
catch(...)
{
printf("error accepting incoming connection!\r\n");
break;
}
}
closesocket(this->loginSocket);
WSACleanup();
return true;
}
And the declarations:
map<int,XClient*> clients;
map<int,XClient*>::iterator it;
You've got several problems, but the chief one is that you appear to be sharing a map between threads without any synchronization at all. That can lead to all kinds of trouble.
Are you using c++11 or Boost? To avoid memory leak nightmares like this, you could create a map of shared pointers. This way, you can let the structure clean itself up.
This is how I would do it:
#include <memory>
#include <map>
#include <algorithm>
#include <functional>
#include <mutex>
typedef std::shared_ptr<XClient> XClientPtr;
std::map<int, XClientPtr> client;
std::mutex the_lock;
bool XSockets::AcceptConnections()
{
/* snip */
auto clientClass = std::make_shared<XClient>(/*... params ...*/);
the_lock.lock();
clients[clientKey] = clientClass;
the_lock.unlock();
/* snip */
}
bool client_is_connected(const std::pair<int, XClientPtr> &p) {
return p.second->connected;
}
DWORD Update(XSockets *sockets) {
while(true) { /* You should probably have some kind of
exit condition here. Like a global "running" bool
so that the thread will eventually stop. */
the_lock.lock();
auto it = sockets->clients.begin(), end = sockets->clients.end();
for(; it != end; ) {
if (!it->second->connected)
//Clients will be destructed here if their refcount goes to 0
sockets->clients.erase(it++);
else
++it;
}
the_lock.unlock();
Sleep(100);
}
return 1;
}
Note: Above code is untested. I haven't even tried to compile it.
See What happens to an STL iterator after erasing it in VS, UNIX/Linux?. In your case, you are not deleting everything, so you will want to not use a for loop.
sockets->it = sockets->clients.begin();
while (sockets->it != sockets->clients.end())
{
int key = (*sockets->it).first;
if(sockets->clients[key]->connected == false) // remove the client, releasing memory
{
delete sockets->clients[key];
sockets->clients.erase(sockets->it++);
}
else
{
sockets->it++;
}
}