SafeSocketHandle.cs 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136
  1. //
  2. // System.Net.Sockets.SafeSocketHandle
  3. //
  4. // Authors:
  5. // Marcos Henrich <[email protected]>
  6. //
  7. using System;
  8. using System.IO;
  9. using System.Threading;
  10. using System.Collections.Generic;
  11. using Microsoft.Win32.SafeHandles;
  12. namespace System.Net.Sockets {
  13. sealed class SafeSocketHandle : SafeHandleZeroOrMinusOneIsInvalid {
  14. List<Thread> blocking_threads;
  15. const int SOCKET_CLOSED = 10004;
  16. const int ABORT_RETRIES = 10;
  17. static bool THROW_ON_ABORT_RETRIES = Environment.GetEnvironmentVariable("MONO_TESTS_IN_PROGRESS") == "yes";
  18. public SafeSocketHandle (IntPtr preexistingHandle, bool ownsHandle) : base (ownsHandle)
  19. {
  20. SetHandle (preexistingHandle);
  21. }
  22. // This is just for marshalling
  23. internal SafeSocketHandle () : base (true)
  24. {
  25. }
  26. bool closii;
  27. /*protected override void Dispose (bool disposing)
  28. {
  29. lock (this) {
  30. if (!closii) {
  31. closii = true;
  32. int error = 0;
  33. Socket.Blocking_internal (handle, false, out error);
  34. //AbortRegisteredThreads ();
  35. Socket.Close_internal (handle, out error);
  36. //Console.Error.WriteLine ("Closed "+ handle);
  37. }
  38. }
  39. base.Dispose (disposing);
  40. }*/
  41. protected override bool ReleaseHandle ()
  42. {
  43. int error = 0;
  44. Socket.Blocking_internal (handle, false, out error);
  45. if (blocking_threads != null) {
  46. int abort_attempts = 0;
  47. while (blocking_threads.Count > 0) {
  48. if (abort_attempts++ >= ABORT_RETRIES) {
  49. if (THROW_ON_ABORT_RETRIES)
  50. throw new Exception ("Could not abort registered blocking threads before closing socket.");
  51. // Attempts to close the socket safely failed.
  52. // We give up, and close the socket with pending blocking system calls.
  53. // This should not occur, nonetheless if it does this avoids an endless loop.
  54. break;
  55. }
  56. /*
  57. * This method can be called by the DangerousRelease inside RegisterForBlockingSyscall
  58. * When this happens blocking_threads contains the current thread.
  59. * We can safely close the socket and throw SocketException in RegisterForBlockingSyscall
  60. * before the blocking system call.
  61. */
  62. lock (blocking_threads) {
  63. if (blocking_threads.Count == 1 && blocking_threads[0] == Thread.CurrentThread)
  64. break;
  65. }
  66. AbortRegisteredThreads ();
  67. // Sleep so other threads can resume
  68. Thread.Sleep (1);
  69. }
  70. }
  71. Socket.Close_internal (handle, out error);
  72. return error == 0;
  73. }
  74. public void RegisterForBlockingSyscall ()
  75. {
  76. if (blocking_threads == null)
  77. Interlocked.CompareExchange (ref blocking_threads, new List<Thread> (), null);
  78. bool release = false;
  79. try {
  80. DangerousAddRef (ref release);
  81. } finally {
  82. /* We must use a finally block here to make this atomic. */
  83. lock (blocking_threads) {
  84. blocking_threads.Add (Thread.CurrentThread);
  85. }
  86. if (release)
  87. DangerousRelease ();
  88. // Handle can be closed by DangerousRelease
  89. if (IsClosed)
  90. throw new SocketException (SOCKET_CLOSED);
  91. }
  92. }
  93. /* This must be called from a finally block! */
  94. public void UnRegisterForBlockingSyscall ()
  95. {
  96. //If this NRE, we're in deep problems because Register Must have
  97. lock (blocking_threads) {
  98. blocking_threads.Remove (Thread.CurrentThread);
  99. }
  100. }
  101. void AbortRegisteredThreads () {
  102. if (blocking_threads == null)
  103. return;
  104. lock (blocking_threads) {
  105. foreach (var t in blocking_threads)
  106. Socket.cancel_blocking_socket_operation (t);
  107. }
  108. }
  109. }
  110. }