You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
This repository has been archived by the owner on Mar 12, 2021. It is now read-only.
I have an application which, at the end of it's lifetime, calls hub_connection::stop(), waits for the returned task to finish, and then exits the process.
It turned out that this doesn't work reliably - sometimes, the process exits without having called abort on the server. For an unsecured SignalR connection, abort is called most of the time (but not always); for secure SignalR connections, abort is usually not called (but sometimes).
I looked a little into the issue and I think it is because connection_impl::shutdown starts two tasks: request_sender::abort and m_transport::disconnect. Only the latter task is returned from the method and ultimately waited upon, while request_sender::abort is fire-and-forget.
This seems to explain why the issue is more prevalent with TLS connections: In order to call abort, a new TLS handshake is performed, which isn't finished within the process' lifetime.
As a fix, I'd suggest wrapping both tasks in a task_group and returning that. In this way, the client can still fire-and-forget-stop the connection, but also wait for it synchronously and reliably:
auto abort_task = request_sender::abort(*m_web_request_factory, m_base_url, m_transport->get_transport_type(), m_connection_token,
m_connection_data, m_query_string, m_signalr_client_config)
.then([](pplx::task<utility::string_t> abort_task)
{
try
{
abort_task.get();
}
catch (...)
{
// We don't care about the result and even if the request failed there is not much we can do. We do
// need to observe the exception though to prevent from a crash due to unobserved exception exception.
}
});
auto disconnect_task = m_transport->disconnect();
return pplx::task<void>([abort_task, disconnect_task]()
{
pplx::task_group task_group;
task_group.run([abort_task](){abort_task.wait(); });
task_group.run([disconnect_task](){disconnect_task.wait(); });
task_group.wait();
});
The text was updated successfully, but these errors were encountered:
Interesting. Seems like stop() should wait for abort to finish/fail (and ignore the result) but dtor should be fire, forget and hope it gets through. The server will timeout inactive connections but it would be best not to rely on this behavior if not necessary - it's mostly a fallback for clients that suddenly disappear.
Yes, the server will notice the connection is gone when the heartbeat is missing, but that takes about 50s (iirc), which is slightly inconvenient for automated tests. The behavior should be as you said.
Sign up for freeto subscribe to this conversation on GitHub.
Already have an account?
Sign in.
I have an application which, at the end of it's lifetime, calls
hub_connection::stop()
, waits for the returned task to finish, and then exits the process.It turned out that this doesn't work reliably - sometimes, the process exits without having called
abort
on the server. For an unsecured SignalR connection,abort
is called most of the time (but not always); for secure SignalR connections,abort
is usually not called (but sometimes).I looked a little into the issue and I think it is because
connection_impl::shutdown
starts two tasks:request_sender::abort
andm_transport::disconnect
. Only the latter task is returned from the method and ultimately waited upon, whilerequest_sender::abort
is fire-and-forget.This seems to explain why the issue is more prevalent with TLS connections: In order to call
abort
, a new TLS handshake is performed, which isn't finished within the process' lifetime.As a fix, I'd suggest wrapping both tasks in a
task_group
and returning that. In this way, the client can still fire-and-forget-stop the connection, but also wait for it synchronously and reliably:The text was updated successfully, but these errors were encountered: