0

EDIT: I never figured this out - I refactored the code to be pretty much identical to a Boost sample, and still had the problem. If anyone else has this problem, yours may be the more common shared_from_this() being called when no shared_ptr exists (or in the constructor). Otherwise, I recommend just rebuilding from the boost asio samples.

I'm trying to do something that I think is pretty common, but I am having some issues.

I'm using boost asio, and trying to create a TCP server. I accept connections with async_accept, and I create shared pointers. I have a long lived object (like a connection manager), that inserts the shared_ptr into a set. Here is a snippet:

std::shared_ptr<WebsocketClient> ptr = std::make_shared<WebsocketClient>(std::move(s));
directory.addPending(ptr);
ptr->onConnect(std::bind(&Directory::addClient, &directory, std::placeholders::_1));
ptr->onDisconnect(std::bind(&Directory::removeClient, &directory, std::placeholders::_1));
ptr->onMessage(std::bind(&Directory::onMessage, &directory, std::placeholders::_1, std::placeholders::_2));
ptr->start();

The Directory has std::set<std::shared_ptr<WebsocketClient>> pendingClients;

The function for adding a client is:

void Directory::addPending(std::shared_ptr<WebsocketClient> ptr){
    std::cout << "Added pending client: " << ptr->getName() << std::endl;
    pendingClients.insert(ptr);
}

Now, when the WebsocketClient starts, it tries to create a shared_ptr using shared_from_this() and then initiates an async_read_until ("\r\n\r\n"), and passes that shared_ptr to the lambda to keep ownership. It crashes before actually invoking the asio function, on shared_from_this().

Call stack looks like this:

server.exe!WebsocketClient::start()
server.exe!Server::acceptConnection::__l2::<lambda>(boost::system::error_code ec)
server.exe!boost::asio::asio_handler_invoke<boost::asio::detail::binder1<void <lambda>(boost::system::error_code),boost::system::error_code> >(boost::asio::detail::binder1<void <lambda>(boost::system::error_code),boost::system::error_code> & function, ...)
server.exe!boost::asio::detail::win_iocp_socket_accept_op<boost::asio::basic_socket<boost::asio::ip::tcp,boost::asio::stream_socket_service<boost::asio::ip::tcp> >,boost::asio::ip::tcp,void <lambda>(boost::system::error_code) ::do_complete(boost::asio::detail::win_iocp_io_service * owner, boost::asio::detail::win_iocp_operation * base, const boost::system::error_code & result_ec, unsigned __int64 __formal) Line 142  C++
server.exe!boost::asio::detail::win_iocp_io_service::do_one(bool ec, boost::system::error_code &)
server.exe!boost::asio::detail::win_iocp_io_service::run(boost::system::error_code & ec)
server.exe!Server::run()
server.exe!main(int argc, char * * argv)

However, I get a bad_weak_ptr when I call shared_from_this. I thought that was thrown when no shared_ptr owned this object, but when I call the addPending, I insert "ptr" into a set, so there should still be a reference to it.

Any ideas? If you need more details please ask, and I'll provide them. This is my first post on StackOverflow, so let me know what I can improve.

mach990
  • 33
  • 7
  • Could [this](http://stackoverflow.com/questions/27697973/shared-from-this-causing-bad-weak-ptr) help you? – Dafang Cao Jun 17 '16 at 23:14
  • 1
    I don't think you can use shared_from_this() in the constructor, which you imply you're doing ("when the WebsocketClient starts..."). It only becomes valid after construction. Lots of related Q&A around, e.g. [here](http://stackoverflow.com/questions/4598986/weak-pointer-to-this-in-constructor). – Tony Delroy Jun 17 '16 at 23:15
  • @DafangCao I actually already saw that. But in my case, I should already have a reference to it, because I insert it into a set (directory.addPending(ptr)) – mach990 Jun 17 '16 at 23:17
  • @TonyD I don't actually call it in the constructor. The constructor doesn't really do anything other than initialize some values related to the connection. It's in the start call where I try to do shared_from_this, but at that point there should already be a reference stored (in two places, actually?) – mach990 Jun 17 '16 at 23:18
  • Nope. Not shared pointers. It's impossible to have them if the object is still being constructed... – sehe Jun 17 '16 at 23:19
  • @sehe What? I don't called shared_from_this while the object is being constructed. It's already been fully constructed by the time ptr->start is called. Why did you mark this as a duplicate? – mach990 Jun 17 '16 at 23:22
  • Oh. Hmm. I misread that then. Reopening. Please make your sample a SSCCE/MCVE – sehe Jun 17 '16 at 23:23
  • 2
    I can think of a lot of things you might have done wrong, but I can't tell which without enough code to replicate the problem. – David Schwartz Jun 17 '16 at 23:26
  • @DavidSchwartz I can try to add more code. Could you suggest what might possibly have gone wrong to throw a bad_weak_ptr, if it isn't that I have no references to that object? – mach990 Jun 17 '16 at 23:28
  • 2
    The devil's likely in the details - e.g. "passes that `shared_ptr` to the lambda to keep ownership" - you're not accidentally passing by reference? Is directory simply a `set>`? You may need to create a [MCVE](http://stackoverflow.com/help/mcve) to get useful help (actually, it's site policy too).... – Tony Delroy Jun 17 '16 at 23:28
  • 1
    It is that you have no shared_ptr references to that object, but how you got there can be subtle. You could have violated the thread safety requirements for shared pointers. You could think you have a shared_ptr to the object but actually have a shared_ptr to some other object. You might have a reference whose life exceeded the life of the thing it references. You might have mixed std::shared_ptr with boost::shared_ptr. – David Schwartz Jun 17 '16 at 23:30
  • Try this: Use a debugger and stop when you get the error. Check the call stack for the invocation of the member function's caller. Make sure that caller called that member function through a shared_ptr. If not, check its caller, and confirm the same. The first member function of the object on which you got the error should have been invoked through a shared_ptr. 100% confirm that it was. – David Schwartz Jun 17 '16 at 23:33
  • @TonyD The bad_weak_ptr gets thrown before I actually even give it to the lambda. It gets thrown inside ptr->start() function. The directory is a class that has a std::set> pendingClients; I'll go ahead and edit the question to reflect this. – mach990 Jun 17 '16 at 23:34
  • @DavidSchwartz By the way, thanks for the help and suggestions. I really appreciate it. I did confirm with the debugger that the websocketclient start() function is being called by `ptr->start();` – mach990 Jun 17 '16 at 23:47
  • @mach990 And `ptr` is a valid `shared_ptr` to the very same object that is calling `shared_from_this`? – David Schwartz Jun 17 '16 at 23:49
  • @DavidSchwartz Yes. It's the one that was created with make_shared in the sample above, so the code goes directly into that function, where the very first line is `auto self(shared_from_this());`. So it's going from creating the shared_ptr to setting up some callbacks, and then invoking the start function, which crashes immediately. So somehow there's something wrong with the shared_ptr, but I don't know what. – mach990 Jun 17 '16 at 23:53
  • What happens if you call `ptr->shared_from_this()` immediately after creating it with `make_shared()`? i.e. remove all the other code that adds it to the set and sets callbacks, just test the simplest possible thing that could be going wrong. Does it work then? If that works, try moving the `shared_from_this()` call slightly later. Keep moving it until you find the pointer where it stops working. Call it after adding it to the set. Call it before calling `ptr->start()`. Call it at the very top of the `WebSocketClient::start()` function. Find the first point at which it throws `bad_weak_ptr`. – Jonathan Wakely Aug 01 '16 at 10:37
  • And get rid of all the Asio code, it almost certainly isn't relevant or related to your problem. Simplify the `WebsocketClient` class, and move the `shared_ptr` creation code into `main` (instead of having it called inside an Asio call stack). Basically, produce a [mcve](http://stackoverflow.com/help/mcve) as requested several times above. Something like http://coliru.stacked-crooked.com/a/ed3527242f911889 works fine, so figure out which part of your code causes it to behave differently. This is basic debugging: isolate what the problem is, then you can fix it. – Jonathan Wakely Aug 01 '16 at 10:47

2 Answers2

1

You could be dealing with memory corruption. Whether that's the case or not, there are some troubleshooting steps you should definitely take:

  1. Log the pointer value returned from make_shared, and again inside the member function just before calling shared_from_this. Check whether that pointer value exists in your running object table (which is effectively what that set<shared_ptr<...>> is)
  2. Instrument constructor and destructor. If the shared_ptr count does actually hit zero, it'll call your destructor and the call stack will give you information on the problem.

If that doesn't help, the fact that you're using make_shared should be useful, because it guarantees that the metadata block is right next to the object.

  1. Use memcpy to dump the raw bytes preceding your object at various times and watch for potential corruption.

Much of this logging will happen in a context that's exhibiting undefined behavior. If the compiler figures out that you're testing for something that's not supposed to be possible, it might actually remove the test. In that case, you can usually manage to make the tests work anyway by precision use of #pragma to disable optimization just on your debug logging code -- you don't want to change optimization settings on the rest of the code, because that might change the way corruption manifests without actually fixing it.

Ben Voigt
  • 277,958
  • 43
  • 419
  • 720
1

It is difficult to determine the cause of the problem without a code. But which enable_shared_from_this you use, boost or std? I see you use std::make_shared, so if WebsocketClient inherits boost::enable_shared_from_this it can cause crash.

dpleshakov
  • 106
  • 7