Reputation: 126
i'm writing server with will handle client connection, but i have problem moving class with thread inside to vector, i know if i had only thread i can move it to vector with std::move()
, but here i have thread inside a class and i'm getting a lot of errors because thread is non-movable object.
Core.cpp:
#define OS_LINUX
#include <iostream>
#include <vector>
#include <string>
#include <thread>
#include <csignal>
#include <cstdlib>
#include <TCPServer/TCPServer.h>
#include <TCPServer/TCPServerConnection.h>
#include <ProcessManip/ProcessManip.h>
#include <Log/Log.h>
#include "ConnectionHandler.h"
//Global
bool TERMNINATE = false;//If true loops must end
const int PORT = 8822;
//Proto
void terminate(int sig);
void receive_message(ConnectionHandler *handler,string message);
using namespace std;
int main(int argc,char *argv[])
{
//Add Terminate handler
signal(SIGINT,terminate);
//Load configuration
Log::logDebug("Starting ...");
//Init
vector<ConnectionHandler> connections;
//Init modules
//Main
Log::logDebug("Running");
TCPServer server;
if(!server._bind(PORT))
return 1;
if(!server._listen())
return 2;
ProcessManip::cleanProcess(); /* Clean dead processes */
server.setBlocking(false);/* accept returns invalid socket if no connection */
Log::logDebug("Listening ...");
while(!TERMNINATE)
{
TCPServerConnection conn = server._accept();
if(!conn.isValid())
continue;
Log::logDebug((string)"Got connection from: "+conn.getAddress());/* Print IP address of client */
ConnectionHandler ch(&TERMNINATE,std::move(conn));
ch.setCallback(receive_message);
ch.start();
connections.push_back(std::move(ch)); //here is problem
/*connections.push_back(ConnectionHandler(&TERMNINATE,conn)); /* Add it to vector */
/*connections.back().setCallback(receive_message);
connections.back().start();*/
Log::logDebug("Connection added to vector");
}
server.setBlocking(true);
//Dispose
Log::logDebug("Stopping ...");
/*for(auto it = connections.begin();it!=connections.end();)
{
Log::logDebug((string)"Closed connection with: "+(*it).getConnection().getAddress());/* Print IP address of client */
//(*it).close(); /* Close connetion */
// it = connections.erase(it); /* Delete ConnectionHandler from vector */
// }
server._close();
Log::logDebug("Closed");
return 0;
}
void terminate(int sig)
{
//Change global value to true
TERMNINATE = true;
}
void receive_message(ConnectionHandler *handler,string message)
{
Log::logDebug((string)"Message ("+handler->getConnection().getAddress()+") : "+message);
}
ConnectionHandler.h
#ifndef EXT_CONNECTIONHANDLER
#define EXT_CONNECTIONHANDLER
#include <TCPServer/TCPServerConnection.h>
#include <thread>
#include <string>
#include <iostream>
#include <functional>
using namespace std;
class ConnectionHandler
{
public:
ConnectionHandler(bool *pMainTerminate,TCPServerConnection pConnection);
ConnectionHandler(TCPServerConnection pConnection);
~ConnectionHandler();
void start(); /* Start listening */
void stop(); /* Stop listening */
void close(); /* Stops listening + close connection */
void setConnection(TCPServerConnection pConnection);
TCPServerConnection getConnection();
void setCallback(function<void(ConnectionHandler*,string)> pFunction);
private:
bool *mainTerminate = NULL;
bool handler_terminate = false;
short status = 0;
TCPServerConnection connection;
bool needTerminate();
void run();
void waitForEnd();
function<void(ConnectionHandler*,string)> callback = NULL;
std::thread m_thread;
};
#endif
ConnectionHandler.cpp
#include "ConnectionHandler.h"
ConnectionHandler::ConnectionHandler(bool *pMainTerminate,TCPServerConnection pConnection)
{
this->mainTerminate = pMainTerminate;
this->connection = pConnection;
}
ConnectionHandler::ConnectionHandler(TCPServerConnection pConnection)
{
this->mainTerminate = NULL;
this->connection = pConnection;
}
ConnectionHandler::~ConnectionHandler()
{
this->close();
}
void ConnectionHandler::start()
{
m_thread = std::thread(&ConnectionHandler::run, this);
this->status = 1;
}
void ConnectionHandler::waitForEnd()
{
if(this->m_thread.joinable())
this->m_thread.join();
}
bool ConnectionHandler::needTerminate()
{
if(mainTerminate!=NULL)
return this->handler_terminate||*(this->mainTerminate);
else
return this->handler_terminate;
}
void ConnectionHandler::run()
{
string message = "";
string tmp = "";
this->connection.setBlocking(false); // So we can terminate any time
while(!this->needTerminate())
{
message = this->connection._receive();
if(message!="")
{
do
{
tmp = this->connection._receive();
message+=tmp;
}while(tmp!=""); /* If we get longer message than we can grab at one time */
this->connection._send(message); /* TODO Remove */
if(this->callback!=NULL)
this->callback(this,message);
message = "";
}
}
this->connection.setBlocking(true);
}
void ConnectionHandler::stop()
{
this->handler_terminate = true; /* Signals thread to stop */
this->waitForEnd();
this->status = 2;
}
void ConnectionHandler::close()
{
this->stop();
this->connection._close(); /* Close connection */
this->status = 3;
}
TCPServerConnection ConnectionHandler::getConnection()
{
return this->connection;
}
void ConnectionHandler::setConnection(TCPServerConnection pConnection)
{
this->connection = pConnection;
}
void ConnectionHandler::setCallback(function<void(ConnectionHandler*,string)> pFunction)
{
this->callback = pFunction;
}
Upvotes: 0
Views: 883
Reputation: 118302
Because this class violates the Rule Of Three, even if the std::thread
issue gets addressed, other problems will likely appear; most likely taking the form of mysterious runtime bugs.
The compilation issue with std::thread
is not the problem, it's merely a symptom of the real problem: this class should not be moved or copied. This class should only be new
-constructed, then stuffed into a std::shared_ptr
(or a reasonable facsimile) and stay there until it gets destroyed. Only the std::shared_ptr
should be passed around, stuffed into a vector, etc...
Upvotes: 3