AnonymousPipeServerStream.Read() иногда зависает при выходе клиента

У меня есть ведущая и подчиненная программа, которые взаимодействуют через пару анонимных каналов.

Взаимодействие выглядит так:

  • Мастер создает два файла AnonymousPipeServerStream
  • Мастер запускает клиентский процесс, предоставляя ему свой .GetClientHandleAsString()
  • Мастер .DisposeLocalCopyOfClientHandle обе трубы
  • Мастер пишет материал в один канал и читает материал из другого.
  • Иногда мастер принудительно завершает работу ведомого из другого потока (Process.Kill()), И ТАКЖЕ закрывает оба объекта pipe.

В очень редких случаях, после завершения ведомого устройства, чтение из трубы на блоках главной стороны неопределенно - или иногда определенно (например, в течение нескольких минут). Я не могу воспроизвести его на своей локальной машине, но время от времени это происходит на большом кластере.

Я захватил свалку этой ситуации, и вот что я увидел:

Stacktrace заблокированного мастера (я на 100% уверен, что в этот момент клиентский процесс уже завершен):

000000000c83e488 000000007700fdba [NDirectMethodFrameStandalone: 000000000c83e488] Microsoft.Win32.UnsafeNativeMethods.ReadFile(Microsoft.Win32.SafeHandles.SafePipeHandle, Byte*, Int32, Int32 ByRef, IntPtr)
000000000c83e430 000007feeab32820 DomainBoundILStubClass.IL_STUB_PInvoke(Microsoft.Win32.SafeHandles.SafePipeHandle, Byte*, Int32, Int32 ByRef, IntPtr)*** WARNING: Unable to verify checksum for System.Core.ni.dll

000000000c83e540 000007feeac14574 System.IO.Pipes.PipeStream.ReadFileNative(Microsoft.Win32.SafeHandles.SafePipeHandle, Byte[], Int32, Int32, System.Threading.NativeOverlapped*, Int32 ByRef)
000000000c83e5a0 000007feeac14a23 System.IO.Pipes.PipeStream.ReadCore(Byte[], Int32, Int32)
000000000c83e610 000007fef0169d8f System.IO.BinaryReader.FillBuffer(Int32)
000000000c83e650 000007fef0169c8a System.IO.BinaryReader.ReadInt32()

Я также просмотрел объект AnonymousPipeServerStream, который блокирует, а также его состояние и дескриптор.

Он имеет:

  • m_state = 4 (т.е. закрыто)
  • m_clientHandle является закрытым дескриптором (согласно SafeHandle._state и выходному дескриптору!)
  • m_handle - открытый дескриптор с _state = 6 (т.е. НЕ закрыт, хотя объект Pipe закрыт и, согласно декомпиляции, он должен был вызвать m_handle.Dispose()):

Здесь выводится дескриптор! для рукоятки главной стороны.

0:000> !handle 1850 ff
Handle 0000000000001850
  Type          File
  Attributes    0
  GrantedAccess 0x120189:
         ReadControl,Synch
         Read/List,ReadEA,ReadAttr,WriteAttr
  HandleCount   2
  PointerCount  5
  No object specific information available

Я немного обеспокоен тем, что HandleCount равен 2, а значение PointerCount равно 5.

Любые идеи, что могло пойти не так? Почему главный конец рукоятки чтения не закрылся, когда я закрыл трубку? Почему труба не сломалась, несмотря на завершение работы клиента и, несмотря на то, что я вызвал DisposeLocalCopyOfClientHandle?

Что я могу сделать, по крайней мере, обойти это? Может быть, просто Thread.Interrupt на поток чтения?..

Ответы

Ответ 1

Возможная последовательность событий в мастер-процессе, которым предшествуют номера потоков:

  • 1 - О вызове Microsoft.Win32.UnsafeNativeMethods.ReadFile(внутри BinaryReader.ReadInt32)
  • 2 - Убейте подчиненный процесс и закройте ручки труб.
  • 3 - Ручка конвейера win32 (которая теперь закрыта) повторно используется для другого канала (или файла или чего-то еще)
  • 1 - Microsoft.Win32.UnsafeNativeMethods.ReadFile начинает чтение, используя старый дескриптор, который теперь ссылается на какой-то другой объект и блокирует.

Fix:

Убейте подчиненный процесс в потоке 2, но не закрывайте ручки. Это приведет к завершению любого чтения в потоке 1, а затем закроет ручки в потоке 1.

Мораль:

Не закрывайте ручки, пока вы их используете.