protected override bool ReleaseHandle() { int error = 0; Socket.Blocking_icall(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_icall(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_icall(handle, out error); return(error == 0); }