예제 #1
0
int api_tcp_accept(api_tcp_listener_t* listener, api_tcp_t* tcp)
{
    api_tcp_listener_accept_t accept;

    if (listener->loop->terminated)
        return API__TERMINATE;

    if (listener->status.closed ||
        listener->status.terminated ||
        listener->status.error != API__OK)
        return listener->status.error;

    accept.task = listener->loop->scheduler.current;
    accept.tcp = tcp;
    accept.success = 0;

    tcp->address.address.ss_family = listener->os_linux.af;
    tcp->address.length = sizeof(tcp->address.address);

    listener->os_linux.reserved = &accept;

    api_loop_read_add(listener->loop, listener->fd, &listener->os_linux.e);

    api_task_sleep(accept.task);

    api_loop_read_del(listener->loop, listener->fd, &listener->os_linux.e);

    listener->os_linux.reserved = 0;

    if (accept.success)
        return API__OK;

    return listener->status.error;
}
예제 #2
0
파일: api_loop.c 프로젝트: cmilan/libapi
int api_loop_stop_and_wait(api_loop_t* current, api_loop_t* loop)
{
    int error = 0;

    error = api_wait_exec(current, loop, 0);
    if (error != API__OK)
        return error;

    error = api_loop_stop(loop);
    if (error != API__OK)
        return error;

    api_task_sleep(current->base.scheduler.current);

    return API__OK;
}
예제 #3
0
int api_async_exec(api_loop_t* current, api_loop_t* loop, api_loop_fn callback, void* arg, size_t stack_size)
{
    api_exec_t exec;

    exec.async.loop = loop;
    exec.async.handler = api_async_exec_handler;
    exec.async.callback = callback;
    exec.async.arg = arg;
    exec.async.stack_size = stack_size;
    exec.loop = current;
    exec.task = current->base.scheduler.current;
    exec.result = 0;

    if (!PostQueuedCompletionStatus(loop->iocp, sizeof(exec),
            (ULONG_PTR)&g_api_async_processor, (LPOVERLAPPED)&exec))
    {
        exec.result = api_error_translate(GetLastError());
        return exec.result;
    }

    api_task_sleep(current->base.scheduler.current);

    return exec.result;
}
예제 #4
0
size_t api_stream_file_on_write(struct api_filter_t* filter,
                                const char* buffer, size_t length)
{
    api_stream_t* stream = filter->stream;
    api_stream_file_write_t write;
    api_timer_t timeout;
    uint64_t timeout_value = stream->write_timeout;
    struct timespec start, end, elapsed;
    uint64_t done = 0;
    int result;

    if (length == 0)
        return length;

    if (stream->status.write_timeout ||
        stream->status.error != API__OK ||
        stream->status.closed ||
        stream->status.peer_closed ||
        stream->status.terminated)
        return 0;

    if (stream->loop->base.terminated)
        return 0;

    if (timeout_value > 0)
    {
        memset(&timeout, 0, sizeof(timeout));
        timeout.task = write.task;

        api_timeout_exec(&stream->loop->base.timeouts, &timeout, timeout_value);
    }

    clock_gettime(CLOCK_MONOTONIC, &start);

    do
    {
        bzero(&write, sizeof(struct api_stream_file_write_t));

        write.aio.aio_fildes = stream->fd;
        write.aio.aio_buf = (char*)buffer + done;
        write.aio.aio_nbytes = length - done;
        write.aio.aio_offset = stream->impl.file.write_offset;
        write.aio.aio_sigevent.sigev_notify = SIGEV_THREAD;
        write.aio.aio_sigevent.sigev_notify_function =
                                    aio_write_completion_handler;
        write.aio.aio_sigevent.sigev_notify_attributes = 0;
        write.aio.aio_sigevent.sigev_value.sival_ptr = &write;
        write.done = 0;
        write.loop = stream->loop;
        write.task = stream->loop->base.scheduler.current;

        result = aio_write(&write.aio);

        if (result == -1)
        {
            stream->status.error = api_error_translate(errno);
            stream->filter_head->on_error(stream->filter_head,
                                            stream->status.error);
            break;
        }

        api_task_sleep(write.task);

        if (stream->loop->base.terminated)
            break;

        if (stream->status.write_timeout ||
            stream->status.error != API__OK ||
            stream->status.closed ||
            stream->status.peer_closed ||
            stream->status.terminated)
            break;

        if (timeout_value > 0 && timeout.elapsed)
            break;

        if (write.error != 0)
        {
            stream->status.error = write.error;
            break;
        }

        stream->impl.file.write_offset += write.done;
        done += write.done;

    }
    while (done < length);

    clock_gettime(CLOCK_MONOTONIC, &end);

	if (end.tv_nsec - start.tv_nsec < 0)
    {
		elapsed.tv_sec = end.tv_sec - start.tv_sec - 1;
		elapsed.tv_nsec = 1000000000 + end.tv_nsec - start.tv_nsec;
	}
    else
    {
		elapsed.tv_sec = end.tv_sec - start.tv_sec;
		elapsed.tv_nsec = end.tv_nsec - start.tv_nsec;
	}

    if (timeout_value > 0)
        api_timeout_exec(&stream->loop->base.timeouts, &timeout, 0);

    stream->write_bandwidth.sent += done;
    stream->write_bandwidth.period += elapsed.tv_sec * 1000000 + elapsed.tv_nsec / 1000;

    if (timeout_value > 0 && timeout.elapsed)
    {
        stream->status.write_timeout = 1;
        stream->filter_head->on_write_timeout(stream->filter_head);

        result = aio_cancel(stream->fd, &write.aio);
        /* handle error */

        return 0;
    }
    else
    {
        return done;
    }
}
예제 #5
0
size_t api_stream_file_on_read(struct api_filter_t* filter,
                               char* buffer, size_t length)
{
    api_stream_t* stream = filter->stream;
    api_stream_file_read_t read;
    api_timer_t timeout;
    struct timespec start, end, elapsed;
    uint64_t timeout_value = stream->read_timeout;
    int result;

    if (length == 0)
        return length;

    if (stream->status.read_timeout ||
        stream->status.eof ||
        stream->status.error != API__OK ||
        stream->status.closed ||
        stream->status.peer_closed ||
        stream->status.terminated)
        return 0;

    bzero(&read, sizeof(struct api_stream_file_read_t));

    read.aio.aio_fildes = stream->fd;
    read.aio.aio_buf = buffer;
    read.aio.aio_nbytes = length;
    read.aio.aio_offset = stream->impl.file.read_offset;
    read.aio.aio_sigevent.sigev_notify = SIGEV_THREAD;
    read.aio.aio_sigevent.sigev_notify_function = aio_read_completion_handler;
    read.aio.aio_sigevent.sigev_notify_attributes = 0;
    read.aio.aio_sigevent.sigev_value.sival_ptr = &read;
    read.done = 0;
    read.loop = stream->loop;
    read.task = stream->loop->base.scheduler.current;

    result = aio_read(&read.aio);

    if (result == -1)
    {
        stream->status.error = api_error_translate(errno);
        stream->filter_head->on_error(stream->filter_head, 
                                stream->status.error);
        return 0;
    }

    if (timeout_value > 0)
    {
        memset(&timeout, 0, sizeof(timeout));
        timeout.task = read.task;

        api_timeout_exec(&stream->loop->base.timeouts, &timeout, timeout_value);
    }

    clock_gettime(CLOCK_MONOTONIC, &start);

    api_task_sleep(read.task);

    clock_gettime(CLOCK_MONOTONIC, &end);

	if (end.tv_nsec - start.tv_nsec < 0)
    {
		elapsed.tv_sec = end.tv_sec - start.tv_sec - 1;
		elapsed.tv_nsec = 1000000000 + end.tv_nsec - start.tv_nsec;
	}
    else
    {
		elapsed.tv_sec = end.tv_sec - start.tv_sec;
		elapsed.tv_nsec = end.tv_nsec - start.tv_nsec;
	}

    if (timeout_value > 0)
        api_timeout_exec(&stream->loop->base.timeouts, &timeout, 0);

    stream->read_bandwidth.read += read.done;
    stream->read_bandwidth.period += elapsed.tv_sec * 1000000 + elapsed.tv_nsec / 1000;

    if (timeout_value > 0 && timeout.elapsed)
    {
        stream->status.read_timeout = 1;
        stream->filter_head->on_read_timeout(stream->filter_head);

        result = aio_cancel(stream->fd, &read.aio);
        /* handle error */

        return 0;
    }
    else
    {
        stream->impl.file.read_offset += read.done;
        if (read.done == 0)
            stream->status.eof = 1;

        if (read.error != 0)
            stream->status.error = read.error;

        return read.done;
    }
}
예제 #6
0
size_t api_stream_on_write(struct api_filter_t* filter,
                           const char* buffer, size_t length)
{
    api_stream_t* stream = filter->stream;
    api_stream_write_t write;
    api_timer_t timeout;
    struct timespec start, end, elapsed;
    uint64_t timeout_value = stream->write_timeout;

    if (length == 0)
        return length;

    if (stream->status.write_timeout ||
        stream->status.error != API__OK ||
        stream->status.closed ||
        stream->status.peer_closed ||
        stream->status.terminated)
        return -1;

    if (stream->loop->base.terminated)
        return -1;

    write.buffer = buffer;
    write.length = length;
    write.offset = 0;
    write.task = stream->loop->base.scheduler.current;

    stream->os_linux.reserved[1] = &write;

    if (timeout_value > 0)
    {
        memset(&timeout, 0, sizeof(timeout));
        timeout.task = write.task;

        api_timeout_exec(&stream->loop->base.timeouts, &timeout, timeout_value);
    }

    clock_gettime(CLOCK_MONOTONIC, &start);

    api_loop_write_add(stream->loop, stream->fd, &stream->os_linux.e);

    do
    {
        api_task_sleep(write.task);

        if (stream->status.write_timeout ||
            stream->status.error != API__OK ||
            stream->status.closed ||
            stream->status.peer_closed ||
            stream->status.terminated)
            break;

        if (timeout_value > 0 && timeout.elapsed)
            break;
    }
    while (write.offset < write.length);

    api_loop_write_del(stream->loop, stream->fd, &stream->os_linux.e);

    clock_gettime(CLOCK_MONOTONIC, &end);

	if (end.tv_nsec - start.tv_nsec < 0)
    {
		elapsed.tv_sec = end.tv_sec - start.tv_sec - 1;
		elapsed.tv_nsec = 1000000000 + end.tv_nsec - start.tv_nsec;
	}
    else
    {
		elapsed.tv_sec = end.tv_sec - start.tv_sec;
		elapsed.tv_nsec = end.tv_nsec - start.tv_nsec;
	}

    if (timeout_value > 0)
        api_timeout_exec(&stream->loop->base.timeouts, &timeout, 0);

    stream->os_linux.reserved[1] = 0;
    stream->write_bandwidth.sent += write.offset;
    stream->write_bandwidth.period += elapsed.tv_sec * 1000000 + elapsed.tv_nsec / 1000;

    if (timeout_value > 0 && timeout.elapsed)
    {
        stream->status.write_timeout = 1;
        stream->filter_head->on_write_timeout(stream->filter_head);
    }

    return write.offset;
}
예제 #7
0
size_t api_stream_on_read(struct api_filter_t* filter, 
                          char* buffer, size_t length)
{
    api_stream_t* stream = filter->stream;
    api_stream_read_t read;
    api_timer_t timeout;
    struct timespec start, end, elapsed;
    uint64_t timeout_value = stream->read_timeout;

    if (length == 0)
        return length;

    if (stream->status.read_timeout ||
        stream->status.eof ||
        stream->status.error != API__OK ||
        stream->status.closed ||
        stream->status.peer_closed ||
        stream->status.terminated)
        return 0;

    read.buffer = buffer;
    read.length = length;
    read.done = 0;
    read.task = stream->loop->base.scheduler.current;

    stream->os_linux.reserved[0] = &read;

    if (timeout_value > 0)
    {
        memset(&timeout, 0, sizeof(timeout));
        timeout.task = read.task;

        api_timeout_exec(&stream->loop->base.timeouts, &timeout, timeout_value);
    }

    clock_gettime(CLOCK_MONOTONIC, &start);

    api_loop_read_add(stream->loop, stream->fd, &stream->os_linux.e);

    api_task_sleep(read.task);

    api_loop_read_del(stream->loop, stream->fd, &stream->os_linux.e);

    clock_gettime(CLOCK_MONOTONIC, &end);

	if (end.tv_nsec - start.tv_nsec < 0)
    {
		elapsed.tv_sec = end.tv_sec - start.tv_sec - 1;
		elapsed.tv_nsec = 1000000000 + end.tv_nsec - start.tv_nsec;
	}
    else
    {
		elapsed.tv_sec = end.tv_sec - start.tv_sec;
		elapsed.tv_nsec = end.tv_nsec - start.tv_nsec;
	}

    if (timeout_value > 0)
        api_timeout_exec(&stream->loop->base.timeouts, &timeout, 0);

    stream->os_linux.reserved[0] = 0;
    stream->read_bandwidth.read += read.done;
    stream->read_bandwidth.period += elapsed.tv_sec * 1000000 + elapsed.tv_nsec / 1000;

    if (timeout_value > 0 && timeout.elapsed)
    {
        stream->status.read_timeout = 1;
        stream->filter_head->on_read_timeout(stream->filter_head);

        return 0;
    }
    else
    {
        return read.done;
    }
}
예제 #8
0
int api_tcp_connect(api_tcp_t* tcp,
                    api_loop_t* loop,
                    const char* ip, int port, uint64_t tmeout)
{
    struct sockaddr_in* addr_in = 
        (struct sockaddr_in*)&tcp->address.address;
    struct sockaddr_in6* addr_in6 = 
        (struct sockaddr_in6*)&tcp->address.address;
    struct sockaddr* a = (struct sockaddr*)&tcp->address.address;
    api_timer_t timeout;
    uint64_t timeout_value = tmeout;
    HANDLE handle;
    DWORD dwSent = 0;
    DWORD sys_error = 0;
    BOOL result;
    BOOL completed = FALSE;
    int af;
    int error = API__OK;

    memset(tcp, 0, sizeof(*tcp));

    if (loop->terminated)
    {
        tcp->stream.status.terminated = 1;
        return API__TERMINATE;
    }

    tcp->stream.os_win.processor = api_tcp_connect_processor;
    tcp->stream.os_win.reserved[0] = loop->scheduler.current;

    if (strchr(ip, ':') == 0)
    {
        // ipv4
        tcp->stream.fd = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP,
                                NULL, 0, WSA_FLAG_OVERLAPPED);

        af = AF_INET;

        tcp->address.length = sizeof(struct sockaddr_in);
        addr_in->sin_family = AF_INET;
        addr_in->sin_addr.s_addr = INADDR_ANY;
        addr_in->sin_port = 0;
    }
    else
    {
        // ipv6
        tcp->stream.fd = WSASocket(AF_INET6, SOCK_STREAM, IPPROTO_TCP, 
                                NULL, 0, WSA_FLAG_OVERLAPPED);

        af = AF_INET6;

        tcp->address.length = sizeof(struct sockaddr_in6);
        addr_in6->sin6_family = AF_INET6;
        addr_in6->sin6_addr = in6addr_any;
        addr_in6->sin6_port = 0;
    }

    error = api_socket_non_block(tcp->stream.fd, 1);
    error = api_socket_recv_buffer_size(tcp->stream.fd, 0);
    error = api_socket_send_buffer_size(tcp->stream.fd, 0);
    error = api_tcp_nodelay(tcp->stream.fd, 1);

    if (0 != bind(tcp->stream.fd, a, tcp->address.length))
    {
        error = api_error_translate(WSAGetLastError());
        closesocket(tcp->stream.fd);
        return error;
    }

    handle = CreateIoCompletionPort((HANDLE)tcp->stream.fd, loop->iocp, 
        (ULONG_PTR)&tcp->stream.os_win, 0);
    if (handle == NULL)
    {
        sys_error = GetLastError();
        closesocket(tcp->stream.fd);
        return api_error_translate(sys_error);
    }

    SetFileCompletionNotificationModes((HANDLE)tcp->stream.fd,
                    FILE_SKIP_COMPLETION_PORT_ON_SUCCESS);

    if (af == AF_INET)
    {
        addr_in->sin_family = AF_INET;
        addr_in->sin_addr.s_addr = inet_addr(ip);
        addr_in->sin_port = htons(port);
    }
    else
    {
        addr_in6->sin6_family = AF_INET6;
        addr_in6->sin6_addr = in6addr_any;
        addr_in6->sin6_port = htons(port);

        inet_pton(AF_INET6, ip, (void*)&addr_in6->sin6_addr);
    }

    if (timeout_value > 0)
    {
        memset(&timeout, 0, sizeof(timeout));
        timeout.task = loop->scheduler.current;

        api_timeout_exec(&loop->timeouts, &timeout, timeout_value);
    }

    result = lpfnConnectEx(tcp->stream.fd, a, tcp->address.length,
                        NULL, 0,
                        &dwSent, (LPOVERLAPPED)&tcp->stream.os_win.read);

    if (!result)
    {
        sys_error = WSAGetLastError();
        if (sys_error == ERROR_SUCCESS)
        {
            completed = TRUE;
        }
        else
        if (sys_error != WSA_IO_PENDING)
        {
            completed = TRUE;
            error = api_error_translate(sys_error);
            tcp->stream.status.error = error;
        }
    }
    else
    {
        completed = TRUE;
    }

    if (!completed)
        api_task_sleep(loop->scheduler.current);

    if (timeout_value > 0)
        api_timeout_exec(&loop->timeouts, &timeout, 0);

    if (timeout_value > 0 && timeout.elapsed)
    {
        tcp->stream.status.read_timeout = 1;

        if (API__OK != error)
            error = api_error_translate(ERROR_TIMEOUT);
    }

    if (API__OK == error)
    {
        api_stream_init(&tcp->stream, STREAM_Tcp, tcp->stream.fd);
        tcp->stream.loop = loop;
        
        api_loop_ref(loop);
        return API__OK;
    }
    else
    {
        closesocket(tcp->stream.fd);
    }

    return error;
}
예제 #9
0
int api_tcp_accept(api_tcp_listener_t* listener, api_tcp_t* tcp)
{
    api_tcp_listener_accept_t accept;
    CHAR buffer[2 * (sizeof(SOCKADDR_IN) + 16)];
    SOCKADDR *lpLocalSockaddr = NULL;
    SOCKADDR *lpRemoteSockaddr = NULL;
    int localSockaddrLen = 0;
    int remoteSockaddrLen = 0;
    DWORD dwBytes;
    BOOL result;
    BOOL success = FALSE;
    BOOL completed = FALSE;
    DWORD sys_error;
    int error = API__OK;

    memset(&listener->os_win.ovl, 0, sizeof(listener->os_win.ovl));

    if (listener->loop->terminated)
        return API__TERMINATE;

    if (listener->status.closed ||
        listener->status.terminated ||
        listener->status.error != API__OK)
        return listener->status.error;

    accept.task = listener->loop->scheduler.current;

    listener->os_win.reserved = &accept;

    do {

        success = FALSE;
        completed = FALSE;

        tcp->stream.fd = WSASocket(listener->os_win.af, SOCK_STREAM,
                    IPPROTO_TCP, NULL, 0, WSA_FLAG_OVERLAPPED);
        result = lpfnAcceptEx(listener->fd,
                            tcp->stream.fd,
                            buffer, 0, 
                            sizeof(SOCKADDR_IN) + 16, sizeof(SOCKADDR_IN) + 16,
                            &dwBytes, (LPOVERLAPPED)&listener->os_win.ovl);
        if (!result)
        {
            sys_error = WSAGetLastError();
            if (sys_error == ERROR_SUCCESS)
            {
                completed = TRUE;
            }
            else
            if (sys_error != WSA_IO_PENDING)
            {
                listener->status.error = api_error_translate(sys_error);
                closesocket(tcp->stream.fd);
                break;
            }
        }
        else
        {
            completed = TRUE;
        }

        if (!completed)
            api_task_sleep(accept.task);

        lpfnGetAcceptExSockaddrs(buffer, 0, 
            sizeof(SOCKADDR_IN) + 16, sizeof(SOCKADDR_IN) + 16,
            &lpLocalSockaddr, &localSockaddrLen,
            &lpRemoteSockaddr, &remoteSockaddrLen);

        tcp->address.length = remoteSockaddrLen;
        memcpy(&tcp->address.address, lpRemoteSockaddr, remoteSockaddrLen);

        success = listener->on_accept(listener, tcp);
        if (success)
        {
            error = api_socket_non_block(tcp->stream.fd, 1);
            error = api_socket_recv_buffer_size(tcp->stream.fd, 0);
            error = api_socket_send_buffer_size(tcp->stream.fd, 0);
            error = api_tcp_nodelay(tcp->stream.fd, 1);

            api_stream_init(&tcp->stream, STREAM_Tcp, tcp->stream.fd);
        }
        else
        {
            closesocket(tcp->stream.fd);
        }
    }
    while (!success);

    listener->os_win.reserved = 0;

    if (success)
        return API__OK;

    return listener->status.error;
}
예제 #10
0
int api_tcp_connect(api_tcp_t* tcp,
                    api_loop_t* loop,
                    const char* ip, int port, uint64_t tmeout)
{
    struct sockaddr_in* addr_in =
        (struct sockaddr_in*)&tcp->address.address;
    struct sockaddr_in6* addr_in6 =
        (struct sockaddr_in6*)&tcp->address.address;
    struct sockaddr* a = (struct sockaddr*)&tcp->address.address;
    int error = API__OK;
    api_timer_t timeout;
    uint64_t timeout_value = tmeout;

    memset(tcp, 0, sizeof(*tcp));

    if (loop->terminated)
    {
        tcp->stream.status.terminated = 1;
        return API__TERMINATE;
    }

    tcp->stream.os_linux.processor = api_tcp_connect_processor;
    tcp->stream.os_linux.reserved[0] = loop->scheduler.current;
    tcp->stream.os_linux.e.data.ptr = &tcp->stream.os_linux;

    if (strchr(ip, ':') == 0)
    {
        // ipv4
        tcp->stream.fd = 
            socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, IPPROTO_TCP);

        tcp->address.length = sizeof(struct sockaddr_in);
        addr_in->sin_family = AF_INET;
        addr_in->sin_addr.s_addr = inet_addr(ip);
        addr_in->sin_port = htons(port);
    }
    else
    {
        // ipv6
        tcp->stream.fd =
            socket(AF_INET6, SOCK_STREAM | SOCK_NONBLOCK, IPPROTO_TCP);

        tcp->address.length = sizeof(struct sockaddr_in6);
        addr_in6->sin6_family = AF_INET6;
        addr_in6->sin6_addr = in6addr_any;
        addr_in6->sin6_port = htons(port);

        inet_pton(AF_INET6, ip, (void*)&addr_in6->sin6_addr.__in6_u);
    }

    error = api_socket_non_block(tcp->stream.fd, 1);
    error = api_socket_recv_buffer_size(tcp->stream.fd, 0);
    error = api_socket_send_buffer_size(tcp->stream.fd, 0);
    error = api_tcp_nodelay(tcp->stream.fd, 1);

    if (0 != connect(tcp->stream.fd, a, tcp->address.length))
    {
        if (errno != EINPROGRESS)
        {
            error = api_error_translate(errno);
        }
        else
        {
            // wait needed

            tcp->stream.os_linux.e.events = 
                EPOLLERR | EPOLLHUP | EPOLLRDHUP | EPOLLOUT;

            error = epoll_ctl(loop->epoll, EPOLL_CTL_ADD, tcp->stream.fd,
                                        &tcp->stream.os_linux.e);

            error = api_error_translate(error);
            if (API__OK == error)
            {
                error = api_loop_write_add(loop, tcp->stream.fd, 
                                        &tcp->stream.os_linux.e);

                if (API__OK != error)
                {
                    epoll_ctl(loop->epoll, EPOLL_CTL_DEL, tcp->stream.fd,
                        &tcp->stream.os_linux.e);
                }
                else
                {
                    if (timeout_value > 0)
                    {
                        memset(&timeout, 0, sizeof(timeout));
                        timeout.task = loop->scheduler.current;

                        api_timeout_exec(&loop->timeouts, &timeout,
                                                    timeout_value);
                    }

                    api_task_sleep(loop->scheduler.current);

                    if (timeout_value > 0)
                        api_timeout_exec(&loop->timeouts, &timeout, 0);

                    if (timeout_value > 0 && timeout.elapsed)
                    {
                        tcp->stream.status.read_timeout = 1;
                        error = api_error_translate(ETIMEDOUT);
                    }

                    if (tcp->stream.status.closed ||
                        tcp->stream.status.terminated ||
                        tcp->stream.status.error != API__OK)
                    {
                        epoll_ctl(loop->epoll, EPOLL_CTL_DEL, tcp->stream.fd,
                                                &tcp->stream.os_linux.e);
                    }
                    else
                    {
                        api_loop_write_del(loop, tcp->stream.fd,
                                            &tcp->stream.os_linux.e);
                    }
                }
            }
        }
    }
    else
    {
        // completed immediately
    }

    if (API__OK == error &&
        tcp->stream.status.closed == 0 &&
        tcp->stream.status.terminated == 0 &&
        tcp->stream.status.error == API__OK)
    {
        api_stream_init(&tcp->stream, STREAM_Tcp, tcp->stream.fd);
        tcp->stream.loop = loop;
        api_loop_ref(loop);
        return API__OK;
    }

    close(tcp->stream.fd);

    if (API__OK != error)
        return error;

    if (API__OK != tcp->stream.status.error)
        return tcp->stream.status.error;

    return -1;
}