cwzx/nngpp

"Object Closed" and no clue

Closed this issue · 3 comments

Hi

Continuing with #10

I had similar issue, as well, using SWIG to interface some socket management functions like the following:

static nng::socket_view getIpcIndexSocket()
{
	std::string sLane = "ipc:///aBridgeIndex";
	nng::socket_view _socket(nng::survey::open());
	// nng::set_opt_recv_timeout(_socket, 1000);

	_socket.dial(sLane.c_str());

	std::cout << "ipc index socket open" << std::endl;

	return _socket;
}

And the Swig interface file (.i) declares:

nng::socket_view getIpcIndexSocket();

It compiles great now, but when importing the generated (python in this case) module, running that method throws an nng::exception:

terminate called after throwing an instance of 'nng::exception'
  what():  Object closed

right when the _socket.dial(...) method is called.

Any clue about what's wrong with the code? There is nothing else going on in the Swig side, this is just a proof of concept, only method called.

Thanks

Originally posted by @webpolis in #10 (comment)

Fixed. It was all about memory management and pointer stuff

			nng::socket&& _socket =  nng::survey::open();
			_socket.dial(sLane.c_str());
			_ipcIndexSocket = std::move(_socket);
cwzx commented

nng::survey::open() returns an nng::socket object, which owns the socket. This means when the socket object is destroyed, the underlying socket is closed.

In your first example the socket object was destroyed at the end of the same line on which it was created.

The normal way to write this function is:

static nng::socket getIpcIndexSocket()
{
	nng::socket sock = nng::survey::open();
	sock.dial("ipc:///aBridgeIndex");
	return sock;
}

which transfers the ownership out of the function to the caller.

However, SWIG does not properly support move semantics (see here), so this will not compile.

As a result you will not be able to use the RAII classes such as nng::socket across the boundary.
The workaround is to release the ownership and use the non-owning views instead:

static nng::socket_view getIpcIndexSocket()
{
	nng::socket sock = nng::survey::open();
	sock.dial("ipc:///aBridgeIndex");
	return sock.release();
}

The view classes have all of the same functionality, but no ownership, so the socket will persist until it is explicitly closed.

To close a socket manually, you can put it back into an nng::socket and allow it to be destroyed:

static void closeSocket(nng::socket_view sv)
{
	nng::socket sock(sv.get());
}

Amazing explanation, thank you. It provides some enlightenment considering I haven't used C++ for years. I had found an alternate way to solve this a short while after I posted that. My solution looks like:

nng::socket&& _socket = nng::respond::open();
_socket.dial(addr);
_indexSocket = std::move(_socket)

That _indexSocket is a private member of the exported class via Swig which preserves its state until I explicitly call the ~socket() destructor.

Do you see anything out of place? It works pretty well, even under stressful tests.

Greetings.
Nicolas