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

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions