SafeSocketHandle.cs 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119
  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. protected override bool ReleaseHandle ()
  27. {
  28. int error = 0;
  29. Socket.Blocking_internal (handle, false, out error);
  30. Socket.Shutdown_internal (handle, SocketShutdown.Both, out error);
  31. if (blocking_threads != null) {
  32. int abort_attempts = 0;
  33. while (blocking_threads.Count > 0) {
  34. if (abort_attempts++ >= ABORT_RETRIES) {
  35. if (THROW_ON_ABORT_RETRIES)
  36. throw new Exception ("Could not abort registered blocking threads before closing socket.");
  37. // Attempts to close the socket safely failed.
  38. // We give up, and close the socket with pending blocking system calls.
  39. // This should not occur, nonetheless if it does this avoids an endless loop.
  40. break;
  41. }
  42. /*
  43. * This method can be called by the DangerousRelease inside RegisterForBlockingSyscall
  44. * When this happens blocking_threads contains the current thread.
  45. * We can safely close the socket and throw SocketException in RegisterForBlockingSyscall
  46. * before the blocking system call.
  47. */
  48. lock (blocking_threads) {
  49. if (blocking_threads.Count == 1 && blocking_threads[0] == Thread.CurrentThread)
  50. break;
  51. }
  52. AbortRegisteredThreads ();
  53. // Sleep so other threads can resume
  54. Thread.Sleep (1);
  55. }
  56. }
  57. Socket.Close_internal (handle, out error);
  58. return error == 0;
  59. }
  60. public void RegisterForBlockingSyscall ()
  61. {
  62. if (blocking_threads == null)
  63. Interlocked.CompareExchange (ref blocking_threads, new List<Thread> (), null);
  64. bool release = false;
  65. try {
  66. DangerousAddRef (ref release);
  67. } finally {
  68. /* We must use a finally block here to make this atomic. */
  69. lock (blocking_threads) {
  70. blocking_threads.Add (Thread.CurrentThread);
  71. }
  72. if (release)
  73. DangerousRelease ();
  74. // Handle can be closed by DangerousRelease
  75. if (IsClosed)
  76. throw new SocketException (SOCKET_CLOSED);
  77. }
  78. }
  79. /* This must be called from a finally block! */
  80. public void UnRegisterForBlockingSyscall ()
  81. {
  82. //If this NRE, we're in deep problems because Register Must have
  83. lock (blocking_threads) {
  84. blocking_threads.Remove (Thread.CurrentThread);
  85. }
  86. }
  87. void AbortRegisteredThreads () {
  88. if (blocking_threads == null)
  89. return;
  90. lock (blocking_threads) {
  91. foreach (var t in blocking_threads)
  92. Socket.cancel_blocking_socket_operation (t);
  93. }
  94. }
  95. }
  96. }