protected override bool ReleaseHandle ()
{
int error = 0;
Socket.Blocking_internal (handle, false, out error);
#if FULL_AOT_DESKTOP
/* 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) {
lock (blocking_threads) {
int abort_attempts = 0;
while (blocking_threads.Count > 0) {
if (abort_attempts++ >= ABORT_RETRIES) {
if (THROW_ON_ABORT_RETRIES) {
StringBuilder sb = new StringBuilder ();
sb.AppendLine ("Could not abort registered blocking threads before closing socket.");
foreach (var thread in blocking_threads) {
sb.AppendLine ("Thread StackTrace:");
sb.AppendLine (threads_stacktraces[thread].ToString ());
}
sb.AppendLine ();
throw new Exception (sb.ToString ());
}
// 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;
}
/*
* 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.
*/
if (blocking_threads.Count == 1 && blocking_threads[0] == Thread.CurrentThread)
break;
// abort registered threads
foreach (var t in blocking_threads)
Socket.cancel_blocking_socket_operation (t);
// Sleep so other threads can resume
in_cleanup = true;
Monitor.Wait (blocking_threads, 100);
}
}
}
Socket.Close_internal (handle, out error);
return error == 0;
}