Skip to content

Commit 6e997d5

Browse files
Add public cancellation_token and pass to http_client (#64)
1 parent 0d51a36 commit 6e997d5

27 files changed

+723
-108
lines changed
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
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+
9+
namespace signalr
10+
{
11+
class cancellation_token_source;
12+
13+
class cancellation_token
14+
{
15+
public:
16+
/**
17+
* Register a callback to run when this token is canceled or destructed.
18+
* If the token is already canceled, the callback will run immediately.
19+
*/
20+
void register_callback(std::function<void()> callback);
21+
22+
/**
23+
* Check if the token has been canceled already.
24+
*/
25+
bool is_canceled() const;
26+
private:
27+
friend cancellation_token get_cancellation_token(std::weak_ptr<cancellation_token_source> s);
28+
29+
cancellation_token(std::weak_ptr<cancellation_token_source> parent)
30+
: mParent(parent)
31+
{
32+
}
33+
34+
std::weak_ptr<cancellation_token_source> mParent;
35+
};
36+
}

include/signalrclient/http_client.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#include <functional>
99
#include <map>
1010
#include <chrono>
11+
#include "cancellation_token.h"
1112
#include <exception>
1213

1314
namespace signalr
@@ -62,7 +63,8 @@ namespace signalr
6263
class http_client
6364
{
6465
public:
65-
virtual void send(const std::string& url, const http_request& request, std::function<void(const http_response&, std::exception_ptr)> callback) = 0;
66+
virtual void send(const std::string& url, http_request& request,
67+
std::function<void(const http_response&, std::exception_ptr)> callback, cancellation_token token) = 0;
6668

6769
virtual ~http_client() {}
6870
};

src/signalrclient/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
set (SOURCES
22
callback_manager.cpp
3+
cancellation_token.cpp
4+
cancellation_token_source.cpp
35
connection.cpp
46
connection_impl.cpp
57
default_http_client.cpp
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
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+
#include "stdafx.h"
6+
#include "cancellation_token_source.h"
7+
#include "signalrclient/cancellation_token.h"
8+
9+
namespace signalr
10+
{
11+
void cancellation_token::register_callback(std::function<void()> callback)
12+
{
13+
auto obj = mParent.lock();
14+
if (obj)
15+
{
16+
obj->register_callback(callback);
17+
}
18+
else
19+
{
20+
callback();
21+
}
22+
}
23+
24+
bool cancellation_token::is_canceled() const
25+
{
26+
auto obj = mParent.lock();
27+
if (obj)
28+
{
29+
return obj->is_canceled();
30+
}
31+
return true;
32+
}
33+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
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+
#include "stdafx.h"
6+
#include "cancellation_token_source.h"
7+
8+
namespace signalr
9+
{
10+
const unsigned int cancellation_token_source::timeout_infinite = 0xFFFFFFFF;
11+
12+
cancellation_token get_cancellation_token(std::weak_ptr<cancellation_token_source> s)
13+
{
14+
return cancellation_token(s);
15+
}
16+
}

src/signalrclient/cancellation_token.h renamed to src/signalrclient/cancellation_token_source.h

Lines changed: 59 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -7,57 +7,91 @@
77
#include <assert.h>
88
#include <condition_variable>
99
#include <mutex>
10+
#include <vector>
11+
#include "signalrclient/cancellation_token.h"
1012

1113
namespace signalr
1214
{
1315
class canceled_exception : public std::exception
1416
{
17+
virtual char const* what() const noexcept
18+
{
19+
return "an operation was canceled";
20+
}
1521
};
1622

17-
class cancellation_token
23+
class aggregate_exception : public std::exception
1824
{
1925
public:
20-
static const unsigned int timeout_infinite = 0xFFFFFFFF;
26+
void add_exception(const std::exception& ex)
27+
{
28+
if (!m_message.empty())
29+
{
30+
m_message += '\n';
31+
}
32+
m_message += ex.what();
33+
}
2134

22-
cancellation_token() noexcept
23-
: m_signaled(false)
35+
virtual char const* what() const noexcept
2436
{
37+
return m_message.c_str();
2538
}
39+
private:
40+
std::string m_message;
41+
};
2642

27-
cancellation_token(const cancellation_token&) = delete;
28-
cancellation_token& operator=(const cancellation_token&) = delete;
43+
class cancellation_token_source
44+
{
45+
public:
46+
static const unsigned int timeout_infinite;
2947

30-
~cancellation_token()
48+
cancellation_token_source() noexcept
49+
: m_signaled(false)
3150
{
32-
if (m_callback)
33-
{
34-
m_callback();
35-
}
3651
}
3752

53+
cancellation_token_source(const cancellation_token_source&) = delete;
54+
cancellation_token_source& operator=(const cancellation_token_source&) = delete;
55+
3856
void cancel()
3957
{
40-
std::function<void()> callback;
58+
std::vector<std::function<void()>> callbacks;
4159
{
4260
std::lock_guard<std::mutex> lock(m_lock);
4361
m_signaled = true;
4462
m_condition.notify_all();
45-
callback = m_callback;
46-
m_callback = nullptr;
63+
callbacks = std::move(m_callbacks);
64+
m_callbacks = std::vector<std::function<void()>>();
4765
} // unlock
4866

49-
if (callback)
67+
if (!callbacks.empty())
5068
{
51-
callback();
69+
aggregate_exception errors;
70+
for (auto& func : callbacks)
71+
{
72+
try
73+
{
74+
func();
75+
}
76+
catch (const std::exception& ex)
77+
{
78+
errors.add_exception(ex);
79+
}
80+
}
81+
82+
if (errors.what()[0] != 0)
83+
{
84+
throw errors;
85+
}
5286
}
5387
}
5488

5589
void reset()
5690
{
5791
std::lock_guard<std::mutex> lock(m_lock);
58-
assert(m_callback == nullptr);
92+
assert(m_callbacks.empty());
5993
m_signaled = false;
60-
m_callback = nullptr;
94+
m_callbacks.clear();
6195
}
6296

6397
bool is_canceled() const
@@ -68,7 +102,7 @@ namespace signalr
68102
unsigned int wait(unsigned int timeout)
69103
{
70104
std::unique_lock<std::mutex> lock(m_lock);
71-
if (timeout == cancellation_token::timeout_infinite)
105+
if (timeout == cancellation_token_source::timeout_infinite)
72106
{
73107
m_condition.wait(lock, [this]() noexcept { return m_signaled; });
74108
return 0;
@@ -79,13 +113,13 @@ namespace signalr
79113
const auto status = m_condition.wait_for(lock, period, [this]() noexcept { return m_signaled; });
80114
assert(status == m_signaled);
81115
// Return 0 if the wait completed as a result of signaling the event. Otherwise, return timeout_infinite
82-
return status ? 0 : cancellation_token::timeout_infinite;
116+
return status ? 0 : cancellation_token_source::timeout_infinite;
83117
}
84118
}
85119

86120
unsigned int wait()
87121
{
88-
return wait(cancellation_token::timeout_infinite);
122+
return wait(cancellation_token_source::timeout_infinite);
89123
}
90124

91125
void throw_if_cancellation_requested() const
@@ -101,14 +135,13 @@ namespace signalr
101135
bool run_callback = false;
102136
{
103137
std::lock_guard<std::mutex> lock(m_lock);
104-
assert(m_callback == nullptr);
105138
if (m_signaled)
106139
{
107140
run_callback = m_signaled;
108141
}
109142
else
110143
{
111-
m_callback = callback;
144+
m_callbacks.push_back(callback);
112145
}
113146
} // unlock
114147

@@ -122,6 +155,8 @@ namespace signalr
122155
std::mutex m_lock;
123156
std::condition_variable m_condition;
124157
bool m_signaled;
125-
std::function<void()> m_callback;
158+
std::vector<std::function<void()>> m_callbacks;
126159
};
160+
161+
cancellation_token get_cancellation_token(std::weak_ptr<cancellation_token_source> s);
127162
}

src/signalrclient/connection_impl.cpp

Lines changed: 62 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ namespace signalr
3535
connection_impl::connection_impl(const std::string& url, trace_level trace_level, const std::shared_ptr<log_writer>& log_writer,
3636
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)
3737
: m_base_url(url), m_connection_state(connection_state::disconnected), m_logger(log_writer, trace_level), m_transport(nullptr), m_skip_negotiation(skip_negotiation),
38-
m_message_received([](const std::string&) noexcept {}), m_disconnected([](std::exception_ptr) noexcept {}), m_disconnect_cts(std::make_shared<cancellation_token>())
38+
m_message_received([](const std::string&) noexcept {}), m_disconnected([](std::exception_ptr) noexcept {}), m_disconnect_cts(std::make_shared<cancellation_token_source>())
3939
{
4040
if (http_client_factory != nullptr)
4141
{
@@ -65,7 +65,18 @@ namespace signalr
6565
// Signaling the event is safe here. We are in the dtor so noone is using this instance. There might be some
6666
// outstanding threads that hold on to the connection via a weak pointer but they won't be able to acquire
6767
// the instance since it is being destroyed. Note that the event may actually be in non-signaled state here.
68-
m_start_completed_event.cancel();
68+
try
69+
{
70+
m_start_completed_event.cancel();
71+
}
72+
catch (const std::exception& ex)
73+
{
74+
if (m_logger.is_enabled(trace_level::warning))
75+
{
76+
m_logger.log(trace_level::warning, std::string("start completed event threw an exception in the destructor: ")
77+
.append(ex.what()));
78+
}
79+
}
6980
completion_event completion;
7081
auto logger = m_logger;
7182
shutdown([completion, logger](std::exception_ptr exception) mutable
@@ -194,7 +205,18 @@ namespace signalr
194205

195206
connection->m_transport = nullptr;
196207
connection->change_state(connection_state::disconnected);
197-
connection->m_start_completed_event.cancel();
208+
try
209+
{
210+
connection->m_start_completed_event.cancel();
211+
}
212+
catch (const std::exception& ex)
213+
{
214+
if (connection->m_logger.is_enabled(trace_level::warning))
215+
{
216+
connection->m_logger.log(trace_level::warning, std::string("start completed event threw an exception in start negotiate: ")
217+
.append(ex.what()));
218+
}
219+
}
198220
callback(std::current_exception());
199221
return;
200222
}
@@ -213,7 +235,18 @@ namespace signalr
213235
assert(false);
214236
}
215237

216-
connection->m_start_completed_event.cancel();
238+
try
239+
{
240+
connection->m_start_completed_event.cancel();
241+
}
242+
catch (const std::exception& ex)
243+
{
244+
if (connection->m_logger.is_enabled(trace_level::warning))
245+
{
246+
connection->m_logger.log(trace_level::warning, std::string("start completed event threw an exception in start negotiate: ")
247+
.append(ex.what()));
248+
}
249+
}
217250
callback(nullptr);
218251
};
219252

@@ -328,7 +361,7 @@ namespace signalr
328361
}
329362

330363
connection->start_transport(url, transport_started);
331-
});
364+
}, get_cancellation_token(m_disconnect_cts));
332365
}
333366

334367
void connection_impl::start_transport(const std::string& url, std::function<void(std::shared_ptr<transport>, std::exception_ptr)> transport_started)
@@ -552,7 +585,18 @@ namespace signalr
552585
const auto current_state = get_connection_state();
553586
if (current_state == connection_state::disconnected)
554587
{
555-
m_disconnect_cts->cancel();
588+
try
589+
{
590+
m_disconnect_cts->cancel();
591+
}
592+
catch (const std::exception& ex)
593+
{
594+
if (m_logger.is_enabled(trace_level::warning))
595+
{
596+
m_logger.log(trace_level::warning, std::string("disconnect event threw an exception in shutdown: ")
597+
.append(ex.what()));
598+
}
599+
}
556600
callback(m_stop_error);
557601
return;
558602
}
@@ -567,7 +611,18 @@ namespace signalr
567611
}
568612

569613
// we request a cancellation of the ongoing start (if any) and wait until it is canceled
570-
m_disconnect_cts->cancel();
614+
try
615+
{
616+
m_disconnect_cts->cancel();
617+
}
618+
catch (const std::exception& ex)
619+
{
620+
if (m_logger.is_enabled(trace_level::warning))
621+
{
622+
m_logger.log(trace_level::warning, std::string("disconnect event threw an exception in shutdown: ")
623+
.append(ex.what()));
624+
}
625+
}
571626

572627
while (m_start_completed_event.wait(60000) != 0)
573628
{

0 commit comments

Comments
 (0)