Skip to content

Commit

Permalink
doc: Add multi-threaded server example that uses single GrpcContext
Browse files Browse the repository at this point in the history
  • Loading branch information
Tradias committed Sep 8, 2024
1 parent d01bbde commit ce77d98
Show file tree
Hide file tree
Showing 8 changed files with 119 additions and 8 deletions.
3 changes: 3 additions & 0 deletions example/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,9 @@ if(ASIO_GRPC_BOOST_ASIO_HAS_CO_AWAIT AND ASIO_GRPC_ENABLE_CPP20_TESTS_AND_EXAMPL
asio_grpc_add_example(multi-threaded-server "20")
target_link_libraries(asio-grpc-example-multi-threaded-server PRIVATE asio-grpc::asio-grpc)

asio_grpc_add_example(multi-threaded-alternative-server "20")
target_link_libraries(asio-grpc-example-multi-threaded-alternative-server PRIVATE asio-grpc::asio-grpc)

if(ASIO_GRPC_ENABLE_IO_URING_EXAMPLES OR "${CMAKE_SYSTEM_NAME}" STREQUAL "Windows")
asio_grpc_add_example(file-transfer-client "20")
target_link_libraries(asio-grpc-example-file-transfer-client
Expand Down
12 changes: 11 additions & 1 deletion example/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -181,12 +181,22 @@ Click on `snippet source` to jump to the code of an individual example.
<a id='snippet-server-side-multi-threaded'></a>
```cpp
// ---------------------------------------------------
// Multi-threaded server performing 20 unary requests using callback API
// Multi-threaded server handling unary requests using callback API and multiple GrpcContexts
// ---------------------------------------------------
```
<sup><a href='/example/multi-threaded-server.cpp#L32-L36' title='Snippet source file'>snippet source</a> | <a href='#snippet-server-side-multi-threaded' title='Start of snippet'>anchor</a></sup>
<!-- endSnippet -->

<!-- snippet: server-side-multi-threaded-alternative -->
<a id='snippet-server-side-multi-threaded-alternative'></a>
```cpp
// ---------------------------------------------------
// Multi-threaded server handling unary requests using callback API and single GrpcContext
// ---------------------------------------------------
```
<sup><a href='/example/multi-threaded-alternative-server.cpp#L30-L34' title='Snippet source file'>snippet source</a> | <a href='#snippet-server-side-multi-threaded-alternative' title='Start of snippet'>anchor</a></sup>
<!-- endSnippet -->

### Generic

<!-- snippet: server-side-generic-unary-request -->
Expand Down
6 changes: 4 additions & 2 deletions example/helper/server_shutdown_asio.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ struct ServerShutdown
if (!is_shutdown.exchange(true))
{
// This will cause all coroutines to run to completion normally
// while returning `false` from RPC related steps. Also cancels the signals
// while returning `false` from rpc related steps. Also cancels the signals
// so that the GrpcContext will eventually run out of work and return
// from `run()`.
// We cannot call server.Shutdown() on the same thread that runs a GrpcContext because that can lead to a
Expand All @@ -61,7 +61,9 @@ struct ServerShutdown
[&]
{
signals.cancel();
// For Shutdown to ever complete some other thread must be calling grpc_context.run().
// Shutdown will wait for all outstanding rpcs to complete normally. Alternatively use Shutdown with
// deadline to cancel them after a certain time. For Shutdown to ever complete some other thread
// must be calling grpc_context.run().
server.Shutdown();
});
}
Expand Down
94 changes: 94 additions & 0 deletions example/multi-threaded-alternative-server.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
// Copyright 2024 Dennis Hezel
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include "helloworld/helloworld.grpc.pb.h"
#include "rethrow_first_arg.hpp"
#include "server_shutdown_asio.hpp"

#include <agrpc/asio_grpc.hpp>
#include <boost/asio/detached.hpp>
#include <grpcpp/server.h>
#include <grpcpp/server_builder.h>

#include <memory>
#include <thread>
#include <vector>

namespace asio = boost::asio;

// begin-snippet: server-side-multi-threaded-alternative
// ---------------------------------------------------
// Multi-threaded server handling unary requests using callback API and single GrpcContext
// ---------------------------------------------------
// end-snippet

void register_request_handler(agrpc::GrpcContext& grpc_context, helloworld::Greeter::AsyncService& service,
example::ServerShutdown& shutdown)
{
using RPC = agrpc::ServerRPC<&helloworld::Greeter::AsyncService::RequestSayHello>;
agrpc::register_callback_rpc_handler<RPC>(
grpc_context, service,
[&](RPC::Ptr ptr, helloworld::HelloRequest& request)
{
helloworld::HelloReply response;
response.set_message("Hello " + request.name());
auto& rpc = *ptr;
rpc.finish(response, grpc::Status::OK,
[&, p = std::move(ptr)](bool)
{
// In this example we shut down the server after 20 requests
static std::atomic_int counter{};
if (19 == counter.fetch_add(1))
{
shutdown.shutdown();
}
});
},
example::RethrowFirstArg{});
}

int main(int argc, const char** argv)
{
const auto port = argc >= 2 ? argv[1] : "50051";
const auto host = std::string("0.0.0.0:") + port;
const auto thread_count = std::thread::hardware_concurrency();

helloworld::Greeter::AsyncService service;
std::unique_ptr<grpc::Server> server;

grpc::ServerBuilder builder;
agrpc::GrpcContext grpc_context(builder.AddCompletionQueue(), thread_count);
builder.AddListeningPort(host, grpc::InsecureServerCredentials());
builder.RegisterService(&service);
server = builder.BuildAndStart();

example::ServerShutdown shutdown{*server, grpc_context};

// Create one thread per GrpcContext.
std::vector<std::thread> threads;
for (size_t i = 0; i < thread_count; ++i)
{
threads.emplace_back(
[&]
{
register_request_handler(grpc_context, service, shutdown);
grpc_context.run();
});
}

for (auto& thread : threads)
{
thread.join();
}
}
2 changes: 1 addition & 1 deletion example/multi-threaded-server.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ namespace asio = boost::asio;

// begin-snippet: server-side-multi-threaded
// ---------------------------------------------------
// Multi-threaded server performing 20 unary requests using callback API
// Multi-threaded server handling unary requests using callback API and multiple GrpcContexts
// ---------------------------------------------------
// end-snippet

Expand Down
2 changes: 1 addition & 1 deletion src/agrpc/health_check_service.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ grpc::ServerBuilder& add_health_check_service(grpc::ServerBuilder& builder);
* `agrpc::add_health_check_service()`. May only be called once for a given HealthCheckService.
*
* Does not contribute to the work tracking of the GrpcContext. May not be called concurrently with
* `GrpcContext::run/poll`.
* `GrpcContext::run/poll`. May not be used with a multi-threaded GrpcContext.
*
* @note When using `GrpcContext::run/poll_completion_queue` then none of the member functions of the service may be
* used.
Expand Down
6 changes: 4 additions & 2 deletions test/example/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,9 @@ add_dependencies(
asio-grpc-example-generic-client
asio-grpc-example-generic-server
asio-grpc-example-multi-threaded-client
asio-grpc-example-multi-threaded-alternative-client
asio-grpc-example-multi-threaded-server
asio-grpc-example-multi-threaded-alternative-client
asio-grpc-example-multi-threaded-alternative-server
asio-grpc-example-unifex-client
asio-grpc-example-unifex-server)

Expand All @@ -50,8 +51,9 @@ target_compile_definitions(
"ASIO_GRPC_EXAMPLE_GENERIC_CLIENT=\"$<TARGET_FILE:asio-grpc-example-generic-client>\""
"ASIO_GRPC_EXAMPLE_GENERIC_SERVER=\"$<TARGET_FILE:asio-grpc-example-generic-server>\""
"ASIO_GRPC_EXAMPLE_MULTI_THREADED_CLIENT=\"$<TARGET_FILE:asio-grpc-example-multi-threaded-client>\""
"ASIO_GRPC_EXAMPLE_MULTI_THREADED_ALTERNATIVE_CLIENT=\"$<TARGET_FILE:asio-grpc-example-multi-threaded-alternative-client>\""
"ASIO_GRPC_EXAMPLE_MULTI_THREADED_SERVER=\"$<TARGET_FILE:asio-grpc-example-multi-threaded-server>\""
"ASIO_GRPC_EXAMPLE_MULTI_THREADED_ALTERNATIVE_CLIENT=\"$<TARGET_FILE:asio-grpc-example-multi-threaded-alternative-client>\""
"ASIO_GRPC_EXAMPLE_MULTI_THREADED_ALTERNATIVE_SERVER=\"$<TARGET_FILE:asio-grpc-example-multi-threaded-alternative-server>\""
"ASIO_GRPC_EXAMPLE_UNIFEX_CLIENT=\"$<TARGET_FILE:asio-grpc-example-unifex-client>\""
"ASIO_GRPC_EXAMPLE_UNIFEX_SERVER=\"$<TARGET_FILE:asio-grpc-example-unifex-server>\"")

Expand Down
2 changes: 1 addition & 1 deletion test/example/test_examples.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ TEST_CASE("examples")
SUBCASE("multi-threaded-alternative")
{
client_program = ASIO_GRPC_EXAMPLE_MULTI_THREADED_ALTERNATIVE_CLIENT;
server_program = ASIO_GRPC_EXAMPLE_MULTI_THREADED_SERVER;
server_program = ASIO_GRPC_EXAMPLE_MULTI_THREADED_ALTERNATIVE_SERVER;
}
boost::process::child server(server_program, args);
REQUIRE(server.valid());
Expand Down

0 comments on commit ce77d98

Please sign in to comment.