Skip to content

TLS websocket cannot be used in different coroutines for i/o #993

@DenisRazinkin

Description

@DenisRazinkin

Add a description

In my case, I have sporadic read operations (subscriptions to new types of events, which may be sent by the client once on connection) and a huge number of events to send (up to 2k per second per socket)

TLS websocket is not thread-safe. It means that it should be used in a single coroutine, and part of the time is taken for dummy syscalls on TryRecv.
The handling may look like:

void Handle(userver::server::websocket::WebSocketConnection& connection,
                               userver::server::request::RequestContext& requestContext) const override {

while (!engine::current_task::ShouldCancel()) {
    userver::server::websocket::Message message;
    if (connection.TryRecv(message)) {
            HandleMessage(message)
    }

    std::string message;
    if (queue->Pop(message, deadline(10ms)) {
           connection.SendText(message);
    }
}
}

In the load test, it leads to 35-40% performance degradation in comparison with two coroutines.
I need to be able to read and write from two coroutines for better performance, e.g.:

void ReadLoop(userver::server::websocket::WebSocketConnection& connection)
{

        userver::server::websocket::Message message;
        while (!userver::engine::current_task::ShouldCancel())
        {
            if (!connection.WaitReadable(Deadline(10ms)))
            {
                continue;
            }

            {
                std::lock_guard lock(_sslLock);
                if (!connection.Recv(message))
                {
                    continue;
                }
            }

           HandleMessage(message);
        }
}

void SendLoop( userver::server::websocket::WebSocketConnection& connection)
{
     while (!engine::current_task::ShouldCancel()) {

             std::string message;
             if (queue->Pop(message) {
                   std::lock_guard lock(_sslLock);
                    connection.SendText(message);
              }
       }
}

At least there is no WaitReadable for WebSocketConnection and current implementation of TlsWrapper::WaitReadable is not-thread safe as well and requires lock:

bool TlsWrapper::WaitReadable(Deadline deadline) {
    impl_->CheckAlive();
    char buf = 0;
    return impl_->PerformSslIo(
        &SSL_peek_ex, &buf, 1, impl::TransferMode::kOnce, InterruptAction::kPass, deadline, "WaitReadable"
    );
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions