| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136 |
- //
- // System.Net.Sockets.SafeSocketHandle
- //
- // Authors:
- // Marcos Henrich <[email protected]>
- //
- using System;
- using System.IO;
- using System.Threading;
- using System.Collections.Generic;
- using Microsoft.Win32.SafeHandles;
- namespace System.Net.Sockets {
- 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)
- {
- SetHandle (preexistingHandle);
- }
- // This is just for marshalling
- internal SafeSocketHandle () : base (true)
- {
- }
- bool closii;
- /*protected override void Dispose (bool disposing)
- {
- lock (this) {
- if (!closii) {
- closii = true;
- int error = 0;
- Socket.Blocking_internal (handle, false, out error);
- //AbortRegisteredThreads ();
- Socket.Close_internal (handle, out error);
- //Console.Error.WriteLine ("Closed "+ handle);
- }
- }
- base.Dispose (disposing);
- }*/
- protected override bool ReleaseHandle ()
- {
- int error = 0;
- Socket.Blocking_internal (handle, false, out error);
- if (blocking_threads != null) {
- int abort_attempts = 0;
- while (blocking_threads.Count > 0) {
- if (abort_attempts++ >= ABORT_RETRIES) {
- 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;
- }
- /*
- * 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 ();
- // Sleep so other threads can resume
- Thread.Sleep (1);
- }
- }
- Socket.Close_internal (handle, out error);
- return error == 0;
- }
- public void RegisterForBlockingSyscall ()
- {
- if (blocking_threads == null)
- Interlocked.CompareExchange (ref blocking_threads, new List<Thread> (), null);
-
- bool release = false;
- try {
- DangerousAddRef (ref release);
- } finally {
- /* We must use a finally block here to make this atomic. */
- lock (blocking_threads) {
- blocking_threads.Add (Thread.CurrentThread);
- }
- if (release)
- DangerousRelease ();
- // Handle can be closed by DangerousRelease
- if (IsClosed)
- throw new SocketException (SOCKET_CLOSED);
- }
- }
- /* This must be called from a finally block! */
- public void UnRegisterForBlockingSyscall ()
- {
- //If this NRE, we're in deep problems because Register Must have
- lock (blocking_threads) {
- blocking_threads.Remove (Thread.CurrentThread);
- }
- }
- void AbortRegisteredThreads () {
- if (blocking_threads == null)
- return;
- lock (blocking_threads) {
- foreach (var t in blocking_threads)
- Socket.cancel_blocking_socket_operation (t);
- }
- }
- }
- }
|