eminfedar/async-sockets-cpp

"accept()" prevents TCPServer to close by blocking.

Closed this issue · 5 comments

Even after the program died, the detached thread of TCPServer for listening the incoming sockets is still working at behind until it crashes at some point.

I can't send a signal to stop it because the thread is blocked by the accept function.

That is a huge problem.

You have 2 options here:

  1. Send a signal to the blocked thread. This makes accept return with the errno set to EINTR.
  2. Use the select function along with non-blocking sockets (non-blocking to prevent a race condition if the connection is dropped between select returning and the call to accept).

See http://man7.org/linux/man-pages/man2/accept.2.html for more information on using the accept function, and http://man7.org/linux/man-pages/man2/select.2.html for the select function.

If I get bored I may implement this and create a PR.

select consumes the cpu heavily. on linux using poll,epoll is a solution, but if we want to make it cross-platform it is a challenge (which libuv managed), but with that way we don't need threads, we can handle all the socket operations with a single thread.

Actually if there is a way to make accept() return with error, that will fit our thread-based approach and it will be easy.

If we can terminate i/o blocking functions from another thread this system will work flawlessly.

How do you manage to send a signal to a blocked thread? I didn't manage it :) It would be a nice help if you create a PR 👍

I can't try now but maybe shutdown()ing the socket in the TCPServer::Close() function can break the block of the accept()?

https://linux.die.net/man/3/shutdown

I couldn't really find anything in the docs of shutdown or accept that defines this behaviour, however their are a few SO answers that references the behaviour here: https://stackoverflow.com/questions/9365282/c-linux-accept-blocking-after-socket-closed

It should be an easy change and fairly quick to test so sure give that a try.

As for Windows, IO is actually quite easy (regarding files, pipes and ports anyway, I'm not sure if sockets are any different) as the blocking functions will return when CloseHandle() releases the resource.

Actually in the link you shared the second answer mentioning the shutdown which I've told:

Use: sock.shutdown (socket.SHUT_RD)

Then accept will return EINVAL. No ugly cross thread signals required!

This is what I was searching for. I will implement it very soon, then it will be stable :)