From eafa657f3b40b115b4cacdb93f459b5fade3225a Mon Sep 17 00:00:00 2001 From: Timur Doumler Date: Tue, 8 Sep 2015 12:08:06 +0100 Subject: [PATCH] DatagramSocket: Added shutdown method to cleanly exit from Datagram Read/Write loop without destroying the object (useful to resolve race conditions). --- modules/juce_core/network/juce_Socket.cpp | 41 ++++++++++++++++++++--- modules/juce_core/network/juce_Socket.h | 14 ++++++++ 2 files changed, 50 insertions(+), 5 deletions(-) diff --git a/modules/juce_core/network/juce_Socket.cpp b/modules/juce_core/network/juce_Socket.cpp index 675369481e..97ebb33a7e 100644 --- a/modules/juce_core/network/juce_Socket.cpp +++ b/modules/juce_core/network/juce_Socket.cpp @@ -569,8 +569,11 @@ DatagramSocket::DatagramSocket (const bool canBroadcast) SocketHelpers::initSockets(); handle = (int) socket (AF_INET, SOCK_DGRAM, 0); - SocketHelpers::resetSocketOptions (handle, true, canBroadcast); - SocketHelpers::makeReusable (handle); + if (handle >= 0) + { + SocketHelpers::resetSocketOptions (handle, true, canBroadcast); + SocketHelpers::makeReusable (handle); + } } DatagramSocket::~DatagramSocket() @@ -578,8 +581,18 @@ DatagramSocket::~DatagramSocket() if (lastServerAddress != nullptr) freeaddrinfo (static_cast (lastServerAddress)); + shutdown(); +} + +void DatagramSocket::shutdown() +{ + if (handle < 0) + return; + + int copyOfHandle = handle; + handle = -1; bool connected = false; - SocketHelpers::closeSocket (handle, readLock, false, 0, connected); + SocketHelpers::closeSocket (copyOfHandle, readLock, false, 0, connected); } bool DatagramSocket::bindToPort (const int port) @@ -589,6 +602,9 @@ bool DatagramSocket::bindToPort (const int port) bool DatagramSocket::bindToPort (const int port, const String& addr) { + if (handle < 0) + return false; + if (SocketHelpers::bindSocket (handle, port, addr)) { isBound = true; @@ -602,6 +618,9 @@ bool DatagramSocket::bindToPort (const int port, const String& addr) int DatagramSocket::getBoundPort() const noexcept { + if (handle < 0) + return -1; + return isBound ? SocketHelpers::getBoundPort (handle) : -1; } @@ -609,11 +628,17 @@ int DatagramSocket::getBoundPort() const noexcept int DatagramSocket::waitUntilReady (const bool readyForReading, const int timeoutMsecs) const { + if (handle < 0) + return -1; + return SocketHelpers::waitForReadiness (handle, readLock, readyForReading, timeoutMsecs); } int DatagramSocket::read (void* destBuffer, int maxBytesToRead, bool shouldBlock) { + if (handle < 0) + return -1; + bool connected = true; return isBound ? SocketHelpers::readSocket (handle, destBuffer, maxBytesToRead, connected, shouldBlock, readLock) : -1; @@ -621,6 +646,9 @@ int DatagramSocket::read (void* destBuffer, int maxBytesToRead, bool shouldBlock int DatagramSocket::read (void* destBuffer, int maxBytesToRead, bool shouldBlock, String& senderIPAddress, int& senderPort) { + if (handle < 0) + return -1; + bool connected = true; return isBound ? SocketHelpers::readSocket (handle, destBuffer, maxBytesToRead, connected, shouldBlock, readLock, &senderIPAddress, &senderPort) : -1; @@ -629,6 +657,9 @@ int DatagramSocket::read (void* destBuffer, int maxBytesToRead, bool shouldBlock int DatagramSocket::write (const String& remoteHostname, int remotePortNumber, const void* sourceBuffer, int numBytesToWrite) { + if (handle < 0) + return -1; + struct addrinfo*& info = reinterpret_cast (lastServerAddress); // getaddrinfo can be quite slow so cache the result of the address lookup @@ -651,7 +682,7 @@ int DatagramSocket::write (const String& remoteHostname, int remotePortNumber, bool DatagramSocket::joinMulticast (const String& multicastIPAddress) { - if (! isBound) + if (! isBound || handle < 0) return false; return SocketHelpers::multicast (handle, multicastIPAddress, lastBindAddress, true); @@ -659,7 +690,7 @@ bool DatagramSocket::joinMulticast (const String& multicastIPAddress) bool DatagramSocket::leaveMulticast (const String& multicastIPAddress) { - if (! isBound) + if (! isBound || handle < 0) return false; return SocketHelpers::multicast (handle, multicastIPAddress, lastBindAddress, false); diff --git a/modules/juce_core/network/juce_Socket.h b/modules/juce_core/network/juce_Socket.h index 56e9b8da9f..37fe0559f6 100644 --- a/modules/juce_core/network/juce_Socket.h +++ b/modules/juce_core/network/juce_Socket.h @@ -306,6 +306,20 @@ public: int write (const String& remoteHostname, int remotePortNumber, const void* sourceBuffer, int numBytesToWrite); + /** Closes the underlying socket object. + + Closes the underlying socket object and aborts any read or write operations. + Note that all other methods will return an error after this call. This + method is useful if another thread is blocking in a read/write call and you + woould like to abort the read/write thread. Simply deleting the socket + object without calling shutdown may cause a race-condition where the read/write + returns just before the socket is deleted and the subsequent read/write would + try to read from an invalid pointer. By calling shutdown first, the socket + object remains valid but all current and subsequent calls to read/write will + return immediately. + */ + void shutdown(); + //============================================================================== /** Join a multicast group