Skip to content

Commit d1afaf3

Browse files
Fix stop to properly wait (#73)
1 parent 2dd0539 commit d1afaf3

17 files changed

+531
-187
lines changed

include/signalrclient/cancellation_token.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#pragma once
66

77
#include <memory>
8+
#include <functional>
89

910
namespace signalr
1011
{

include/signalrclient/connection.h

Lines changed: 0 additions & 54 deletions
This file was deleted.

src/signalrclient/cancellation_token_source.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,11 +53,14 @@ namespace signalr
5353
cancellation_token_source(const cancellation_token_source&) = delete;
5454
cancellation_token_source& operator=(const cancellation_token_source&) = delete;
5555

56-
void cancel()
56+
// returns false if already canceled, otherwise true if this cancels
57+
bool cancel()
5758
{
59+
bool signal = false;
5860
std::vector<std::function<void()>> callbacks;
5961
{
6062
std::lock_guard<std::mutex> lock(m_lock);
63+
signal = !m_signaled;
6164
m_signaled = true;
6265
m_condition.notify_all();
6366
callbacks = std::move(m_callbacks);
@@ -84,6 +87,8 @@ namespace signalr
8487
throw errors;
8588
}
8689
}
90+
91+
return signal;
8792
}
8893

8994
void reset()

src/signalrclient/connection.cpp

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,19 +3,33 @@
33
// See the LICENSE file in the project root for more information.
44

55
#include "stdafx.h"
6-
#include "signalrclient/connection.h"
6+
#include "connection.h"
77
#include "signalrclient/transport_type.h"
88
#include "connection_impl.h"
9+
#include "completion_event.h"
910

1011
namespace signalr
1112
{
12-
connection::connection(const std::string& url, trace_level trace_level, std::shared_ptr<log_writer> log_writer)
13-
: m_pImpl(connection_impl::create(url, trace_level, std::move(log_writer)))
13+
connection::connection(const std::string& url, trace_level trace_level, std::shared_ptr<log_writer> log_writer,
14+
std::function<std::shared_ptr<http_client>(const signalr_client_config&)> http_client_factory,
15+
std::function<std::shared_ptr<websocket_client>(const signalr_client_config&)> websocket_factory, bool skip_negotiation)
16+
: m_pImpl(connection_impl::create(url, trace_level, std::move(log_writer), http_client_factory, websocket_factory, skip_negotiation))
1417
{}
1518

1619
// Do NOT remove this destructor. Letting the compiler generate and inline the default dtor may lead to
1720
// undefinded behavior since we are using an incomplete type. More details here: http://herbsutter.com/gotw/_100/
18-
connection::~connection() = default;
21+
connection::~connection()
22+
{
23+
if (m_pImpl)
24+
{
25+
completion_event completion;
26+
m_pImpl->stop([completion](std::exception_ptr) mutable
27+
{
28+
completion.set();
29+
}, nullptr);
30+
completion.get();
31+
}
32+
}
1933

2034
void connection::start(std::function<void(std::exception_ptr)> callback) noexcept
2135
{

src/signalrclient/connection.h

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
// See the LICENSE file in the project root for more information.
4+
5+
#pragma once
6+
7+
#include <memory>
8+
#include <functional>
9+
#include "signalrclient/connection_state.h"
10+
#include "signalrclient/trace_level.h"
11+
#include "signalrclient/log_writer.h"
12+
#include "signalrclient/signalr_client_config.h"
13+
#include "signalrclient/transfer_format.h"
14+
#include "signalrclient/http_client.h"
15+
#include "signalrclient/websocket_client.h"
16+
17+
namespace signalr
18+
{
19+
class connection_impl;
20+
21+
class connection
22+
{
23+
public:
24+
typedef std::function<void(std::string&&)> message_received_handler;
25+
26+
explicit connection(const std::string& url, trace_level trace_level = trace_level::info, std::shared_ptr<log_writer> log_writer = nullptr,
27+
std::function<std::shared_ptr<http_client>(const signalr_client_config&)> http_client_factory = nullptr,
28+
std::function<std::shared_ptr<websocket_client>(const signalr_client_config&)> websocket_factory = nullptr, bool skip_negotiation = false);
29+
30+
~connection();
31+
32+
connection(const connection&) = delete;
33+
34+
connection& operator=(const connection&) = delete;
35+
36+
void start(std::function<void(std::exception_ptr)> callback) noexcept;
37+
38+
void send(const std::string& data, transfer_format transfer_format, std::function<void(std::exception_ptr)> callback) noexcept;
39+
40+
void set_message_received(const message_received_handler& message_received_callback);
41+
void set_disconnected(const std::function<void(std::exception_ptr)>& disconnected_callback);
42+
43+
void set_client_config(const signalr_client_config& config);
44+
45+
void stop(std::function<void(std::exception_ptr)> callback, std::exception_ptr exception) noexcept;
46+
47+
connection_state get_connection_state() const noexcept;
48+
std::string get_connection_id() const;
49+
50+
private:
51+
// The recommended smart pointer to use when doing pImpl is the `std::unique_ptr`. However
52+
// we are capturing the m_pImpl instance in the lambdas used by tasks which can outlive
53+
// the connection instance. Using `std::shared_ptr` guarantees that we won't be using
54+
// a deleted object if the task is run after the `connection` instance goes away.
55+
std::shared_ptr<connection_impl> m_pImpl;
56+
};
57+
}

src/signalrclient/connection_impl.cpp

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,6 @@
2020

2121
namespace signalr
2222
{
23-
std::shared_ptr<connection_impl> connection_impl::create(const std::string& url, trace_level trace_level, const std::shared_ptr<log_writer>& log_writer)
24-
{
25-
return connection_impl::create(url, trace_level, log_writer, nullptr, nullptr, false);
26-
}
27-
2823
std::shared_ptr<connection_impl> connection_impl::create(const std::string& url, trace_level trace_level, const std::shared_ptr<log_writer>& log_writer,
2924
std::function<std::shared_ptr<http_client>(const signalr_client_config&)> http_client_factory, std::function<std::shared_ptr<websocket_client>(const signalr_client_config&)> websocket_factory, const bool skip_negotiation)
3025
{

src/signalrclient/connection_impl.h

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,6 @@ namespace signalr
2727
class connection_impl : public std::enable_shared_from_this<connection_impl>
2828
{
2929
public:
30-
static std::shared_ptr<connection_impl> create(const std::string& url, trace_level trace_level, const std::shared_ptr<log_writer>& log_writer);
31-
3230
static std::shared_ptr<connection_impl> create(const std::string& url, trace_level trace_level, const std::shared_ptr<log_writer>& log_writer,
3331
std::function<std::shared_ptr<http_client>(const signalr_client_config&)> http_client_factory, std::function<std::shared_ptr<websocket_client>(const signalr_client_config&)> websocket_factory, bool skip_negotiation = false);
3432

src/signalrclient/hub_connection.cpp

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,18 @@ namespace signalr
3131

3232
// Do NOT remove this destructor. Letting the compiler generate and inline the default dtor may lead to
3333
// undefined behavior since we are using an incomplete type. More details here: http://herbsutter.com/gotw/_100/
34-
hub_connection::~hub_connection() = default;
34+
hub_connection::~hub_connection()
35+
{
36+
if (m_pImpl)
37+
{
38+
completion_event completion;
39+
m_pImpl->stop([completion](std::exception_ptr) mutable
40+
{
41+
completion.set();
42+
});
43+
completion.get();
44+
}
45+
}
3546

3647
void hub_connection::start(std::function<void(std::exception_ptr)> callback) noexcept
3748
{

src/signalrclient/hub_connection_impl.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,11 +38,11 @@ namespace signalr
3838
hub_connection_impl::hub_connection_impl(const std::string& url, std::unique_ptr<hub_protocol>&& hub_protocol, trace_level trace_level,
3939
const std::shared_ptr<log_writer>& log_writer, std::function<std::shared_ptr<http_client>(const signalr_client_config&)> http_client_factory,
4040
std::function<std::shared_ptr<websocket_client>(const signalr_client_config&)> websocket_factory, const bool skip_negotiation)
41-
: m_connection(connection_impl::create(url, trace_level, log_writer,
42-
http_client_factory, websocket_factory, skip_negotiation)), m_logger(log_writer, trace_level),
41+
: m_connection(connection_impl::create(url, trace_level, log_writer, http_client_factory, websocket_factory, skip_negotiation))
42+
, m_logger(log_writer, trace_level),
4343
m_callback_manager("connection went out of scope before invocation result was received"),
4444
m_handshakeReceived(false), m_disconnected([](std::exception_ptr) noexcept {}), m_protocol(std::move(hub_protocol))
45-
{}
45+
{ }
4646

4747
void hub_connection_impl::initialize()
4848
{

src/signalrclient/hub_connection_impl.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,15 @@
55
#pragma once
66

77
#include <unordered_map>
8-
#include "connection_impl.h"
98
#include "callback_manager.h"
109
#include "case_insensitive_comparison_utils.h"
1110
#include "completion_event.h"
1211
#include "signalrclient/signalr_value.h"
1312
#include "hub_protocol.h"
13+
#include "connection.h"
14+
#include "logger.h"
15+
#include "cancellation_token_source.h"
16+
#include "connection_impl.h"
1417

1518
namespace signalr
1619
{

0 commit comments

Comments
 (0)