Müllmusik
Müllmusik

Reputation: 101

Proper way to close a blocking UDP socket

I have a C++ object that creates a thread to read from a blocking UDP socket:

mRunning.store(true);
while (mRunning.load(boost::memory_order_consume)) {
    ...
    int size = recvfrom(mSocket, buf, kTextBufSize , 0,
                        (struct sockaddr *) &packet->mReplyAddr.mSockAddr, (socklen_t*)&packet->mReplyAddr.mSockAddrLen);

    if (size > 0) {
        //do stuff
    }
}
return 0;

(mRunning is a boost::atomic) The object's destructor is called from another thread and does this:

mRunning.store(false);  
#ifdef WIN32
    if (mSocket != -1) closesocket(mSocket);
#else
    if (mSocket != -1) close(mSocket);
#endif
pthread_join(mThread, NULL);

This seems to work, but one of my colleagues suggested that there might be a problem if recv is interrupted in the middle of reading something. Is this thread safe? What's the correct way of closing a blocking UDP socket? (Needs to be cross-platform OSX/Linux/Windows)

Upvotes: 10

Views: 11102

Answers (3)

blaze
blaze

Reputation: 4364

There could be a lot of different problems. Moving my application from one FreeBSD version to another I found that such close() worked normally on older kernel and just hung close() until something returned from recv() on newer. And OSX is FreeBSD based :)

Portable way of closing sockets from different thread is to create pipe and block not in recv(), but in select(). When you need to close socket, write something to pipe, select() will unblock and you can safely do close().

Upvotes: 5

Andrew Seddon
Andrew Seddon

Reputation: 56

Your colleague is right, boost sockets are not thread safe.

Your options;

  1. Use ASIO (do this one)
  2. Timeout on blocking call. This isn't really portable though it might work.

Upvotes: 0

cnicutar
cnicutar

Reputation: 182684

Well recvfrom in itself is thread-safe. IIRC all socket functions are. The question is:

  • What's going to happen if you pull the descriptor from under recvfrom while it's copying data to your buffer ?

It's a good question but I doubt the standard says anything about this (I also doubt the specific manual of an implementation says anything about it). So any implementation is free to:

  • Complete the operation (perhaps because it doesn't need the descriptor anymore, or because it's doing some cool reference counting or something else)
  • Make the recvfrom fail and return a -1 (ENOTSOCK, EINVAL ?)
  • Crash spectacularly because buffers and internal data structures are freed by the close.

Obviously this is just speculation (I have been wrong before, many times), but unless you find something in the standard to support the idea that you can close the socket while receiving through it, you're not safe.

So, what can you do ? THe safest: use a synchronization mechanism to ensure that you only close the socket after the recvfrom is done (semaphores, mutexes, whatever).

Personally I would do an UP on a semaphore after the recvfrom and a DOWN before the close.

Upvotes: 1

Related Questions