sealed class SafeSocketHandle : SafeHandleZeroOrMinusOneIsInvalid {
List<Thread> blocking_threads;
+
+ const int SOCKET_CLOSED = 10004;
+
const int ABORT_RETRIES = 10;
+ static bool THROW_ON_ABORT_RETRIES = Environment.GetEnvironmentVariable("MONO_TESTS_IN_PROGRESS") == "yes";
public SafeSocketHandle (IntPtr preexistingHandle, bool ownsHandle) : base (ownsHandle)
{
protected override bool ReleaseHandle ()
{
int error = 0;
-
+
Socket.Blocking_internal (handle, false, out error);
-
+#if MOBILE_STATIC
+ /* It's only for platforms that do not have working syscall abort mechanism, like WatchOS and TvOS */
+ Socket.Shutdown_internal (handle, SocketShutdown.Both, out error);
+#endif
+
if (blocking_threads != null) {
int abort_attempts = 0;
while (blocking_threads.Count > 0) {
if (abort_attempts++ >= ABORT_RETRIES) {
-#if DEBUG
- throw new Exception ("Could not abort registered blocking threads before closing socket.");
-#else
+ if (THROW_ON_ABORT_RETRIES)
+ throw new Exception ("Could not abort registered blocking threads before closing socket.");
+
// Attempts to close the socket safely failed.
// We give up, and close the socket with pending blocking system calls.
// This should not occur, nonetheless if it does this avoids an endless loop.
break;
-#endif
+ }
+
+ /*
+ * This method can be called by the DangerousRelease inside RegisterForBlockingSyscall
+ * When this happens blocking_threads contains the current thread.
+ * We can safely close the socket and throw SocketException in RegisterForBlockingSyscall
+ * before the blocking system call.
+ */
+ lock (blocking_threads) {
+ if (blocking_threads.Count == 1 && blocking_threads[0] == Thread.CurrentThread)
+ break;
}
AbortRegisteredThreads ();
}
if (release)
DangerousRelease ();
+
+ // Handle can be closed by DangerousRelease
+ if (IsClosed)
+ throw new SocketException (SOCKET_CLOSED);
}
}