I am sending an HTTP request to save/update data on the server. The request is made asynchronously and a callback function is called when it completes. Everything works fine except that sometimes, the application crashes in the callback.
This is what I am doing:
user = new User();
user->saveOnServer();
user->zombie = true; // Mark the user that it needs to be deleted in the callback.
In User, I have a saveOnServer() method:
void User::saveOnServer(){
Request *request = new Request();
// Send request to the server and register the callback.
request ->setCallback(&userCallback, (void*)this);
}
The callback:
void userCallback(void *data){
User *user = (User*)data;
// Do something here.
// Delete user if it's a zombie.
if(user->zombie)
delete user;
}
At times, I need to create a new user after sending a request to the server:
user = new User();
user->saveOnServer();
user->zombie = true;
// Some code comes here.
if(user)
delete user;
user = new User();
The problem is that in such cases, the application crashes when deleting the user in the callback as it has already been deleted. Another issue is that the callback deletes user but user pointer in main is still pointing to some address (dangling pointer) and so I again try to delete it.
I am not sure what is the best way of managing memory in this case. I have zombie in place because there are cases when I do not want the callback to delete the user.
Once you've called saveOnServer of a zombie user, the request is the effective "owner" of that user object. Don't free it yourself since there's something else that still intends to use it and delete it later.
In fact, if the server action can return asynchronously, then the user object might get destroyed at any time. You should cease using it entirely from the other code. You've granted control of that object to the request, and you must stop using it from anywhere else:
user = new User();
user->zombie = true; // set *before* transferring ownership to server
user->saveOnServer();
user = NULL;
//some code comes here
user = new User();
If you don't want the request to use that object anymore, then you need to provide some facility for "canceling" the save-on-server action so that it doesn't use the object.
Another option is to use smart pointers. In your main code, store the object in a shared_ptr. In the request object, store it in a weak_ptr. That way, if your main code wants to destroy the user object, it can simply call user.reset(). Then if the callback attempts to use the weak_ptr, it will discover that the pointed-to object is no longer available. When using smart pointers, neither function should use delete. The pointer objects will manage the lifetime of the user for you.
shared_ptr<User> user = make_shared<User>()
user->saveOnServer();
//some code comes here
user.reset(new User());
In the saveOnServer function, use shared_from_this to create a weak_ptr to the object:
void User::saveOnServer(){
Request *request = new Request();
//send request on server and register the callback
weak_ptr<User> self(shared_from_this());
request ->setCallback(&userCallback, self);
}
In the callback, use that weak_ptr:
void userCallback(weak_ptr<User> data){
shared_ptr<User) user = data.lock();
if (!user)
return;
//do something here
}
Related
Can someone explain me how to pass a custom class object to another function in wxWidgets? I have a wxDialog class called AddUser which contains a void type OnButtonClick function that creates an object of a custom class "User". How can I pass that object to another OnButtonClick function that is located in Main class?
One important thing to know (in case you don't already) about wxDialog is that it is quite okay to create them on the stack (most wxWidgets windows should be created on the heap).
This means that your dialog instance remains available even after it has been closed by the user pressing "Ok". You can test for user responses by the following code:
... existing method ...
AddUser dialog (this);
if (dialog.ShowModal() == wxID_OK)
{
... process new user ...
}
Because the dialog is still instantiated, you can include a method in your dialog code that returns the new user as follows:
User AddUser::GetUser ()
{
return newUser;
}
However, you should of course be careful where the new user is actually created. For example, if the new user object is created locally in the dialog, then you will need to make a copy of it (as the above example will do). If it is created on the heap (which I wouldn't advise) then you can return a pointer. A third alternative would be to pass a reference to the GetUser method so the dialog method looks like this:
bool AddUser::GetUser (User& user)
{
// Make sure that all fields are valid. Simple example given, but
// should be more complete.
if (TextName->GetValue() != "" && TextSurname->GetValue() != "")
{
user.setName(TextName->GetValue());
user.setSurname(TextSurname->GetValue());
return true;
}
return false;
return newUser;
}
And the call looks like this:
void wxBiblioFrame::OnButAddUserClick(wxCommandEvent& event)
{
AddUser dialog(this);
myUserDialog dialog (this);
myUserClass newUser;
if (dialog.ShowModal() == wxID_OK)
{
if (dialog.GetUser (newUser))
{
... process and store the new user ...
}
else
{
... handle the error ...
}
}
// NOTE: no need to Destroy() the dialog.
}
By the way, unless your user class is huge, I wouldn't be too concerned making copies of the object from an efficiency point of view. Creating and closing the dialog is likely to dwarf any time taken in making a copy.
You can't call an OnClick event and pass something different than the parameters in the event signature. If you need somthing like tis then maybe you should consider reiterating your application's architecture.
I have a question regarding the new connect() syntax of qt.
My connect looks like this:
connect(m_ui->addEntryButton, &QPushButton::clicked, [&](){HandleSignalEvents(Events::AddEntryButton);});
with m_ui->addEntryButton as my triggering pushbutton and the lambda calling a method, which would then go and redirect the signal depending on its enum parameter to the actual handling method.
However, the problem I have, is that my HandleSignalEvents method looks like this:
void UIController::HandleSignalEvents(Events event)
{
switch (event)
{
case Events::AddEntryButton:
m_eventHandler.HandleAddEntryClick(m_ui->addEntryDateEdit, m_ui->addEntryTextEdit, m_ui->mainEntryList);
break;
}
}
I think the content is pretty much not too relevant in detail, I just want to make sure that you guys understand, that I want to pass pointers to my UI controls to the function, so I can perform all my actions there, including resetting text fields etc.
However, I can't quite figure out how to deal with the scope issue, considering the function is called from a lambda. I tried to capture this, and then passing the whole ui pointer to the HandleSignalEvent function, but all I ever end with is a memory access violation assertion.
However, I would really like to do it the lambda way, as I otherwise would have to setup SignalMappers or some other fuzzy stuff.
Is there any advice on how to properly pass the correct handles?
I should note that my ui pointer is a boost::shared_ptr.
EDIT:
Here is some additional code, as requested:
My controller:
void UIController::InitBinds()
{
connect(m_ui->addEntryButton, &QPushButton::clicked, [&](){HandleSignalEvents(Events::AddEntryButton); });
}
void UIController::HandleSignalEvents(Events event)
{
switch (event)
{
case Events::AddEntryButton:
m_eventHandler.HandleAddEntryClick(m_ui->addEntryDateEdit, m_ui->addEntryTextEdit, m_ui->mainEntryList);
break;
}
}
The EventHandler-class:
void EventHandler::HandleAddEntryClick(QDateTimeEdit *dateTimeEdit, QTextEdit *textEdit, QListWidget *mainList)
{
QDate dateTime = dateTimeEdit->date();
int day = dateTime.dayOfWeek();
}
The error I get is following (My visual studio is set to german language, but I try to translate it so it doesn't lose its meaning):
Exception at 0x... (Qt5Core.dll) in ... .exe:
0xC0000005: Access violated while reading at position 0x8B0C43BD
This happens at the moment I try to access dateTimeEdit->date();
I tried to capture this, and then passing the whole ui pointer to the
HandleSignalEvent function, but all I ever end with is a memory access
violation assertion.
I'd try to pass the m_ui pointer by value to the event handler:
connect(m_ui->addEntryButton, &QPushButton::clicked, [=](){HandleSignalEvents(Events::AddEntryButton, m_ui);});
You're invoking a method on an object, so you must insure that the target object exists when the signal is emitted. You do this by providing the target object as the context to the connection.
This will work, or be a no-op if the target object has vanished:
connect(m_ui->addEntryButton, &QPushButton::clicked,
this, [this]{ HandleSignalEvents(Events::AddEntryButton); });
^^^^
Absolutely critical
Furthermore, in the UIController::HandleSignalEvents, there's a likelihood that some m_ui members are null or are dangling pointers. Ensure that's not the case!
Note that [&]{ ... } captures this by value, as it should - capturing it by reference is nonsense as this would be out of scope when the method exits; see this question for details.
I try to use QTcpSserver, which would keep connection with one and only one client at a time, until the client disconnects. So, I keep the client with a member pointer in my class.
The problem arises here: In the examples I see on the internet, after disconnected(), it is called deleteLater(). Good, but I would use this class-member pointer again for another connection. Remember that the server keeps one and only one client at a time. So, what if the socket object is deleted after another connection assigned on it?
What I mean is:
class TcpServer(QObject* o) : public QTcpServer {
...
private:
QTcpSocket* client;
}
void TcpServer::connected() {
client = this->nextPendingConnection();
this->pauseAccepting();
connect(client, SIGNAL(disconnected()), client, SLOT(clientDisconnected()));
}
void TcpServer::clientDisconnected() {
client->deleteLater();
this->resumeAccepting();
}
Scenario is this:
Client connected. So, client = nextPendingConnection();
Server paused listening. Does not accept new connection.
Client is disconnected. client needs to be released. So, client->deleteLater() is calleed.
Server continues listening.
New connection comes. So, I need to client = nextPendingConnection();
But, previous client object was deleted? Maybe? Maybe not? What if event loop tries to delete client, after I have assigned the new connection to it in step 5?
So, how would I keep one and only one client, while deleting previous disconnected ones?
Would it be safe if I do this?
void TcpServer::clientDisconnected()
{
QSocket* ptr = client;
ptr->deleteLater();
...
}
I will cite Qt documentation about it:
The object will be deleted when control returns to the event loop.
So deleteLater() is a delayed delete. The object is to be regarded as deleted as soon as the call deleteLater() was made.
Your nextPendingConnection() call will create another object that need to be deleted some time later.
However in your case you only allow one pending connection as you said and disallow accepting until client gets disconnected. I this case it should be safe, in other cases you could overwrite your client pointer and will lose control over it (memory leak).
Even in your case, I would prefer this solution:
void TcpServer::clientDisconnected()
{
if (qobject_cast<QAbstractSocket*>(sender())) {
sender()->deleteLater();
}
...
}
This would also be safe if more than one connection is allowed in future changes of your application.
As i understand nextPendingConnection(); will return pointer to new QTcpSocket class object so you have nothing to worry about.
deleteLater() will scheduled for deletion only your old object. QTcpSocket* client contains only pointer to QTcpSocket class object. When you calling deleteLater() Qt will delete only object to which client was pointed at time of calling this function.
So i just programmed a simple multithreaded client server application using winsock2 and TCP.
Here is a quick summary of how it works:
The servers main-thread is in a endless loop accepting clients and then also adding them to the servers vector which holds every connected client like this:
(only adding in the important stuff for my question)
std::vector <Client*> clients;
while (true){
clients.push_back(&Client(accept(serverSocket, NULL, NULL), this));
}
When a new client connects to the server we basically create a new Client object with the socket of the new client and the server itself as parameters.
My idea was then to give every client its own thread so every client can send data at the same time.
std::thread tickThread;
Client::Client(SOCKET socket,Server* server) :
isConnected(true),
socket(socket),
server(server)
{
tickThread = std::thread(&Client::tick,this);
}
The thread for the client then checks if the client sent something and then sends it to the server. It also checks wether the client is still connected.
void Client::tick(){
while (isConnected){
errorHandler = recv(socket, receivedData, 255, 0);
if (errorHandler == SOCKET_ERROR){
disconnect();
}
else {
//send received data to server
}
}
If the client disconnected it tells the server to remove the client from the connected clients vector and then sets the "isConnected" bool to false so the thread can exit its function.
void Client::disconnect(){
isConnected = false;
server->removeClient(this);
}
This is how it's supposed to work, however as soon as a client disconnects again the server crashes with the error:
R6010 - abort() has been called
All debugging shows me is this as my error:
switch (_CrtDbgReportW(_CRT_ERROR, NULL, 0, NULL, L"%s", error_text)){
case 1: _CrtDbgBreak(); msgshown = 1; break;
case 0: msgshown = 1; break;
}
So yeah i don't really know whats causing this crash, however i suspect that it might be related to the thread using a function of the client that's basically being deleted as it is being removed from the client vector of the server.
And if this turns out to be the problem could you guys give me ideas for a better way of implementing every client having its own thread?
Edit: changed the vector error, however the crash still happens as soon as a client disconnects
The error is in this block of code:
while (true){
clients.push_back(&Client(accept(serverSocket, NULL, NULL), this));
}
Client(accept(serverSocket, NULL, NULL), this) is an expression which generates a temporary Client object that is destroyed when the statement finishes executing. However, you take the address of that temporary object and add it to your vector.
If you want to create Client objects and store pointers to them, you will need to allocate memory for them. I would recommend using std::unique_ptr to manage them so that your vector claims ownership of their memory and automatically frees them if they are removed from the vector or the vector itself is destroyed. Then your code becomes:
std::vector<std::unique_ptr<Client>> clients;
while (true){
clients.push_back(std::make_unique<Client>(accept(serverSocket, NULL, NULL), this));
}
In this piece of code:
clients.push_back(&Client(accept(serverSocket, NULL, NULL), this));
You are pushing an address of a temporary object into the container. When push_back() is done, the temporary object is destroyed, so that address is no longer valid. I wonder, what kind of compiler allows you to do this.
i have a tcp server, which requires to allow exactly one client to connect to it at any time. anytime a new client connects, older session must be deleted and new session created.
right now, i am doing it like this:
void TcpServer::start_accept() {
Logger::info("[TCPSERVER] TCP Server starting to accept", __LINE__, __FILE__);
if (session) { // check if there is any older session, if so..delete them
session = NULL;
delete session;
}
session = new TcpServerSession(io_service_);
acceptor_.async_accept(session->socket(), boost::bind(&TcpServer::handle_accept, this, session, boost::asio::placeholders::error));
}
so any time i would like to send a msg to the client, it is being done like this:
int TcpServer::sendMsgToClient(std::string msg) {
if (session)
session->sendMsgToClient(msg);
}
i am wondering if this is being done correctly? basically the main point is deleting a pointer and re-creating it.whats the best way to do this?
Just use a std::unique_ptr<> :
session.reset(new TcpServerSession(io_service_));
It gets everything right: don't delete old object before a new one is available, never have session point to something invalid, and even in the presence of exceptions no memory is leaked.
if (session) { // check if there is any older session, if so..delete them
session = NULL;
delete session;
}
This is totally wrong! You blank out session, leaking whatever is currently there, and then delete NULL, which does absolutely nothing.
To be exception safe, you should not delete the old session until you have successfully created the new one. Something like this:
if (session) {
// Create and initialise the new session first
TcpServerSession* newSession = new TcpServerSession(io_service_);
// Don't know what this line does, but I assume it's important
acceptor_.async_accept(newSession->socket(), boost::bind(&TcpServer::handle_accept, this, newSession, boost::asio::placeholders::error));
std::swap(session, newSession); // Put the new one in place
delete newSession; // delete the old one.
}
Actually, this assumes async_accept doesn't throw. If it can, you will need to be careful to delete the newSession, probably with some kind of smart pointer.
session = NULL;
delete session;
Is most certainly not correct. If you replace the value that session holds (which points to a block of memory allocated by new) before calling delete on it, you effectively lose that block of memory, causing a memory leak. The only reason why this code doesn't blow up is because calling delete with a NULL is guaranteed to be a no-op.
Thus, you should replace the code with the following :
delete session;
session = NULL; // or nullptr if you've got C++11
Which will guarantee that the memory is properly freed.
Get rid of session = NULL before delete session. You're trying to delete the null pointer.
You don't need to set it to null because you're immediately going to set it to the new TCP session.
if (session) { // check if there is any older session, if so..delete them
session = NULL;
delete session;
}
This code says:
If session points to some valid object (instead of being null), then stop pointing to it (instead, point to NULL), and then delete what session now points to, i.e. delete nothing.
This is very bad. It is a genuine memory leak.
The comment is a lie.