protected override bool ReleaseHandle()
{
bool ret = false;
#if DEBUG
try
{
#endif
GlobalLog.Print("SafeCloseSocket::ReleaseHandle(handle:" + handle.ToString("x") + ")");
SocketError errorCode;
// If m_Blockable was set in BlockingRelease, it's safe to block here, which means
// we can honor the linger options set on the socket. It also means closesocket() might return WSAEWOULDBLOCK, in which
// case we need to do some recovery.
if (m_Blockable)
{
GlobalLog.Print("SafeCloseSocket::ReleaseHandle(handle:" + handle.ToString("x") + ") Following 'blockable' branch.");
errorCode = UnsafeNclNativeMethods.SafeNetHandles.closesocket(handle);
#if DEBUG
m_CloseSocketHandle = handle;
m_CloseSocketResult = errorCode;
#endif
if (errorCode == SocketError.SocketError) errorCode = (SocketError) Marshal.GetLastWin32Error();
GlobalLog.Print("SafeCloseSocket::ReleaseHandle(handle:" + handle.ToString("x") + ") closesocket()#1:" + errorCode.ToString());
// If it's not WSAEWOULDBLOCK, there's no more recourse - we either succeeded or failed.
if (errorCode != SocketError.WouldBlock)
{
return ret = errorCode == SocketError.Success;
}
// The socket must be non-blocking with a linger timeout set.
// We have to set the socket to blocking.
int nonBlockCmd = 0;
errorCode = UnsafeNclNativeMethods.SafeNetHandles.ioctlsocket(
handle,
IoctlSocketConstants.FIONBIO,
ref nonBlockCmd);
if (errorCode == SocketError.SocketError) errorCode = (SocketError) Marshal.GetLastWin32Error();
GlobalLog.Print("SafeCloseSocket::ReleaseHandle(handle:" + handle.ToString("x") + ") ioctlsocket()#1:" + errorCode.ToString());
// This can fail if there's a pending WSAEventSelect. Try canceling it.
if (errorCode == SocketError.InvalidArgument)
{
errorCode = UnsafeNclNativeMethods.SafeNetHandles.WSAEventSelect(
handle,
IntPtr.Zero,
AsyncEventBits.FdNone);
GlobalLog.Print("SafeCloseSocket::ReleaseHandle(handle:" + handle.ToString("x") + ") WSAEventSelect():" + (errorCode == SocketError.SocketError ? (SocketError)Marshal.GetLastWin32Error() : errorCode).ToString());
// Now retry the ioctl.
errorCode = UnsafeNclNativeMethods.SafeNetHandles.ioctlsocket(
handle,
IoctlSocketConstants.FIONBIO,
ref nonBlockCmd);
GlobalLog.Print("SafeCloseSocket::ReleaseHandle(handle:" + handle.ToString("x") + ") ioctlsocket#2():" + (errorCode == SocketError.SocketError ? (SocketError)Marshal.GetLastWin32Error() : errorCode).ToString());
}
// If that succeeded, try again.
if (errorCode == SocketError.Success)
{
errorCode = UnsafeNclNativeMethods.SafeNetHandles.closesocket(handle);
#if DEBUG
m_CloseSocketHandle = handle;
m_CloseSocketResult = errorCode;
#endif
if (errorCode == SocketError.SocketError) errorCode = (SocketError) Marshal.GetLastWin32Error();
GlobalLog.Print("SafeCloseSocket::ReleaseHandle(handle:" + handle.ToString("x") + ") closesocket#2():" + errorCode.ToString());
// If it's not WSAEWOULDBLOCK, there's no more recourse - we either succeeded or failed.
if (errorCode != SocketError.WouldBlock)
{
return ret = errorCode == SocketError.Success;
}
}
// It failed. Fall through to the regular abortive close.
}
// By default or if CloseAsIs() path failed, set linger timeout to zero to get an abortive close (RST).
Linger lingerStruct;
lingerStruct.OnOff = 1;
lingerStruct.Time = (short) 0;
errorCode = UnsafeNclNativeMethods.SafeNetHandles.setsockopt(
handle,
SocketOptionLevel.Socket,
SocketOptionName.Linger,
ref lingerStruct,
Linger.Size);
#if DEBUG
m_CloseSocketLinger = errorCode;
#endif
if (errorCode == SocketError.SocketError) errorCode = (SocketError) Marshal.GetLastWin32Error();
GlobalLog.Print("SafeCloseSocket::ReleaseHandle(handle:" + handle.ToString("x") + ") setsockopt():" + errorCode.ToString());
if (errorCode != SocketError.Success && errorCode != SocketError.InvalidArgument && errorCode != SocketError.ProtocolOption)
{
// Too dangerous to try closesocket() - it might block!
return ret = false;
}
errorCode = UnsafeNclNativeMethods.SafeNetHandles.closesocket(handle);
#if DEBUG
m_CloseSocketHandle = handle;
m_CloseSocketResult = errorCode;
#endif
GlobalLog.Print("SafeCloseSocket::ReleaseHandle(handle:" + handle.ToString("x") + ") closesocket#3():" + (errorCode == SocketError.SocketError ? (SocketError)Marshal.GetLastWin32Error() : errorCode).ToString());
return ret = errorCode == SocketError.Success;
#if DEBUG
}
catch (Exception exception)
{
if (!NclUtilities.IsFatal(exception)){
GlobalLog.Assert("SafeCloseSocket::ReleaseHandle(handle:" + handle.ToString("x") + ")", exception.Message);
}
ret = true; // Avoid a second assert.
throw;
}
finally
{
m_CloseSocketThread = Thread.CurrentThread.ManagedThreadId;
m_CloseSocketTick = Environment.TickCount;
GlobalLog.Assert(ret, "SafeCloseSocket::ReleaseHandle(handle:{0:x})|ReleaseHandle failed.", handle);
}
#endif
}