cdef __pipe_init_uv_handle(UVStream handle, Loop loop):
    cdef int err

    handle._handle = <uv.uv_handle_t*>PyMem_RawMalloc(sizeof(uv.uv_pipe_t))
    if handle._handle is NULL:
        handle._abort_init()
        raise MemoryError()

    # Initialize pipe handle with ipc=0.
    # ipc=1 means that libuv will use recvmsg/sendmsg
    # instead of recv/send.
    err = uv.uv_pipe_init(handle._loop.uvloop,
                          <uv.uv_pipe_t*>handle._handle,
                          0)
    # UV_HANDLE_READABLE allows calling uv_read_start() on this pipe
    # even if it is O_WRONLY, see also #317, libuv/libuv#2058
    handle._handle.flags |= uv.UV_INTERNAL_HANDLE_READABLE
    if err < 0:
        handle._abort_init()
        raise convert_error(err)

    handle._finish_init()


cdef __pipe_open(UVStream handle, int fd):
    cdef int err
    err = uv.uv_pipe_open(<uv.uv_pipe_t *>handle._handle,
                          <uv.uv_file>fd)
    if err < 0:
        exc = convert_error(err)
        raise exc


cdef __pipe_get_socket(UVSocketHandle handle):
    fileno = handle._fileno()
    return PseudoSocket(uv.AF_UNIX, uv.SOCK_STREAM, 0, fileno)


@cython.no_gc_clear
cdef class UnixServer(UVStreamServer):

    @staticmethod
    cdef UnixServer new(Loop loop, object protocol_factory, Server server,
                        object backlog,
                        object ssl,
                        object ssl_handshake_timeout,
                        object ssl_shutdown_timeout):

        cdef UnixServer handle
        handle = UnixServer.__new__(UnixServer)
        handle._init(loop, protocol_factory, server, backlog,
                     ssl, ssl_handshake_timeout, ssl_shutdown_timeout)
        __pipe_init_uv_handle(<UVStream>handle, loop)
        return handle

    cdef _new_socket(self):
        return __pipe_get_socket(<UVSocketHandle>self)

    cdef _open(self, int sockfd):
        self._ensure_alive()
        __pipe_open(<UVStream>self, sockfd)
        self._mark_as_open()

    cdef bind(self, str path):
        cdef int err
        self._ensure_alive()
        err = uv.uv_pipe_bind(<uv.uv_pipe_t *>self._handle,
                              path.encode())
        if err < 0:
            exc = convert_error(err)
            self._fatal_error(exc, True)
            return

        self._mark_as_open()

    cdef UVStream _make_new_transport(self, object protocol, object waiter,
                                      object context):
        cdef UnixTransport tr
        tr = UnixTransport.new(self._loop, protocol, self._server, waiter,
                               context)
        return <UVStream>tr


@cython.no_gc_clear
cdef class UnixTransport(UVStream):

    @staticmethod
    cdef UnixTransport new(Loop loop, object protocol, Server server,
                           object waiter, object context):

        cdef UnixTransport handle
        handle = UnixTransport.__new__(UnixTransport)
        handle._init(loop, protocol, server, waiter, context)
        __pipe_init_uv_handle(<UVStream>handle, loop)
        return handle

    cdef _new_socket(self):
        return __pipe_get_socket(<UVSocketHandle>self)

    cdef _open(self, int sockfd):
        __pipe_open(<UVStream>self, sockfd)

    cdef connect(self, char* addr):
        cdef _PipeConnectRequest req
        req = _PipeConnectRequest(self._loop, self)
        req.connect(addr)


@cython.no_gc_clear
cdef class ReadUnixTransport(UVStream):

    @staticmethod
    cdef ReadUnixTransport new(Loop loop, object protocol, Server server,
                               object waiter):
        cdef ReadUnixTransport handle
        handle = ReadUnixTransport.__new__(ReadUnixTransport)
        # This is only used in connect_read_pipe() and subprocess_shell/exec()
        # directly, we could simply copy the current context.
        handle._init(loop, protocol, server, waiter, Context_CopyCurrent())
        __pipe_init_uv_handle(<UVStream>handle, loop)
        return handle

    cdef _new_socket(self):
        return __pipe_get_socket(<UVSocketHandle>self)

    cdef _open(self, int sockfd):
        __pipe_open(<UVStream>self, sockfd)

    def get_write_buffer_limits(self):
        raise NotImplementedError

    def set_write_buffer_limits(self, high=None, low=None):
        raise NotImplementedError

    def get_write_buffer_size(self):
        raise NotImplementedError

    def write(self, data):
        raise NotImplementedError

    def writelines(self, list_of_data):
        raise NotImplementedError

    def write_eof(self):
        raise NotImplementedError

    def can_write_eof(self):
        raise NotImplementedError

    def abort(self):
        raise NotImplementedError


@cython.no_gc_clear
cdef class WriteUnixTransport(UVStream):

    @staticmethod
    cdef WriteUnixTransport new(Loop loop, object protocol, Server server,
                                object waiter):
        cdef WriteUnixTransport handle
        handle = WriteUnixTransport.__new__(WriteUnixTransport)

        # We listen for read events on write-end of the pipe. When
        # the read-end is close, the uv_stream_t.read callback will
        # receive an error -- we want to silence that error, and just
        # close the transport.
        handle._close_on_read_error()

        # This is only used in connect_write_pipe() and subprocess_shell/exec()
        # directly, we could simply copy the current context.
        handle._init(loop, protocol, server, waiter, Context_CopyCurrent())
        __pipe_init_uv_handle(<UVStream>handle, loop)
        return handle

    cdef _new_socket(self):
        return __pipe_get_socket(<UVSocketHandle>self)

    cdef _open(self, int sockfd):
        __pipe_open(<UVStream>self, sockfd)

    def pause_reading(self):
        raise NotImplementedError

    def resume_reading(self):
        raise NotImplementedError


cdef class _PipeConnectRequest(UVRequest):
    cdef:
        UnixTransport transport
        uv.uv_connect_t _req_data

    def __cinit__(self, loop, transport):
        self.request = <uv.uv_req_t*> &self._req_data
        self.request.data = <void*>self
        self.transport = transport

    cdef connect(self, char* addr):
        # uv_pipe_connect returns void
        uv.uv_pipe_connect(<uv.uv_connect_t*>self.request,
                           <uv.uv_pipe_t*>self.transport._handle,
                           addr,
                           __pipe_connect_callback)

cdef void __pipe_connect_callback(
    uv.uv_connect_t* req,
    int status,
) noexcept with gil:
    cdef:
        _PipeConnectRequest wrapper
        UnixTransport transport

    wrapper = <_PipeConnectRequest> req.data
    transport = wrapper.transport

    if status < 0:
        exc = convert_error(status)
    else:
        exc = None

    try:
        transport._on_connect(exc)
    except BaseException as ex:
        wrapper.transport._fatal_error(ex, False)
    finally:
        wrapper.on_done()
