Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove pplx::task from public API #8747

Merged
merged 9 commits into from
Mar 30, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 3 additions & 4 deletions src/SignalR/clients/cpp/include/signalrclient/connection.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
#include "_exports.h"
#include <memory>
#include <functional>
#include "pplx/pplxtasks.h"
#include "connection_state.h"
#include "trace_level.h"
#include "log_writer.h"
Expand All @@ -29,16 +28,16 @@ namespace signalr

connection& operator=(const connection&) = delete;

SIGNALRCLIENT_API pplx::task<void> __cdecl start();
SIGNALRCLIENT_API void __cdecl start(std::function<void(std::exception_ptr)> callback) noexcept;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can these callbacks be marked noexcept too? std::function<void(std::exception_ptr) noexcept>

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought about it, but I don't think it works
https://godbolt.org/z/dI2GuJ


SIGNALRCLIENT_API pplx::task<void> __cdecl send(const std::string& data);
SIGNALRCLIENT_API void __cdecl send(const std::string& data, std::function<void(std::exception_ptr)> callback) noexcept;

SIGNALRCLIENT_API void __cdecl set_message_received(const message_received_handler& message_received_callback);
SIGNALRCLIENT_API void __cdecl set_disconnected(const std::function<void __cdecl()>& disconnected_callback);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it make sense to have set_disconnected's completion callback take an exception as well?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably, I'll track that in our public API review issue #8717


SIGNALRCLIENT_API void __cdecl set_client_config(const signalr_client_config& config);

SIGNALRCLIENT_API pplx::task<void> __cdecl stop();
SIGNALRCLIENT_API void __cdecl stop(std::function<void(std::exception_ptr)> callback) noexcept;

SIGNALRCLIENT_API connection_state __cdecl get_connection_state() const noexcept;
SIGNALRCLIENT_API std::string __cdecl get_connection_id() const;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
#include "_exports.h"
#include <memory>
#include <functional>
#include "pplx/pplxtasks.h"
#include "cpprest/json.h"
#include "connection_state.h"
#include "trace_level.h"
Expand All @@ -31,8 +30,8 @@ namespace signalr

hub_connection& operator=(const hub_connection&) = delete;

SIGNALRCLIENT_API pplx::task<void> __cdecl start();
SIGNALRCLIENT_API pplx::task<void> __cdecl stop();
SIGNALRCLIENT_API void __cdecl start(std::function<void(std::exception_ptr)> callback) noexcept;
SIGNALRCLIENT_API void __cdecl stop(std::function<void(std::exception_ptr)> callback) noexcept;

SIGNALRCLIENT_API connection_state __cdecl get_connection_state() const;
SIGNALRCLIENT_API std::string __cdecl get_connection_id() const;
Expand All @@ -43,9 +42,9 @@ namespace signalr

SIGNALRCLIENT_API void __cdecl on(const std::string& event_name, const method_invoked_handler& handler);

SIGNALRCLIENT_API pplx::task<web::json::value> invoke(const std::string& method_name, const web::json::value& arguments = web::json::value::array());
SIGNALRCLIENT_API void invoke(const std::string& method_name, const web::json::value& arguments = web::json::value::array(), std::function<void(const web::json::value&, std::exception_ptr)> callback = [](const web::json::value&, std::exception_ptr) {}) noexcept;

SIGNALRCLIENT_API pplx::task<void> send(const std::string& method_name, const web::json::value& arguments = web::json::value::array());
SIGNALRCLIENT_API void send(const std::string& method_name, const web::json::value& arguments = web::json::value::array(), std::function<void(std::exception_ptr)> callback = [](std::exception_ptr) {}) noexcept;

private:
std::shared_ptr<hub_connection_impl> m_pImpl;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include <sstream>
#include "hub_connection.h"
#include "log_writer.h"
#include <future>

class logger : public signalr::log_writer
{
Expand All @@ -23,13 +24,16 @@ void send_message(signalr::hub_connection& connection, const std::string& messag
args[0] = web::json::value(utility::conversions::to_string_t(message));

// if you get an internal compiler error uncomment the lambda below or install VS Update 4
connection.invoke("Send", args)
.then([](pplx::task<web::json::value> invoke_task) // fire and forget but we need to observe exceptions
connection.invoke("Send", args, [](const web::json::value& value, std::exception_ptr exception)
{
try
{
auto val = invoke_task.get();
ucout << U("Received: ") << val.serialize() << std::endl;
if (exception)
{
std::rethrow_exception(exception);
}

ucout << U("Received: ") << value.serialize() << std::endl;
}
catch (const std::exception &e)
{
Expand All @@ -41,44 +45,63 @@ void send_message(signalr::hub_connection& connection, const std::string& messag
void chat()
{
signalr::hub_connection connection("http://localhost:5000/default", signalr::trace_level::all, std::make_shared<logger>());
connection.on("Send", [](const web::json::value& m)
connection.on("Send", [](const web::json::value & m)
{
ucout << std::endl << m.at(0).as_string() << /*U(" wrote:") << m.at(1).as_string() <<*/ std::endl << U("Enter your message: ");
});

connection.start()
.then([&connection]()
std::promise<void> task;
connection.start([&connection, &task](std::exception_ptr exception)
{
if (exception)
{
ucout << U("Enter your message:");
for (;;)
try
{
BrennanConroy marked this conversation as resolved.
Show resolved Hide resolved
std::rethrow_exception(exception);
}
catch (const std::exception & ex)
{
std::string message;
std::getline(std::cin, message);
ucout << U("exception when starting connection: ") << ex.what() << std::endl;
}
task.set_value();
return;
}

if (message == ":q")
{
break;
}
ucout << U("Enter your message:");
for (;;)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

while true lol

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 for for (;;) lol

{
std::string message;
std::getline(std::cin, message);

send_message(connection, message);
if (message == ":q")
{
break;
}
})
.then([&connection]() // fine to capture by reference - we are blocking so it is guaranteed to be valid
{
return connection.stop();
})
.then([](pplx::task<void> stop_task)

send_message(connection, message);
}

connection.stop([&task](std::exception_ptr exception)
{
try
{
stop_task.get();
if (exception)
{
std::rethrow_exception(exception);
}

ucout << U("connection stopped successfully") << std::endl;
}
catch (const std::exception &e)
catch (const std::exception & e)
{
ucout << U("exception when starting or stopping connection: ") << e.what() << std::endl;
ucout << U("exception when stopping connection: ") << e.what() << std::endl;
}
}).get();

task.set_value();
BrennanConroy marked this conversation as resolved.
Show resolved Hide resolved
});
});

task.get_future().get();
}

int main()
Expand Down
12 changes: 6 additions & 6 deletions src/SignalR/clients/cpp/src/signalrclient/connection.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,14 @@ namespace signalr
// undefinded behavior since we are using an incomplete type. More details here: http://herbsutter.com/gotw/_100/
connection::~connection() = default;

pplx::task<void> connection::start()
void connection::start(std::function<void(std::exception_ptr)> callback) noexcept
{
return m_pImpl->start();
m_pImpl->start(callback);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you need checks for the implementation object here as well?

}

pplx::task<void> connection::send(const std::string& data)
void connection::send(const std::string& data, std::function<void(std::exception_ptr)> callback) noexcept
{
return m_pImpl->send(data);
m_pImpl->send(data, callback);
}

void connection::set_message_received(const message_received_handler& message_received_callback)
Expand All @@ -41,9 +41,9 @@ namespace signalr
m_pImpl->set_client_config(config);
}

pplx::task<void> connection::stop()
void connection::stop(std::function<void(std::exception_ptr)> callback) noexcept
{
return m_pImpl->stop();
m_pImpl->stop(callback);
}

connection_state connection::get_connection_state() const noexcept
Expand Down
54 changes: 38 additions & 16 deletions src/SignalR/clients/cpp/src/signalrclient/connection_impl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -74,14 +74,14 @@ namespace signalr
change_state(connection_state::disconnected);
}

pplx::task<void> connection_impl::start()
void connection_impl::start(std::function<void(std::exception_ptr)> callback) noexcept
{
{
std::lock_guard<std::mutex> lock(m_stop_lock);
if (!change_state(connection_state::disconnected, connection_state::connecting))
{
return pplx::task_from_exception<void>(
signalr_exception("cannot start a connection that is not in the disconnected state"));
callback(std::make_exception_ptr(signalr_exception("cannot start a connection that is not in the disconnected state")));
return;
}

// there should not be any active transport at this point
Expand All @@ -92,7 +92,19 @@ namespace signalr
m_connection_id = "";
}

return start_negotiate(m_base_url, 0);
start_negotiate(m_base_url, 0)
.then([callback](pplx::task<void> prev_task)
{
try
{
prev_task.get();
callback(nullptr);
}
catch (...)
{
callback(std::current_exception());
}
});
}

pplx::task<void> connection_impl::start_negotiate(const std::string& url, int redirect_count)
Expand Down Expand Up @@ -362,7 +374,7 @@ namespace signalr
}
}

pplx::task<void> connection_impl::send(const std::string& data)
void connection_impl::send(const std::string& data, std::function<void(std::exception_ptr)> callback) noexcept
{
// To prevent an (unlikely) condition where the transport is nulled out after we checked the connection_state
// and before sending data we store the pointer in the local variable. In this case `send()` will throw but
Expand All @@ -372,25 +384,25 @@ namespace signalr
const auto connection_state = get_connection_state();
if (connection_state != signalr::connection_state::connected || !transport)
{
return pplx::task_from_exception<void>(signalr_exception(
callback(std::make_exception_ptr(signalr_exception(
std::string("cannot send data when the connection is not in the connected state. current connection state: ")
.append(translate_connection_state(connection_state))));
.append(translate_connection_state(connection_state)))));
return;
}

auto logger = m_logger;

logger.log(trace_level::info, std::string("sending data: ").append(data));

pplx::task_completion_event<void> event;
transport->send(data, [logger, event](std::exception_ptr exception)
transport->send(data, [logger, callback](std::exception_ptr exception)
mutable {
try
{
if (exception != nullptr)
{
std::rethrow_exception(exception);
}
event.set();
callback(nullptr);
}
catch (const std::exception &e)
{
Expand All @@ -399,21 +411,29 @@ namespace signalr
std::string("error sending data: ")
.append(e.what()));

event.set_exception(exception);
callback(exception);
}
});

return pplx::create_task(event);
}

pplx::task<void> connection_impl::stop()
void connection_impl::stop(std::function<void(std::exception_ptr)> callback) noexcept
{
m_logger.log(trace_level::info, "stopping connection");

auto connection = shared_from_this();
return shutdown()
.then([connection]()
shutdown()
.then([connection, callback](pplx::task<void> prev_task)
{
try
{
prev_task.get();
}
catch (...)
{
callback(std::current_exception());
return;
}

{
// the lock prevents a race where the user calls `stop` on a disconnected connection and calls `start`
// on a different thread at the same time. In this case we must not null out the transport if we are
Expand Down Expand Up @@ -443,6 +463,8 @@ namespace signalr
trace_level::errors,
std::string("disconnected callback threw an unknown exception"));
}

callback(nullptr);
});
}

Expand Down
7 changes: 3 additions & 4 deletions src/SignalR/clients/cpp/src/signalrclient/connection_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@

#include <atomic>
#include <mutex>
#include "cpprest/http_client.h"
#include "signalrclient/http_client.h"
#include "signalrclient/trace_level.h"
#include "signalrclient/connection_state.h"
Expand Down Expand Up @@ -37,9 +36,9 @@ namespace signalr

~connection_impl();

pplx::task<void> start();
pplx::task<void> send(const std::string &data);
pplx::task<void> stop();
void start(std::function<void(std::exception_ptr)> callback) noexcept;
void send(const std::string &data, std::function<void(std::exception_ptr)> callback) noexcept;
void stop(std::function<void(std::exception_ptr)> callback) noexcept;

connection_state get_connection_state() const noexcept;
std::string get_connection_id() const noexcept;
Expand Down
Loading