File: Microsoft\Win32\SafeHandles\SafePipeHandle.Unix.cs
Web Access
Project: src\src\libraries\System.IO.Pipes\src\System.IO.Pipes.csproj (System.IO.Pipes)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System;
using System.Diagnostics;
using System.Net.Sockets;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Security;
using System.Threading;
 
namespace Microsoft.Win32.SafeHandles
{
    public sealed partial class SafePipeHandle : SafeHandleZeroOrMinusOneIsInvalid
    {
        private const int DefaultInvalidHandle = -1;
 
        // For anonymous pipes, SafePipeHandle.handle is the file descriptor of the pipe.
        // For named pipes, SafePipeHandle.handle is a copy of the file descriptor
        // extracted from the Socket's SafeHandle.
        // This allows operations related to file descriptors to be performed directly on the SafePipeHandle,
        // and operations that should go through the Socket to be done via PipeSocket. We keep the
        // Socket's SafeHandle alive as long as this SafeHandle is alive.
 
        private Socket? _pipeSocket;
        private SafeHandle? _pipeSocketHandle;
        private volatile int _disposed;
 
        internal SafePipeHandle(Socket namedPipeSocket) : base(ownsHandle: true)
        {
            SetPipeSocketInterlocked(namedPipeSocket, ownsHandle: true);
            base.SetHandle(_pipeSocketHandle!.DangerousGetHandle());
        }
 
        internal Socket PipeSocket => _pipeSocket ?? CreatePipeSocket();
 
        internal SafeHandle? PipeSocketHandle => _pipeSocketHandle;
 
        protected override void Dispose(bool disposing)
        {
            base.Dispose(disposing); // must be called before trying to Dispose the socket
            _disposed = 1;
            if (disposing && Volatile.Read(ref _pipeSocket) is Socket socket)
            {
                socket.Dispose();
                _pipeSocket = null;
            }
        }
 
        protected override bool ReleaseHandle()
        {
            Debug.Assert(!IsInvalid);
 
            if (_pipeSocketHandle != null)
            {
                base.SetHandle((IntPtr)DefaultInvalidHandle);
                _pipeSocketHandle.DangerousRelease();
                _pipeSocketHandle = null;
                return true;
            }
            else
            {
                return (long)handle >= 0 ?
                    Interop.Sys.Close(handle) == 0 :
                    true;
            }
        }
 
        public override bool IsInvalid
        {
            get { return (long)handle < 0 && _pipeSocket == null; }
        }
 
        private Socket CreatePipeSocket(bool ownsHandle = true)
        {
            Socket? socket = null;
            if (_disposed == 0)
            {
                bool refAdded = false;
                try
                {
                    DangerousAddRef(ref refAdded);
 
                    socket = SetPipeSocketInterlocked(new Socket(new SafeSocketHandle(handle, ownsHandle)), ownsHandle);
 
                    // Double check if we haven't Disposed in the meanwhile, and ensure
                    // the Socket is disposed, in case Dispose() missed the _pipeSocket assignment.
                    if (_disposed == 1)
                    {
                        Volatile.Write(ref _pipeSocket, null);
                        socket.Dispose();
                        socket = null;
                    }
                }
                finally
                {
                    if (refAdded)
                    {
                        DangerousRelease();
                    }
                }
            }
 
            ObjectDisposedException.ThrowIf(socket is null, this);
            return socket;
        }
 
        private Socket SetPipeSocketInterlocked(Socket socket, bool ownsHandle)
        {
            Debug.Assert(socket != null);
 
            // Multiple threads may try to create the PipeSocket.
            Socket? current = Interlocked.CompareExchange(ref _pipeSocket, socket, null);
            if (current != null)
            {
                socket.Dispose();
                return current;
            }
 
            // If we own the handle, defer ownership to the SocketHandle.
            SafeSocketHandle socketHandle = _pipeSocket.SafeHandle;
            if (ownsHandle)
            {
                _pipeSocketHandle = socketHandle;
 
                bool ignored = false;
                socketHandle.DangerousAddRef(ref ignored);
            }
 
            return socket;
        }
 
        internal void SetHandle(IntPtr descriptor, bool ownsHandle = true)
        {
            base.SetHandle(descriptor);
 
            // Avoid throwing when we own the handle by defering pipe creation.
            if (!ownsHandle)
            {
                _pipeSocket = CreatePipeSocket(ownsHandle);
            }
        }
    }
}