├── .clang-format ├── .gitignore ├── .travis.yml ├── BUILD.bazel ├── CMakeLists.txt ├── Dockerfile.cmake.trusty ├── Dockerfile.trusty ├── LICENSE ├── README.md ├── WORKSPACE ├── async_grpc-config.cmake.in ├── async_grpc ├── BUILD.bazel ├── async_client.h ├── client.h ├── client_test.cc ├── common │ ├── blocking_queue.h │ ├── make_unique.h │ ├── mutex.h │ ├── optional.h │ ├── port.h │ ├── time.cc │ └── time.h ├── completion_queue_pool.cc ├── completion_queue_pool.h ├── completion_queue_thread.cc ├── completion_queue_thread.h ├── event_queue_thread.cc ├── event_queue_thread.h ├── execution_context.h ├── opencensus_span.cc ├── opencensus_span.h ├── proto │ └── math_service.proto ├── retry.cc ├── retry.h ├── rpc.cc ├── rpc.h ├── rpc_handler.h ├── rpc_handler_interface.h ├── rpc_service_method_traits.h ├── server.cc ├── server.h ├── server_test.cc ├── service.cc ├── service.h ├── span.h ├── testing │ ├── rpc_handler_test_server.h │ └── rpc_handler_wrapper.h ├── tools │ └── bazel.rc ├── type_traits.h └── type_traits_test.cc ├── bazel ├── BUILD.bazel ├── repositories.bzl └── third_party │ ├── BUILD.bazel │ ├── curl.BUILD │ ├── rapidjson.BUILD │ └── zlib.BUILD ├── cmake ├── functions.cmake └── modules │ └── FindGMock.cmake ├── gfx ├── diagram.png └── logo.png ├── package.xml └── scripts ├── install_async_grpc_bazel.sh ├── install_async_grpc_cmake.sh ├── install_debs_bazel.sh ├── install_debs_cmake.sh ├── install_grpc.sh ├── install_proto3.sh ├── load_docker_cache.sh └── save_docker_cache.sh /.clang-format: -------------------------------------------------------------------------------- 1 | BasedOnStyle: Google 2 | DerivePointerAlignment: false 3 | PointerAlignment: Left 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode 2 | bazel-* -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | # Copyright 2018 The Cartographer Authors 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | sudo: required 16 | services: docker 17 | 18 | # Cache intermediate Docker layers. For a description of how this works, see: 19 | # https://giorgos.sealabs.net/docker-cache-on-travis-and-docker-112.html 20 | cache: 21 | directories: 22 | - /home/travis/docker/ 23 | 24 | env: 25 | - LSB_RELEASE=trusty DOCKER_CACHE_FILE=/home/travis/docker/trusty-cache.tar.gz 26 | - LSB_RELEASE=cmake.trusty DOCKER_CACHE_FILE=/home/travis/docker/cmake.trusty-cache.tar.gz 27 | 28 | before_install: scripts/load_docker_cache.sh 29 | 30 | install: true 31 | script: 32 | - docker build ${TRAVIS_BUILD_DIR} -t async_grpc:${LSB_RELEASE} -f Dockerfile.${LSB_RELEASE} 33 | - scripts/save_docker_cache.sh 34 | -------------------------------------------------------------------------------- /BUILD.bazel: -------------------------------------------------------------------------------- 1 | # Copyright 2018 The Cartographer Authors 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | # Top-level proto and C++ targets for Cartographer's gRPC server. 16 | 17 | licenses(["notice"]) # Apache 2.0 18 | 19 | package(default_visibility = ["//visibility:public"]) 20 | 21 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright 2018 The Cartographer Authors 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | cmake_minimum_required(VERSION 2.8.12) # Ships with Ubuntu 14.04 (Trusty) 16 | 17 | project(async_grpc) 18 | 19 | set(ASYNC_GRPC_MAJOR_VERSION 0) 20 | set(ASYNC_GRPC_MINOR_VERSION 1) 21 | set(ASYNC_GRPC_PATCH_VERSION 0) 22 | set(ASYNC_GRPC_VERSION ${ASYNC_GRPC_MAJOR_VERSION}.${ASYNC_GRPC_MINOR_VERSION}.${ASYNC_GRPC_PATCH_VERSION}) 23 | set(ASYNC_GRPC_SOVERSION ${ASYNC_GRPC_MAJOR_VERSION}.${ASYNC_GRPC_MINOR_VERSION}) 24 | 25 | include("${PROJECT_SOURCE_DIR}/cmake/functions.cmake") 26 | google_initialize_async_grpc_project() 27 | enable_testing() 28 | find_package(GMock REQUIRED) 29 | find_package(Protobuf 3.0.0 REQUIRED) 30 | 31 | set(ALL_LIBRARY_HDRS 32 | async_grpc/async_client.h 33 | async_grpc/client.h 34 | async_grpc/common/blocking_queue.h 35 | async_grpc/common/make_unique.h 36 | async_grpc/common/mutex.h 37 | async_grpc/common/optional.h 38 | async_grpc/common/port.h 39 | async_grpc/common/time.h 40 | async_grpc/completion_queue_pool.h 41 | async_grpc/completion_queue_thread.h 42 | async_grpc/event_queue_thread.h 43 | async_grpc/execution_context.h 44 | async_grpc/retry.h 45 | async_grpc/rpc.h 46 | async_grpc/rpc_handler_interface.h 47 | async_grpc/rpc_handler.h 48 | async_grpc/rpc_service_method_traits.h 49 | async_grpc/server.h 50 | async_grpc/service.h 51 | async_grpc/span.h 52 | async_grpc/testing/rpc_handler_test_server.h 53 | async_grpc/testing/rpc_handler_wrapper.h 54 | async_grpc/type_traits.h) 55 | 56 | set(ALL_LIBRARY_SRCS 57 | async_grpc/common/time.cc 58 | async_grpc/completion_queue_pool.cc 59 | async_grpc/completion_queue_thread.cc 60 | async_grpc/event_queue_thread.cc 61 | async_grpc/retry.cc 62 | async_grpc/rpc.cc 63 | async_grpc/server.cc 64 | async_grpc/service.cc) 65 | 66 | set(ALL_TESTS 67 | async_grpc/client_test.cc 68 | async_grpc/server_test.cc 69 | async_grpc/type_traits_test.cc) 70 | 71 | set(ALL_PROTOS 72 | async_grpc/proto/math_service.proto) 73 | 74 | set(ALL_PROTO_SRCS) 75 | set(ALL_PROTO_HDRS) 76 | foreach(RELATIVEPATH ${ALL_PROTOS}) 77 | get_filename_component(DIR ${RELATIVEPATH} DIRECTORY) 78 | get_filename_component(FILENAME ${RELATIVEPATH} NAME_WE) 79 | 80 | list(APPEND ALL_PROTO_SRCS "${PROJECT_BINARY_DIR}/${DIR}/${FILENAME}.pb.cc") 81 | list(APPEND ALL_PROTO_HDRS "${PROJECT_BINARY_DIR}/${DIR}/${FILENAME}.pb.h") 82 | 83 | add_custom_command( 84 | OUTPUT "${PROJECT_BINARY_DIR}/${DIR}/${FILENAME}.pb.cc" 85 | "${PROJECT_BINARY_DIR}/${DIR}/${FILENAME}.pb.h" 86 | COMMAND ${PROTOBUF_PROTOC_EXECUTABLE} 87 | ARGS --cpp_out ${PROJECT_BINARY_DIR} -I 88 | ${PROJECT_SOURCE_DIR} ${PROJECT_SOURCE_DIR}/${RELATIVEPATH} 89 | DEPENDS ${RELATIVEPATH} 90 | COMMENT "Running C++ protocol buffer compiler on ${RELATIVEPATH}" 91 | VERBATIM 92 | ) 93 | endforeach() 94 | set_source_files_properties(${ALL_PROTO_SRCS} ${ALL_PROTO_HDRS} PROPERTIES GENERATED TRUE) 95 | 96 | add_library(${PROJECT_NAME} ${ALL_LIBRARY_HDRS} ${ALL_LIBRARY_SRCS} ${ALL_PROTO_HDRS} ${ALL_PROTO_SRCS}) 97 | 98 | foreach(RELATIVEPATH ${ALL_TESTS}) 99 | get_filename_component(DIR ${RELATIVEPATH} DIRECTORY) 100 | get_filename_component(FILENAME ${RELATIVEPATH} NAME_WE) 101 | # Replace slashes as required for CMP0037. 102 | string(REPLACE "/" "." TEST_TARGET_NAME "${DIR}/${FILENAME}") 103 | google_test("${TEST_TARGET_NAME}" ${RELATIVEPATH}) 104 | target_link_libraries(${TEST_TARGET_NAME} PUBLIC ${GMOCK_LIBRARY}) 105 | target_link_libraries("${TEST_TARGET_NAME}" PUBLIC grpc++) 106 | endforeach() 107 | 108 | target_link_libraries(${PROJECT_NAME} PUBLIC glog) 109 | target_link_libraries(${PROJECT_NAME} PUBLIC gflags) 110 | 111 | target_include_directories(${PROJECT_NAME} SYSTEM PUBLIC 112 | ${PROTOBUF_INCLUDE_DIR}) 113 | # TODO(hrapp): This should not explicitly list pthread and use 114 | # PROTOBUF_LIBRARIES, but that failed on first try. 115 | target_link_libraries(${PROJECT_NAME} PUBLIC ${PROTOBUF_LIBRARY} pthread) 116 | 117 | # TODO(cschuet): Write FindGRPC.cmake 118 | target_link_libraries(${PROJECT_NAME} PUBLIC grpc++) 119 | 120 | target_include_directories(${PROJECT_NAME} PUBLIC 121 | $ 122 | $ 123 | $ 124 | ) 125 | 126 | target_include_directories(${PROJECT_NAME} SYSTEM PRIVATE 127 | "${GMOCK_INCLUDE_DIRS}") 128 | 129 | set(TARGET_COMPILE_FLAGS "${TARGET_COMPILE_FLAGS} ${GOOG_CXX_FLAGS}") 130 | set_target_properties(${PROJECT_NAME} PROPERTIES 131 | COMPILE_FLAGS ${TARGET_COMPILE_FLAGS}) 132 | 133 | install( 134 | TARGETS ${PROJECT_NAME} 135 | EXPORT AsyncGrpcExport 136 | ARCHIVE DESTINATION lib 137 | LIBRARY DESTINATION lib 138 | RUNTIME DESTINATION bin 139 | ) 140 | 141 | foreach(HDR ${ALL_LIBRARY_HDRS}) 142 | get_filename_component(DIR ${HDR} DIRECTORY) 143 | install( 144 | FILES ${HDR} 145 | DESTINATION include/${DIR} 146 | ) 147 | endforeach() 148 | 149 | set(ASYNC_GRPC_CMAKE_DIR share/async_grpc/cmake) 150 | include(CMakePackageConfigHelpers) 151 | configure_package_config_file( 152 | async_grpc-config.cmake.in 153 | ${PROJECT_BINARY_DIR}/cmake/async_grpc/async_grpc-config.cmake 154 | PATH_VARS ASYNC_GRPC_CMAKE_DIR 155 | INSTALL_DESTINATION ${CMAKE_INSTALL_PREFIX}/share/async_grpc 156 | ) 157 | 158 | install( 159 | EXPORT AsyncGrpcExport 160 | DESTINATION share/async_grpc/cmake/ 161 | FILE AsyncGrpcTargets.cmake 162 | ) 163 | 164 | install( 165 | FILES ${PROJECT_BINARY_DIR}/cmake/async_grpc/async_grpc-config.cmake 166 | DESTINATION share/async_grpc/ 167 | ) 168 | -------------------------------------------------------------------------------- /Dockerfile.cmake.trusty: -------------------------------------------------------------------------------- 1 | # Copyright 2018 The Cartographer Authors 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | FROM ubuntu:trusty 16 | 17 | # Install debs for cmake build 18 | COPY scripts/install_debs_cmake.sh scripts/ 19 | RUN scripts/install_debs_cmake.sh && rm -rf /var/lib/apt/lists/* 20 | 21 | # Install proto3 22 | COPY scripts/install_proto3.sh scripts/ 23 | RUN scripts/install_proto3.sh && rm -rf protobuf 24 | 25 | # Install gRPC 26 | COPY scripts/install_grpc.sh scripts/ 27 | RUN scripts/install_grpc.sh && rm -rf grpc 28 | 29 | # Compile and install 30 | COPY scripts/install_async_grpc_cmake.sh scripts/ 31 | COPY . async_grpc 32 | RUN scripts/install_async_grpc_cmake.sh && rm -rf async_grpc 33 | -------------------------------------------------------------------------------- /Dockerfile.trusty: -------------------------------------------------------------------------------- 1 | # Copyright 2018 The Cartographer Authors 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | FROM ubuntu:trusty 16 | COPY scripts/install_debs_bazel.sh scripts/ 17 | RUN scripts/install_debs_bazel.sh && rm -rf /var/lib/apt/lists/* 18 | COPY scripts/install_async_grpc_bazel.sh scripts/ 19 | COPY . async_grpc 20 | RUN scripts/install_async_grpc_bazel.sh && rm -rf async_grpc 21 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 | 3 | # Tiny C++ gRPC Server Framework 4 | 5 | ## Objective 6 | 7 | 8 | This repository contains a C++ gRPC framework for building asynchronous, multi-threaded gRPC servers. 9 | The framework is simple to use and supports arbitrary RPC types, i.e. all combinations of streaming and unary requests and responses. 10 | 11 | ## Motivation 12 | 13 | 14 | [gRPC](https://grpc.io/docs/guides/) is Google's open source RPC system which relies on [Protobuf](https://developers.google.com/protocol-buffers/) as a message serialization format. 15 | 16 | gRPC makes it convenient through its protobuf compiler plugin to implement _synchronous_ servers. 17 | For Cartographer, sensor data (IMU, odometry, fixed-frame poses) and intermediate local SLAM results need to be continuously streamed to the cloud Cartographer instance. 18 | Strict time ordering is only required for sensor data of the same type. 19 | Therefore it is natural to have up to 4 client-streaming RPC connections concurrently open per mapping client. 20 | With a server handling requests synchronously even for small robot fleets this would quickly result in unnaturally many threads. 21 | 22 | A good way to solve this problem is to handle RPC connections asynchronously on the server where a fixed number of worker threads processes RPC event queues. 23 | Fortunately gRPC offers a way to asynchronously schedule RPC actions, e.g. RequestAsyncRead(), and also an event queue mechanism to wait for the completion of these events, e.g. ::grpc::CompletionQueue. 24 | 25 | So we built a small server framework on top of the existing gRPC async mechanisms to make it convenient to write asynchronous gRPC servers. 26 | 27 | ## Usage 28 | 29 | ### Example: Server offering unary RPC method “GetSquare()” 30 | 31 | We start by defining the gRPC service as a proto service definition: 32 | 33 | ```proto 34 | syntax = "proto3"; 35 | 36 | package proto; 37 | 38 | message GetSquareRequest { 39 | int32 input = 1; 40 | } 41 | 42 | message GetSquareResponse { 43 | int32 output = 1; 44 | } 45 | 46 | service Math { 47 | rpc GetSquare(GetSquareRequest) returns (GetSquareResponse); 48 | } 49 | ``` 50 | 51 | Note that the above defines an RPC that expects a unary request and generates a unary response from the server. 52 | For streaming RPCs the request or response type would have been prepended with a `stream` annotation (see next example). 53 | 54 | Next we define an implementation of the business logic that consumes the request and generates the response, i.e. the RPC handler. 55 | 56 | ```C++ 57 | DEFINE_HANDLER_SIGNATURE( 58 | GetSquareSignature, proto::GetSquareRequest, proto::GetSquareResponse, 59 | "/proto.Math/GetSquare") 60 | 61 | class GetSquareHandler : public RpcHandler { 62 | public: 63 | void OnRequest(const proto::GetSquareRequest& request) override { 64 | auto response = 65 | cartographer::common::make_unique(); 66 | response->set_output(request.input() * request.input()); 67 | Send(std::move(response)); 68 | } 69 | }; 70 | ``` 71 | 72 | When a client establishes a connection to the server to call the `GetSquare` method, a new instance of a `GetSquareHandler` is instantiated. 73 | The lifetime of each handler is therefore request scoped. 74 | The `OnRequest()` method is invoked with the deserialized client request and the handler now has a chance to react to the request. 75 | In this case we perform the calculation, populate a `GetSquareResponse` proto and pass it to the `Send()` method to be shipped to the client. 76 | Note that for unary response RPCs calling `Send()` finishes the connection with `::grpc::Status::Ok`. 77 | Note that we could have decided to call also call e.g. Finish(`PERMISSION_DENIED`) to error out instead of providing a response. 78 | 79 | Next we set up the server and register our message handler. 80 | 81 | ```C++ 82 | Server::Builder server_builder; 83 | server_builder.SetServerAddress("localhost:50051"); 84 | server_builder.SetNumGrpcThreads(2); 85 | server_builder.SetNumEventThreads(2); 86 | server_builder.RegisterHandler(); 87 | auto server = server_builder.Build(); 88 | server.Start(); 89 | server.WaitForShutdown(); 90 | ``` 91 | 92 | The server address sets the interfaces and port on which the server will offer the gRPC service. 93 | Number of gRPC threads specifies the number of threads the server will use for running the network stack and performing proto serialization / deserialization. 94 | Number of event threads determines the number of threads running the business logic, i.e. the `GetSquareHandler` implemented above. 95 | 96 | Note that there is no guarantee on which thread the handler will be created, but once the handler has been created it will always executed for the lifetime of the exchange on the same thread. 97 | There is therfore no requirement for the `GetSquareHandler` to be thread-safe. 98 | 99 | ## Overview 100 | 101 | 102 |
103 | 104 |
Figure 1: gRPC framework architecture overview.
105 | 106 | The above class diagram provides an overview of the most important concepts in the framework. 107 | Let's look at these in turn. 108 | 109 | 110 | ### Server 111 | 112 | 113 | The `Server` class owns all server-wide resources, such as the gRPC completion queues, all event queues and the threads that process these queues (see [Threading Model](#threading-model) section below for more details). 114 | In addition to that the server may own an implementation of `ExecutionContext` which provides shared state that can be accessed by `RpcHandlers` in a thread-safe manner (see [here](#example-sharing-resources-across-handler-invocations) for example). 115 | 116 | The `Server` also maintains a mapping from (service name, method name) to `RpcHandler` implementations. When the server is started it creates the `Service` objects as needed for the registered methods and passes the mappings to the `RpcHandler`s to the `Service`s so that they can instantiate the appropriate handlers when requests are issued against their methods. 117 | 118 | `Server`s are set up using a builder pattern. A `ServerBuilder` is used to specify server properties such as the serving network address or the number of threads to use for completion and event queues. 119 | 120 | 121 | #### Threading Model 122 | 123 | 124 | The gRPC framework relies on two sets of threads for execution. 125 | There are a configurable number of gRPC completion queue threads. 126 | These threads carry out any network activity through libgrpc and also perform protobuf serialization / deserialization. 127 | 128 | The event threads actually run the business logic, i.e. they execute the `RpcHandler`s. 129 | Note that the framework guarantees that for a given RPC method invocation all events are always processed by the same completion queue and the same event queue. 130 | For a given `Rpc` object everything looks single-threaded. 131 | 132 | Most events originate in libgrpc by an `Rpc` object requesting an asynchronous operation such as `::grpc::Service::RequestAsyncClientStreaming`. 133 | These events eventually show up on their gRPC completion queue and are processed by a completion queue thread. 134 | The thread then advances these events to the appropriate event thread, where they are further processed, e.g. the appropriate handler is invoked to perform some action in response to the event. 135 | 136 | ### Service 137 | 138 | 139 | The `Service` objects represent gRPC services and are actually implementations of `::grpc::Service`. 140 | Their job is to route the events that bubble up from the `Server` to the appropriate `Rpc` objects for processing. 141 | The `Service` therefore maintains a mapping from its RPC method names to the `RpcHandlerInfo` objects to be able to instantiate handlers when requests are issued against its methods. 142 | In addition to that it also manages the lifetime of the `Rpc` objects which represent a client connection for a specific RPC method invocation. 143 | The `ActiveRpcs` object owned by the services is responsible for that. An `EventQueueSelector` is used to select an event queue for incoming requests. 144 | By default we simply assign them in a round-robin fashion. 145 | 146 | 147 | ### Rpc 148 | 149 | 150 | An `Rpc` represents a connection from a particular client for a specific method invocations. 151 | One client invoking two different methods on the same server is therefore represented by two `Rpc` objects. 152 | `N * M` `Rpc` objects are created when the server is started waiting for incoming client connections, where `N` is the number of completion queues and `M` is the total number of methods offered by the server across all services. 153 | When a client invokes a method, the connection is randomly assigned to a completion queue by gRPC. 154 | The `Rpc` object responsible for the method on that particular completion queue is cloned and the new `Rpc` object starts listening for new connections while the old one takes care of any interaction with the client and dies when the method invocation is finished by either client or server. 155 | 156 | ### RpcHandlerInterface 157 | 158 | 159 | `RpcHandlerInterface` is the low-level interface implemented by handlers and used by the `Rpc` object, responsible for the connection to the client, to interact with the handler. 160 | It is not user-facing. Rather users implement the derived template class `RpcHandler`, where `Incoming` specifies the incoming and `Outgoing` the outgoing message type. 161 | In order to allow users to express that the handler is expecting a stream of requests or responses rather than a single one, we use the templated type `Stream` to wrap the raw message type. 162 | A handler implementation of `RpcHandler, B>`expects a stream of `A`-typed requests and replies with a single response of type `B` (see also [examples below](#example-server-offering-streaming-request-rpc-method-"getsum-")). 163 | 164 | 165 | #### Response Generation On Different Thread 166 | 167 | 168 | For server-streaming (`RpcHandler>`) RPCs it can be convenient to perform the response message generation on a thread other than the handler thread. 169 | For Cartographer we had this situation for streaming local SLAM results to the client: the event of a new local SLAM result becoming available occurs on our SLAM thread and not the handler thread. 170 | 171 | So rather than having the user of our framework create a synchronization mechanism between the two threads we built such a mechanism into the framework. 172 | A call to `RpcHandler::GetWriter()` returns a thread-safe `Writer` object that can be used to enqueue messages into the`Rpc` send queue and notifies the handler thread that it should process that queue. 173 | It is always safe to call the `Writer` from any thread even as the connection is being closing (`Writer::write()` will return `false` in that case) as the `Writer` only weakly points to the `Rpc` object. 174 | 175 | ### RpcEvent 176 | 177 | 178 | `RpcEvent`s are used to propagate information from libgrpc to and through our framework. 179 | libgrpc uses tagging of asynchronous operations with `void*` pointers as a way to associate events on the completion queues with the operations that they represent. 180 | We tag our async gRPC ops with pointers to `RpcEvent`s which contain enough metadata for `Server` and `Service` to be able to route them to their appropriate `Rpc` objects, where they are acted upon. 181 | Most `RpcEvent`s first show up in the completion queue when their corresponding network activity completes and are then advanced to the appropriate event queues for handler invocation. 182 | An exception is the `WRITE_NEEDED` event issued by the `Writer` objects mentioned above which only weakly point to their `Rpc` object and are directly injected into the event queue bypassing the completion queue altogether. 183 | 184 | ### Client 185 | 186 | 187 | `Client` is a templated convenience class to make synchronous invocations of RPC methods on remote services simple. 188 | It offers a uniform interface for all RPC types (i.e. unary, cient-streaming, server-streaming, bidi-streaming). 189 | The `Client` is instantiated with the implementation of the `RpcHandlerInterface` of the method to be invoked as the template parameter. 190 | It relies on the `RpcHandlerInterface::method_name() `method to extract the fully qualified gRPC method name. 191 | The `Client` is constructed with a `::grpc::Channel` that is bound to the server hosting the service. 192 | Optionally a `RetryStrategy` can be passed to make the `Client` retry failed invocation attempts. 193 | 194 | 195 | ### RetryStrategy 196 | 197 | 198 | A `RetryStrategy` is passed to a `Client` to retry failed invocation attempts. 199 | Retries are only supported for unary RPCs. 200 | The only `RetryStrategy` implemented at the moment is the `LimitedBackoffStrategy` which takes `min_delay`, `backoff_factor` and `max_attempts` as parameters. 201 | 202 | Example: 203 | 204 | ``` 205 | min_delay = 100ms 206 | backoff_factor = 2 207 | max_attempts = 5 208 | ``` 209 | would retry after 100ms, 200ms, 400ms and 800ms and then give up. 210 | 211 | 212 | ### RpcHandlerTestServer 213 | 214 | 215 | Since all side-effects (that extend beyond the lifetime of an RPC) a`RpcHandler` can cause must happen through the `ExecutionContext`, it is desirable to test handlers by mocking the `ExecutionContext`, throw some gRPC requests at the handler and expect certain call invocations on the `ExecutionContext`. 216 | In practice this is however slightly complicated by the fact that all `RpcHandler` executions are asynchronously. 217 | 218 | The `RpcHandlerTestServer` is a convenient way to test `RpcHandler`s. 219 | It is instantiated with the handler-under-test as a template parameter and automatically starts a new `Server` with only that `RpcHandler` registered. 220 | The methods `SendWrite()`, `SendWritesDone()` and `SendFinish()` can be used to synchronously drive the `RpcHandler` through its lifetime, i.e. when these methods return, all invocations on the `RpcHandler` side are guaranteed to have completed. 221 | 222 | The `RpcHandlerTestServer` achieves that by wrapping the handler-under-test in a `RpcHandlerWrapper` and conspires with that wrapper to ensure that it only returns once the `RpcHandler` invocations have completed. 223 | -------------------------------------------------------------------------------- /WORKSPACE: -------------------------------------------------------------------------------- 1 | # Copyright 2018 The Cartographer Authors 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | workspace(name = "com_github_googlecartographer_async_grpc") 16 | 17 | load("//:bazel/repositories.bzl", "repositories") 18 | 19 | repositories() 20 | 21 | load("@com_github_grpc_grpc//bazel:grpc_deps.bzl", "grpc_deps") 22 | 23 | grpc_deps() 24 | -------------------------------------------------------------------------------- /async_grpc-config.cmake.in: -------------------------------------------------------------------------------- 1 | # Copyright 2018 The Cartographer Authors 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | # Usage from an external project: 16 | # In your CMakeLists.txt, add these lines: 17 | # 18 | # find_package(async_grpc REQUIRED) 19 | # target_link_libraries(MY_TARGET_NAME PUBLIC async_grpc) 20 | 21 | @PACKAGE_INIT@ 22 | 23 | set_and_check(ASYNC_GRPC_CMAKE_DIR "@PACKAGE_ASYNC_GRPC_CMAKE_DIR@") 24 | 25 | include("${ASYNC_GRPC_CMAKE_DIR}/AsyncGrpcTargets.cmake") 26 | -------------------------------------------------------------------------------- /async_grpc/BUILD.bazel: -------------------------------------------------------------------------------- 1 | # Copyright 2018 The Cartographer Authors 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | # Top-level proto and C++ targets for Cartographer's gRPC server. 16 | 17 | licenses(["notice"]) # Apache 2.0 18 | 19 | package(default_visibility = ["//visibility:public"]) 20 | 21 | proto_library( 22 | name = "protos", 23 | srcs = glob( 24 | [ 25 | "**/*.proto", 26 | ], 27 | ), 28 | deps = [ 29 | "@com_google_protobuf//:empty_proto", 30 | ], 31 | ) 32 | 33 | cc_proto_library( 34 | name = "cc_protos", 35 | deps = [":protos"], 36 | ) 37 | 38 | # TODO(rodrigoq): This special rule name is required by cc_grpc_library. This 39 | # makes :protos look like it was created by 40 | # cc_grpc_library(proto_only=True, ...) 41 | proto_library( 42 | name = "_cc_protos_only", 43 | deps = [ 44 | ":protos", 45 | "@com_google_protobuf//:empty_proto", 46 | ], 47 | ) 48 | 49 | cc_library( 50 | name = "async_grpc", 51 | srcs = glob( 52 | [ 53 | "**/*.cc", 54 | ], 55 | exclude = [ 56 | "**/*_test.cc", 57 | ], 58 | ), 59 | hdrs = glob([ 60 | "**/*.h", 61 | ]), 62 | copts = ["-Wno-sign-compare"], 63 | includes = ["."], 64 | defines = ["BUILD_TRACING=0"], 65 | deps = [ 66 | ":cc_protos", 67 | "@com_github_grpc_grpc//:grpc++", 68 | "@com_google_glog//:glog", 69 | "@com_google_protobuf//:cc_wkt_protos", 70 | ], 71 | ) 72 | 73 | cc_library( 74 | name = "async_grpc_tracing", 75 | srcs = glob( 76 | [ 77 | "**/*.cc", 78 | ], 79 | exclude = [ 80 | "**/*_test.cc", 81 | ], 82 | ), 83 | hdrs = glob([ 84 | "**/*.h", 85 | ]), 86 | copts = ["-Wno-sign-compare"], 87 | includes = ["."], 88 | defines = ["BUILD_TRACING=1"], 89 | deps = [ 90 | ":cc_protos", 91 | "@com_github_grpc_grpc//:grpc++", 92 | "@com_google_glog//:glog", 93 | "@com_google_protobuf//:cc_wkt_protos", 94 | "@com_github_census_instrumentation_opencensus_cpp//opencensus/exporters/trace/stackdriver:stackdriver_exporter", 95 | "@com_github_census_instrumentation_opencensus_cpp//opencensus/trace", 96 | ], 97 | ) 98 | 99 | [cc_test( 100 | name = src.replace("/", "_").replace(".cc", ""), 101 | size = "small", 102 | srcs = [src], 103 | deps = [ 104 | ":async_grpc", 105 | "@com_google_googletest//:gtest_main", 106 | ], 107 | ) for src in glob( 108 | ["**/*_test.cc"], 109 | )] 110 | -------------------------------------------------------------------------------- /async_grpc/async_client.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 The Cartographer Authors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #ifndef ASYNC_GRPC_ASYNC_CLIENT_H 18 | #define ASYNC_GRPC_ASYNC_CLIENT_H 19 | 20 | #include 21 | 22 | #include "async_grpc/rpc_service_method_traits.h" 23 | #include "common/make_unique.h" 24 | #include "completion_queue_pool.h" 25 | #include "glog/logging.h" 26 | #include "grpc++/grpc++.h" 27 | #include "grpc++/impl/codegen/async_stream.h" 28 | #include "grpc++/impl/codegen/async_unary_call.h" 29 | #include "grpc++/impl/codegen/proto_utils.h" 30 | 31 | namespace async_grpc { 32 | 33 | class ClientEvent; 34 | 35 | class AsyncClientInterface { 36 | friend class CompletionQueue; 37 | 38 | public: 39 | virtual ~AsyncClientInterface() = default; 40 | 41 | private: 42 | virtual void HandleEvent( 43 | const CompletionQueue::ClientEvent& client_event) = 0; 44 | }; 45 | 46 | template ::StreamType> 49 | class AsyncClient {}; 50 | 51 | template 52 | class AsyncClient 54 | : public AsyncClientInterface { 55 | using RpcServiceMethod = RpcServiceMethodTraits; 56 | using RequestType = typename RpcServiceMethod::RequestType; 57 | using ResponseType = typename RpcServiceMethod::ResponseType; 58 | using CallbackType = 59 | std::function; 60 | 61 | public: 62 | AsyncClient(std::shared_ptr<::grpc::Channel> channel, CallbackType callback) 63 | : channel_(channel), 64 | callback_(callback), 65 | completion_queue_(CompletionQueuePool::GetCompletionQueue()), 66 | rpc_method_name_(RpcServiceMethod::MethodName()), 67 | rpc_method_(rpc_method_name_.c_str(), RpcServiceMethod::StreamType, 68 | channel_), 69 | finish_event_(CompletionQueue::ClientEvent::Event::FINISH, this) {} 70 | 71 | void WriteAsync(const RequestType& request) { 72 | response_reader_ = 73 | std::unique_ptr<::grpc::ClientAsyncResponseReader>( 74 | ::grpc::internal::ClientAsyncResponseReaderFactory< 75 | ResponseType>::Create(channel_.get(), completion_queue_, 76 | rpc_method_, &client_context_, request, 77 | /*start=*/false)); 78 | response_reader_->StartCall(); 79 | response_reader_->Finish(&response_, &status_, (void*)&finish_event_); 80 | } 81 | 82 | void HandleEvent(const CompletionQueue::ClientEvent& client_event) override { 83 | switch (client_event.event) { 84 | case CompletionQueue::ClientEvent::Event::FINISH: 85 | HandleFinishEvent(client_event); 86 | break; 87 | default: 88 | LOG(FATAL) << "Unhandled event type: " << (int)client_event.event; 89 | } 90 | } 91 | 92 | void HandleFinishEvent(const CompletionQueue::ClientEvent& client_event) { 93 | if (callback_) { 94 | callback_(status_, status_.ok() ? &response_ : nullptr); 95 | } 96 | } 97 | 98 | private: 99 | ::grpc::ClientContext client_context_; 100 | std::shared_ptr<::grpc::Channel> channel_; 101 | CallbackType callback_; 102 | ::grpc::CompletionQueue* completion_queue_; 103 | const std::string rpc_method_name_; 104 | const ::grpc::internal::RpcMethod rpc_method_; 105 | std::unique_ptr<::grpc::ClientAsyncResponseReader> 106 | response_reader_; 107 | CompletionQueue::ClientEvent finish_event_; 108 | ::grpc::Status status_; 109 | ResponseType response_; 110 | }; 111 | 112 | template 113 | class AsyncClient 115 | : public AsyncClientInterface { 116 | using RpcServiceMethod = RpcServiceMethodTraits; 117 | using RequestType = typename RpcServiceMethod::RequestType; 118 | using ResponseType = typename RpcServiceMethod::ResponseType; 119 | using CallbackType = 120 | std::function; 121 | 122 | public: 123 | AsyncClient(std::shared_ptr<::grpc::Channel> channel, CallbackType callback) 124 | : channel_(channel), 125 | callback_(callback), 126 | completion_queue_(CompletionQueuePool::GetCompletionQueue()), 127 | rpc_method_name_(RpcServiceMethod::MethodName()), 128 | rpc_method_(rpc_method_name_.c_str(), RpcServiceMethod::StreamType, 129 | channel_), 130 | write_event_(CompletionQueue::ClientEvent::Event::WRITE, this), 131 | read_event_(CompletionQueue::ClientEvent::Event::READ, this), 132 | finish_event_(CompletionQueue::ClientEvent::Event::FINISH, this) {} 133 | 134 | void WriteAsync(const RequestType& request) { 135 | // Start the call. 136 | response_reader_ = std::unique_ptr<::grpc::ClientAsyncReader>( 137 | ::grpc::internal::ClientAsyncReaderFactory::Create( 138 | channel_.get(), completion_queue_, rpc_method_, &client_context_, 139 | request, 140 | /*start=*/true, (void*)&write_event_)); 141 | } 142 | 143 | void HandleEvent(const CompletionQueue::ClientEvent& client_event) override { 144 | switch (client_event.event) { 145 | case CompletionQueue::ClientEvent::Event::WRITE: 146 | HandleWriteEvent(client_event); 147 | break; 148 | case CompletionQueue::ClientEvent::Event::READ: 149 | HandleReadEvent(client_event); 150 | break; 151 | case CompletionQueue::ClientEvent::Event::FINISH: 152 | HandleFinishEvent(client_event); 153 | break; 154 | default: 155 | LOG(FATAL) << "Unhandled event type: " << (int)client_event.event; 156 | } 157 | } 158 | 159 | void HandleWriteEvent(const CompletionQueue::ClientEvent& client_event) { 160 | if (!client_event.ok) { 161 | LOG(ERROR) << "Write failed in async server streaming."; 162 | ::grpc::Status status(::grpc::INTERNAL, 163 | "Write failed in async server streaming."); 164 | if (callback_) { 165 | callback_(status, nullptr); 166 | callback_ = nullptr; 167 | } 168 | finish_status_ = status; 169 | response_reader_->Finish(&finish_status_, (void*)&finish_event_); 170 | return; 171 | } 172 | 173 | response_reader_->Read(&response_, (void*)&read_event_); 174 | } 175 | 176 | void HandleReadEvent(const CompletionQueue::ClientEvent& client_event) { 177 | if (client_event.ok) { 178 | if (callback_) { 179 | callback_(::grpc::Status(), &response_); 180 | if (!client_event.ok) callback_ = nullptr; 181 | } 182 | response_reader_->Read(&response_, (void*)&read_event_); 183 | } else { 184 | finish_status_ = ::grpc::Status(); 185 | response_reader_->Finish(&finish_status_, (void*)&finish_event_); 186 | } 187 | } 188 | 189 | void HandleFinishEvent(const CompletionQueue::ClientEvent& client_event) { 190 | if (callback_) { 191 | if (!client_event.ok) { 192 | LOG(ERROR) << "Finish failed in async server streaming."; 193 | } 194 | callback_( 195 | client_event.ok 196 | ? ::grpc::Status() 197 | : ::grpc::Status(::grpc::INTERNAL, 198 | "Finish failed in async server streaming."), 199 | nullptr); 200 | callback_ = nullptr; 201 | } 202 | } 203 | 204 | private: 205 | ::grpc::ClientContext client_context_; 206 | std::shared_ptr<::grpc::Channel> channel_; 207 | CallbackType callback_; 208 | ::grpc::CompletionQueue* completion_queue_; 209 | const std::string rpc_method_name_; 210 | const ::grpc::internal::RpcMethod rpc_method_; 211 | std::unique_ptr<::grpc::ClientAsyncReader> response_reader_; 212 | CompletionQueue::ClientEvent write_event_; 213 | CompletionQueue::ClientEvent read_event_; 214 | CompletionQueue::ClientEvent finish_event_; 215 | ::grpc::Status status_; 216 | ResponseType response_; 217 | ::grpc::Status finish_status_; 218 | }; 219 | 220 | } // namespace async_grpc 221 | 222 | #endif // ASYNC_GRPC_ASYNC_CLIENT_H 223 | -------------------------------------------------------------------------------- /async_grpc/client.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 The Cartographer Authors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #ifndef CPP_GRPC_CLIENT_H 18 | #define CPP_GRPC_CLIENT_H 19 | 20 | #include "async_grpc/common/optional.h" 21 | #include "async_grpc/retry.h" 22 | #include "async_grpc/rpc_handler_interface.h" 23 | #include "async_grpc/rpc_service_method_traits.h" 24 | 25 | #include "grpc++/grpc++.h" 26 | #include "grpc++/impl/codegen/client_unary_call.h" 27 | #include "grpc++/impl/codegen/proto_utils.h" 28 | #include "grpc++/impl/codegen/sync_stream.h" 29 | 30 | #include "glog/logging.h" 31 | 32 | namespace async_grpc { 33 | 34 | // Wraps a method invocation for all rpc types, unary, client streaming, 35 | // server streaming, or bidirectional. 36 | // It cannot be used for multiple invocations. 37 | // It is not thread safe. 38 | template ::StreamType> 41 | class Client {}; 42 | 43 | // TODO(gaschler): Move specializations to separate header files. 44 | template 45 | class Client { 46 | using RpcServiceMethod = RpcServiceMethodTraits; 47 | using RequestType = typename RpcServiceMethod::RequestType; 48 | using ResponseType = typename RpcServiceMethod::ResponseType; 49 | 50 | public: 51 | Client(std::shared_ptr<::grpc::Channel> channel) 52 | : channel_(channel), 53 | client_context_(common::make_unique<::grpc::ClientContext>()), 54 | rpc_method_name_(RpcServiceMethod::MethodName()), 55 | rpc_method_(rpc_method_name_.c_str(), RpcServiceMethod::StreamType, 56 | channel_) {} 57 | 58 | // 'timeout' is used for every 'Write' separately, but multiple retries count 59 | // towards a single timeout. 60 | Client(std::shared_ptr<::grpc::Channel> channel, common::Duration timeout, 61 | RetryStrategy retry_strategy = nullptr) 62 | : channel_(channel), 63 | client_context_(common::make_unique<::grpc::ClientContext>()), 64 | rpc_method_name_(RpcServiceMethod::MethodName()), 65 | rpc_method_(rpc_method_name_.c_str(), RpcServiceMethod::StreamType, 66 | channel_), 67 | timeout_(timeout), 68 | retry_strategy_(retry_strategy) {} 69 | 70 | bool Write(const RequestType& request, ::grpc::Status* status = nullptr) { 71 | ::grpc::Status internal_status; 72 | common::optional deadline; 73 | if (timeout_.has_value()) { 74 | deadline = std::chrono::system_clock::now() + timeout_.value(); 75 | } 76 | client_context_ = ResetContext(deadline); 77 | bool result = RetryWithStrategy( 78 | retry_strategy_, 79 | [this, &request, &internal_status] { 80 | WriteImpl(request, &internal_status); 81 | return internal_status; 82 | }, 83 | [this, deadline] { client_context_ = ResetContext(deadline); }); 84 | if (status != nullptr) { 85 | *status = internal_status; 86 | } 87 | return result; 88 | } 89 | 90 | const ResponseType& response() { return response_; } 91 | 92 | private: 93 | static std::unique_ptr<::grpc::ClientContext> ResetContext( 94 | common::optional deadline) { 95 | auto context = common::make_unique<::grpc::ClientContext>(); 96 | if (deadline.has_value()) { 97 | context->set_deadline(deadline.value()); 98 | } 99 | return context; 100 | } 101 | 102 | bool WriteImpl(const RequestType& request, ::grpc::Status* status) { 103 | auto status_normal_rpc = MakeBlockingUnaryCall(request, &response_); 104 | if (status != nullptr) { 105 | *status = status_normal_rpc; 106 | } 107 | return status_normal_rpc.ok(); 108 | } 109 | ::grpc::Status MakeBlockingUnaryCall(const RequestType& request, 110 | ResponseType* response) { 111 | return ::grpc::internal::BlockingUnaryCall( 112 | channel_.get(), rpc_method_, client_context_.get(), request, response); 113 | } 114 | 115 | std::shared_ptr<::grpc::Channel> channel_; 116 | std::unique_ptr<::grpc::ClientContext> client_context_; 117 | const std::string rpc_method_name_; 118 | const ::grpc::internal::RpcMethod rpc_method_; 119 | common::optional timeout_; 120 | 121 | ResponseType response_; 122 | RetryStrategy retry_strategy_; 123 | }; 124 | 125 | template 126 | class Client { 128 | using RpcServiceMethod = RpcServiceMethodTraits; 129 | using RequestType = typename RpcServiceMethod::RequestType; 130 | using ResponseType = typename RpcServiceMethod::ResponseType; 131 | 132 | public: 133 | Client(std::shared_ptr<::grpc::Channel> channel) 134 | : channel_(channel), 135 | client_context_(common::make_unique<::grpc::ClientContext>()), 136 | rpc_method_name_(RpcServiceMethod::MethodName()), 137 | rpc_method_(rpc_method_name_.c_str(), RpcServiceMethod::StreamType, 138 | channel_) {} 139 | 140 | bool Write(const RequestType& request, ::grpc::Status* status = nullptr) { 141 | ::grpc::Status internal_status; 142 | WriteImpl(request, &internal_status); 143 | if (status != nullptr) { 144 | *status = internal_status; 145 | } 146 | return internal_status.ok(); 147 | } 148 | 149 | bool StreamWritesDone() { 150 | InstantiateClientWriterIfNeeded(); 151 | return client_writer_->WritesDone(); 152 | } 153 | 154 | ::grpc::Status StreamFinish() { 155 | InstantiateClientWriterIfNeeded(); 156 | return client_writer_->Finish(); 157 | } 158 | 159 | const ResponseType& response() { return response_; } 160 | 161 | private: 162 | bool WriteImpl(const RequestType& request, ::grpc::Status* status) { 163 | InstantiateClientWriterIfNeeded(); 164 | return client_writer_->Write(request); 165 | } 166 | 167 | void InstantiateClientWriterIfNeeded() { 168 | if (!client_writer_) { 169 | client_writer_.reset( 170 | ::grpc::internal::ClientWriterFactory::Create( 171 | channel_.get(), rpc_method_, client_context_.get(), &response_)); 172 | } 173 | } 174 | 175 | std::shared_ptr<::grpc::Channel> channel_; 176 | std::unique_ptr<::grpc::ClientContext> client_context_; 177 | const std::string rpc_method_name_; 178 | const ::grpc::internal::RpcMethod rpc_method_; 179 | 180 | std::unique_ptr<::grpc::ClientWriter> client_writer_; 181 | ResponseType response_; 182 | }; 183 | 184 | template 185 | class Client { 187 | using RpcServiceMethod = RpcServiceMethodTraits; 188 | using RequestType = typename RpcServiceMethod::RequestType; 189 | using ResponseType = typename RpcServiceMethod::ResponseType; 190 | 191 | public: 192 | Client(std::shared_ptr<::grpc::Channel> channel) 193 | : channel_(channel), 194 | client_context_(common::make_unique<::grpc::ClientContext>()), 195 | rpc_method_name_(RpcServiceMethod::MethodName()), 196 | rpc_method_(rpc_method_name_.c_str(), RpcServiceMethod::StreamType, 197 | channel_) {} 198 | 199 | bool StreamRead(ResponseType* response) { 200 | CHECK(client_reader_); 201 | return client_reader_->Read(response); 202 | } 203 | 204 | bool Write(const RequestType& request, ::grpc::Status* status = nullptr) { 205 | ::grpc::Status internal_status; 206 | WriteImpl(request, &internal_status); 207 | if (status != nullptr) { 208 | *status = internal_status; 209 | } 210 | return internal_status.ok(); 211 | } 212 | 213 | ::grpc::Status StreamFinish() { 214 | CHECK(client_reader_); 215 | return client_reader_->Finish(); 216 | } 217 | 218 | private: 219 | bool WriteImpl(const RequestType& request, ::grpc::Status* status) { 220 | InstantiateClientReader(request); 221 | return true; 222 | } 223 | 224 | void InstantiateClientReader(const RequestType& request) { 225 | client_reader_.reset( 226 | ::grpc::internal::ClientReaderFactory::Create( 227 | channel_.get(), rpc_method_, client_context_.get(), request)); 228 | } 229 | 230 | std::shared_ptr<::grpc::Channel> channel_; 231 | std::unique_ptr<::grpc::ClientContext> client_context_; 232 | const std::string rpc_method_name_; 233 | const ::grpc::internal::RpcMethod rpc_method_; 234 | 235 | std::unique_ptr<::grpc::ClientReader> client_reader_; 236 | }; 237 | 238 | template 239 | class Client { 241 | using RpcServiceMethod = RpcServiceMethodTraits; 242 | using RequestType = typename RpcServiceMethod::RequestType; 243 | using ResponseType = typename RpcServiceMethod::ResponseType; 244 | 245 | public: 246 | Client(std::shared_ptr<::grpc::Channel> channel) 247 | : channel_(channel), 248 | client_context_(common::make_unique<::grpc::ClientContext>()), 249 | rpc_method_name_(RpcServiceMethod::MethodName()), 250 | rpc_method_(rpc_method_name_.c_str(), RpcServiceMethod::StreamType, 251 | channel_) {} 252 | 253 | bool StreamRead(ResponseType* response) { 254 | InstantiateClientReaderWriterIfNeeded(); 255 | return client_reader_writer_->Read(response); 256 | } 257 | 258 | bool Write(const RequestType& request, ::grpc::Status* status = nullptr) { 259 | ::grpc::Status internal_status; 260 | WriteImpl(request, &internal_status); 261 | if (status != nullptr) { 262 | *status = internal_status; 263 | } 264 | return internal_status.ok(); 265 | } 266 | 267 | bool StreamWritesDone() { 268 | InstantiateClientReaderWriterIfNeeded(); 269 | return client_reader_writer_->WritesDone(); 270 | } 271 | 272 | ::grpc::Status StreamFinish() { 273 | InstantiateClientReaderWriterIfNeeded(); 274 | return client_reader_writer_->Finish(); 275 | } 276 | 277 | private: 278 | bool WriteImpl(const RequestType& request, ::grpc::Status* status) { 279 | InstantiateClientReaderWriterIfNeeded(); 280 | return client_reader_writer_->Write(request); 281 | } 282 | 283 | void InstantiateClientReaderWriterIfNeeded() { 284 | if (!client_reader_writer_) { 285 | client_reader_writer_.reset( 286 | ::grpc::internal::ClientReaderWriterFactory< 287 | RequestType, ResponseType>::Create(channel_.get(), rpc_method_, 288 | client_context_.get())); 289 | } 290 | } 291 | 292 | std::shared_ptr<::grpc::Channel> channel_; 293 | std::unique_ptr<::grpc::ClientContext> client_context_; 294 | const std::string rpc_method_name_; 295 | const ::grpc::internal::RpcMethod rpc_method_; 296 | 297 | std::unique_ptr<::grpc::ClientReaderWriter> 298 | client_reader_writer_; 299 | }; 300 | 301 | } // namespace async_grpc 302 | 303 | #endif // CPP_GRPC_CLIENT_H 304 | -------------------------------------------------------------------------------- /async_grpc/client_test.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 The Cartographer Authors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #include "async_grpc/client.h" 18 | 19 | #include "async_grpc/proto/math_service.pb.h" 20 | #include "async_grpc/retry.h" 21 | #include "glog/logging.h" 22 | #include "grpc++/grpc++.h" 23 | #include "gtest/gtest.h" 24 | 25 | namespace async_grpc { 26 | namespace { 27 | 28 | struct GetEchoMethod { 29 | static constexpr const char* MethodName() { 30 | return "/async_grpc.proto.Math/GetEcho"; 31 | } 32 | using IncomingType = proto::GetEchoRequest; 33 | using OutgoingType = proto::GetEchoResponse; 34 | }; 35 | 36 | const char* kWrongAddress = "wrong-domain-does-not-exist:50051"; 37 | 38 | TEST(ClientTest, TimesOut) { 39 | auto client_channel = ::grpc::CreateChannel( 40 | kWrongAddress, ::grpc::InsecureChannelCredentials()); 41 | Client client(client_channel, common::FromSeconds(0.1)); 42 | proto::GetEchoRequest request; 43 | grpc::Status status; 44 | EXPECT_FALSE(client.Write(request, &status)); 45 | } 46 | 47 | TEST(ClientTest, TimesOutWithRetries) { 48 | auto client_channel = ::grpc::CreateChannel( 49 | kWrongAddress, ::grpc::InsecureChannelCredentials()); 50 | Client client( 51 | client_channel, common::FromSeconds(0.5), 52 | CreateLimitedBackoffStrategy(common::FromSeconds(0.1), 1, 3)); 53 | proto::GetEchoRequest request; 54 | grpc::Status status; 55 | EXPECT_FALSE(client.Write(request, &status)); 56 | } 57 | 58 | } // namespace 59 | } // namespace async_grpc 60 | -------------------------------------------------------------------------------- /async_grpc/common/blocking_queue.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 The Cartographer Authors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #ifndef CPP_GRPC_COMMON_BLOCKING_QUEUE_H_ 18 | #define CPP_GRPC_COMMON_BLOCKING_QUEUE_H_ 19 | 20 | #include 21 | #include 22 | #include 23 | 24 | #include "async_grpc/common/mutex.h" 25 | #include "async_grpc/common/port.h" 26 | #include "async_grpc/common/time.h" 27 | #include "glog/logging.h" 28 | 29 | namespace async_grpc { 30 | namespace common { 31 | 32 | // A thread-safe blocking queue that is useful for producer/consumer patterns. 33 | // 'T' must be movable. 34 | template 35 | class BlockingQueue { 36 | public: 37 | static constexpr size_t kInfiniteQueueSize = 0; 38 | 39 | // Constructs a blocking queue with infinite queue size. 40 | BlockingQueue() : BlockingQueue(kInfiniteQueueSize) {} 41 | 42 | BlockingQueue(const BlockingQueue&) = delete; 43 | BlockingQueue& operator=(const BlockingQueue&) = delete; 44 | 45 | // Constructs a blocking queue with a size of 'queue_size'. 46 | explicit BlockingQueue(const size_t queue_size) : queue_size_(queue_size) {} 47 | 48 | // Pushes a value onto the queue. Blocks if the queue is full. 49 | void Push(T t) { 50 | MutexLocker lock(&mutex_); 51 | lock.Await([this]() REQUIRES(mutex_) { return QueueNotFullCondition(); }); 52 | deque_.push_back(std::move(t)); 53 | } 54 | 55 | // Like push, but returns false if 'timeout' is reached. 56 | bool PushWithTimeout(T t, const common::Duration timeout) { 57 | MutexLocker lock(&mutex_); 58 | if (!lock.AwaitWithTimeout( 59 | [this]() REQUIRES(mutex_) { return QueueNotFullCondition(); }, 60 | timeout)) { 61 | return false; 62 | } 63 | deque_.push_back(std::move(t)); 64 | return true; 65 | } 66 | 67 | // Pops the next value from the queue. Blocks until a value is available. 68 | T Pop() { 69 | MutexLocker lock(&mutex_); 70 | lock.Await([this]() REQUIRES(mutex_) { return !QueueEmptyCondition(); }); 71 | 72 | T t = std::move(deque_.front()); 73 | deque_.pop_front(); 74 | return t; 75 | } 76 | 77 | // Like Pop, but can timeout. Returns nullptr in this case. 78 | T PopWithTimeout(const common::Duration timeout) { 79 | MutexLocker lock(&mutex_); 80 | if (!lock.AwaitWithTimeout( 81 | [this]() REQUIRES(mutex_) { return !QueueEmptyCondition(); }, 82 | timeout)) { 83 | return nullptr; 84 | } 85 | T t = std::move(deque_.front()); 86 | deque_.pop_front(); 87 | return t; 88 | } 89 | 90 | // Returns the next value in the queue or nullptr if the queue is empty. 91 | // Maintains ownership. This assumes a member function get() that returns 92 | // a pointer to the given type R. 93 | template 94 | const R* Peek() { 95 | MutexLocker lock(&mutex_); 96 | if (deque_.empty()) { 97 | return nullptr; 98 | } 99 | return deque_.front().get(); 100 | } 101 | 102 | // Returns the number of items currently in the queue. 103 | size_t Size() { 104 | MutexLocker lock(&mutex_); 105 | return deque_.size(); 106 | } 107 | 108 | // Blocks until the queue is empty. 109 | void WaitUntilEmpty() { 110 | MutexLocker lock(&mutex_); 111 | lock.Await([this]() REQUIRES(mutex_) { return QueueEmptyCondition(); }); 112 | } 113 | 114 | private: 115 | // Returns true iff the queue is empty. 116 | bool QueueEmptyCondition() REQUIRES(mutex_) { return deque_.empty(); } 117 | 118 | // Returns true iff the queue is not full. 119 | bool QueueNotFullCondition() REQUIRES(mutex_) { 120 | return queue_size_ == kInfiniteQueueSize || deque_.size() < queue_size_; 121 | } 122 | 123 | Mutex mutex_; 124 | const size_t queue_size_ GUARDED_BY(mutex_); 125 | std::deque deque_ GUARDED_BY(mutex_); 126 | }; 127 | 128 | } // namespace common 129 | } // namespace async_grpc 130 | 131 | #endif // CPP_GRPC_COMMON_BLOCKING_QUEUE_H_ 132 | -------------------------------------------------------------------------------- /async_grpc/common/make_unique.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 The Cartographer Authors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #ifndef CPP_GRPC_COMMON_MAKE_UNIQUE_H_ 18 | #define CPP_GRPC_COMMON_MAKE_UNIQUE_H_ 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | namespace async_grpc { 26 | namespace common { 27 | 28 | // Implementation of c++14's std::make_unique, taken from 29 | // https://isocpp.org/files/papers/N3656.txt 30 | template 31 | struct _Unique_if { 32 | typedef std::unique_ptr _Single_object; 33 | }; 34 | 35 | template 36 | struct _Unique_if { 37 | typedef std::unique_ptr _Unknown_bound; 38 | }; 39 | 40 | template 41 | struct _Unique_if { 42 | typedef void _Known_bound; 43 | }; 44 | 45 | template 46 | typename _Unique_if::_Single_object make_unique(Args&&... args) { 47 | return std::unique_ptr(new T(std::forward(args)...)); 48 | } 49 | 50 | template 51 | typename _Unique_if::_Unknown_bound make_unique(size_t n) { 52 | typedef typename std::remove_extent::type U; 53 | return std::unique_ptr(new U[n]()); 54 | } 55 | 56 | template 57 | typename _Unique_if::_Known_bound make_unique(Args&&...) = delete; 58 | 59 | } // namespace common 60 | } // namespace async_grpc 61 | 62 | #endif // CPP_GRPC_COMMON_MAKE_UNIQUE_H_ 63 | -------------------------------------------------------------------------------- /async_grpc/common/mutex.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 The Cartographer Authors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #ifndef CPP_GRPC_COMMON_MUTEX_H_ 18 | #define CPP_GRPC_COMMON_MUTEX_H_ 19 | 20 | #include 21 | #include 22 | 23 | #include "async_grpc/common/time.h" 24 | 25 | namespace async_grpc { 26 | namespace common { 27 | 28 | // Enable thread safety attributes only with clang. 29 | // The attributes can be safely erased when compiling with other compilers. 30 | #if defined(__SUPPORT_TS_ANNOTATION__) || defined(__clang__) 31 | #define THREAD_ANNOTATION_ATTRIBUTE__(x) __attribute__((x)) 32 | #else 33 | #define THREAD_ANNOTATION_ATTRIBUTE__(x) // no-op 34 | #endif 35 | 36 | #define CAPABILITY(x) THREAD_ANNOTATION_ATTRIBUTE__(capability(x)) 37 | 38 | #define SCOPED_CAPABILITY THREAD_ANNOTATION_ATTRIBUTE__(scoped_lockable) 39 | 40 | #define GUARDED_BY(x) THREAD_ANNOTATION_ATTRIBUTE__(guarded_by(x)) 41 | 42 | #define PT_GUARDED_BY(x) THREAD_ANNOTATION_ATTRIBUTE__(pt_guarded_by(x)) 43 | 44 | #define REQUIRES(...) \ 45 | THREAD_ANNOTATION_ATTRIBUTE__(requires_capability(__VA_ARGS__)) 46 | 47 | #define ACQUIRE(...) \ 48 | THREAD_ANNOTATION_ATTRIBUTE__(acquire_capability(__VA_ARGS__)) 49 | 50 | #define RELEASE(...) \ 51 | THREAD_ANNOTATION_ATTRIBUTE__(release_capability(__VA_ARGS__)) 52 | 53 | #define EXCLUDES(...) THREAD_ANNOTATION_ATTRIBUTE__(locks_excluded(__VA_ARGS__)) 54 | 55 | #define NO_THREAD_SAFETY_ANALYSIS \ 56 | THREAD_ANNOTATION_ATTRIBUTE__(no_thread_safety_analysis) 57 | 58 | // Defines an annotated mutex that can only be locked through its scoped locker 59 | // implementation. 60 | class CAPABILITY("mutex") Mutex { 61 | public: 62 | // A RAII class that acquires a mutex in its constructor, and 63 | // releases it in its destructor. It also implements waiting functionality on 64 | // conditions that get checked whenever the mutex is released. 65 | class SCOPED_CAPABILITY Locker { 66 | public: 67 | Locker(Mutex* mutex) ACQUIRE(mutex) : mutex_(mutex), lock_(mutex->mutex_) {} 68 | 69 | ~Locker() RELEASE() { 70 | lock_.unlock(); 71 | mutex_->condition_.notify_all(); 72 | } 73 | 74 | template 75 | void Await(Predicate predicate) REQUIRES(this) { 76 | mutex_->condition_.wait(lock_, predicate); 77 | } 78 | 79 | template 80 | bool AwaitWithTimeout(Predicate predicate, common::Duration timeout) 81 | REQUIRES(this) { 82 | return mutex_->condition_.wait_for(lock_, timeout, predicate); 83 | } 84 | 85 | private: 86 | Mutex* mutex_; 87 | std::unique_lock lock_; 88 | }; 89 | 90 | private: 91 | std::condition_variable condition_; 92 | std::mutex mutex_; 93 | }; 94 | 95 | using MutexLocker = Mutex::Locker; 96 | 97 | } // namespace common 98 | } // namespace async_grpc 99 | 100 | #endif // CPP_GRPC_COMMON_MUTEX_H_ 101 | -------------------------------------------------------------------------------- /async_grpc/common/optional.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 The Cartographer Authors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #ifndef CPP_GRPC_COMMON_OPTIONAL_H_ 18 | #define CPP_GRPC_COMMON_OPTIONAL_H_ 19 | 20 | #include 21 | 22 | #include "async_grpc/common/make_unique.h" 23 | #include "glog/logging.h" 24 | 25 | namespace async_grpc { 26 | namespace common { 27 | 28 | template 29 | class optional { 30 | public: 31 | optional() {} 32 | 33 | optional(const optional& other) { 34 | if (other.has_value()) { 35 | value_ = common::make_unique(other.value()); 36 | } 37 | } 38 | 39 | explicit optional(const T& value) { value_ = common::make_unique(value); } 40 | 41 | bool has_value() const { return value_ != nullptr; } 42 | 43 | const T& value() const { 44 | CHECK(value_ != nullptr); 45 | return *value_; 46 | } 47 | 48 | optional& operator=(const T& other_value) { 49 | this->value_ = common::make_unique(other_value); 50 | return *this; 51 | } 52 | 53 | optional& operator=(const optional& other) { 54 | if (!other.has_value()) { 55 | this->value_ = nullptr; 56 | } else { 57 | this->value_ = common::make_unique(other.value()); 58 | } 59 | return *this; 60 | } 61 | 62 | private: 63 | std::unique_ptr value_; 64 | }; 65 | 66 | } // namespace common 67 | } // namespace async_grpc 68 | 69 | #endif // CPP_GRPC_COMMON_OPTIONAL_H_ 70 | -------------------------------------------------------------------------------- /async_grpc/common/port.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 The Cartographer Authors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #ifndef CPP_GRPC_COMMON_PORT_H_ 18 | #define CPP_GRPC_COMMON_PORT_H_ 19 | 20 | #include 21 | 22 | namespace async_grpc { 23 | 24 | using int8 = int8_t; 25 | using int16 = int16_t; 26 | using int32 = int32_t; 27 | using int64 = int64_t; 28 | using uint8 = uint8_t; 29 | using uint16 = uint16_t; 30 | using uint32 = uint32_t; 31 | using uint64 = uint64_t; 32 | 33 | } // namespace async_grpc 34 | 35 | #endif // CPP_GRPC_COMMON_PORT_H_ 36 | -------------------------------------------------------------------------------- /async_grpc/common/time.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 The Cartographer Authors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #include "async_grpc/common/time.h" 18 | 19 | #include 20 | 21 | namespace async_grpc { 22 | namespace common { 23 | 24 | Duration FromSeconds(const double seconds) { 25 | return std::chrono::duration_cast( 26 | std::chrono::duration(seconds)); 27 | } 28 | 29 | double ToSeconds(const Duration duration) { 30 | return std::chrono::duration_cast>(duration) 31 | .count(); 32 | } 33 | 34 | Time FromUniversal(const int64 ticks) { return Time(Duration(ticks)); } 35 | 36 | int64 ToUniversal(const Time time) { return time.time_since_epoch().count(); } 37 | 38 | std::ostream& operator<<(std::ostream& os, const Time time) { 39 | os << std::to_string(ToUniversal(time)); 40 | return os; 41 | } 42 | 43 | common::Duration FromMilliseconds(const int64 milliseconds) { 44 | return std::chrono::duration_cast( 45 | std::chrono::milliseconds(milliseconds)); 46 | } 47 | 48 | } // namespace common 49 | } // namespace async_grpc 50 | -------------------------------------------------------------------------------- /async_grpc/common/time.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 The Cartographer Authors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #ifndef CPP_GRPC_COMMON_TIME_H_ 18 | #define CPP_GRPC_COMMON_TIME_H_ 19 | 20 | #include 21 | #include 22 | #include 23 | 24 | #include "async_grpc/common/port.h" 25 | 26 | namespace async_grpc { 27 | namespace common { 28 | 29 | constexpr int64 kUtsEpochOffsetFromUnixEpochInSeconds = 30 | (719162ll * 24ll * 60ll * 60ll); 31 | 32 | struct UniversalTimeScaleClock { 33 | using rep = int64; 34 | using period = std::ratio<1, 10000000>; 35 | using duration = std::chrono::duration; 36 | using time_point = std::chrono::time_point; 37 | static constexpr bool is_steady = true; 38 | }; 39 | 40 | // Represents Universal Time Scale durations and timestamps which are 64-bit 41 | // integers representing the 100 nanosecond ticks since the Epoch which is 42 | // January 1, 1 at the start of day in UTC. 43 | using Duration = UniversalTimeScaleClock::duration; 44 | using Time = UniversalTimeScaleClock::time_point; 45 | 46 | // Convenience functions to create common::Durations. 47 | Duration FromSeconds(double seconds); 48 | Duration FromMilliseconds(int64 milliseconds); 49 | 50 | // Returns the given duration in seconds. 51 | double ToSeconds(Duration duration); 52 | 53 | // Creates a time from a Universal Time Scale. 54 | Time FromUniversal(int64 ticks); 55 | 56 | // Outputs the Universal Time Scale timestamp for a given Time. 57 | int64 ToUniversal(Time time); 58 | 59 | // For logging and unit tests, outputs the timestamp integer. 60 | std::ostream& operator<<(std::ostream& os, Time time); 61 | 62 | } // namespace common 63 | } // namespace async_grpc 64 | 65 | #endif // CPP_GRPC_COMMON_TIME_H_ 66 | -------------------------------------------------------------------------------- /async_grpc/completion_queue_pool.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 The Cartographer Authors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #include 18 | 19 | #include "async_grpc/async_client.h" 20 | #include "async_grpc/completion_queue_pool.h" 21 | #include "common/make_unique.h" 22 | #include "glog/logging.h" 23 | 24 | namespace async_grpc { 25 | namespace { 26 | 27 | size_t kDefaultNumberCompletionQueues = 2; 28 | 29 | } // namespace 30 | 31 | void CompletionQueue::Start() { 32 | CHECK(!thread_) << "CompletionQueue already started."; 33 | thread_ = 34 | common::make_unique([this]() { RunCompletionQueue(); }); 35 | } 36 | 37 | void CompletionQueue::Shutdown() { 38 | CHECK(thread_) << "CompletionQueue not yet started."; 39 | LOG(INFO) << "Shutting down client completion queue " << this; 40 | completion_queue_.Shutdown(); 41 | thread_->join(); 42 | } 43 | 44 | void CompletionQueue::RunCompletionQueue() { 45 | bool ok; 46 | void* tag; 47 | while (completion_queue_.Next(&tag, &ok)) { 48 | auto* client_event = static_cast(tag); 49 | client_event->ok = ok; 50 | client_event->async_client->HandleEvent(*client_event); 51 | } 52 | } 53 | 54 | CompletionQueuePool* CompletionQueuePool::completion_queue_pool() { 55 | static CompletionQueuePool* const kInstance = new CompletionQueuePool(); 56 | return kInstance; 57 | } 58 | 59 | void CompletionQueuePool::SetNumberCompletionQueues( 60 | size_t number_completion_queues) { 61 | CompletionQueuePool* pool = completion_queue_pool(); 62 | CHECK(!pool->initialized_) 63 | << "Can't change number of completion queues after initialization."; 64 | CHECK_GT(number_completion_queues, 0u); 65 | pool->number_completion_queues_ = number_completion_queues; 66 | } 67 | 68 | ::grpc::CompletionQueue* CompletionQueuePool::GetCompletionQueue() { 69 | CompletionQueuePool* pool = completion_queue_pool(); 70 | pool->Initialize(); 71 | const unsigned int qid = rand() % pool->completion_queues_.size(); 72 | return pool->completion_queues_.at(qid).completion_queue(); 73 | } 74 | 75 | void CompletionQueuePool::Start() { 76 | CompletionQueuePool* pool = completion_queue_pool(); 77 | pool->Initialize(); 78 | } 79 | 80 | void CompletionQueuePool::Shutdown() { 81 | LOG(INFO) << "Shutting down CompletionQueuePool"; 82 | CompletionQueuePool* pool = completion_queue_pool(); 83 | common::MutexLocker locker(&pool->mutex_); 84 | for (size_t i = 0; i < pool->completion_queues_.size(); ++i) { 85 | pool->completion_queues_.at(i).Shutdown(); 86 | } 87 | pool->completion_queues_.clear(); 88 | pool->initialized_ = false; 89 | } 90 | 91 | CompletionQueuePool::CompletionQueuePool() 92 | : number_completion_queues_(kDefaultNumberCompletionQueues) { 93 | } 94 | 95 | CompletionQueuePool::~CompletionQueuePool() { 96 | LOG(INFO) << "~CompletionQueuePool"; 97 | } 98 | 99 | void CompletionQueuePool::Initialize() { 100 | common::MutexLocker locker(&mutex_); 101 | if (initialized_) { 102 | return; 103 | } 104 | completion_queues_.resize(number_completion_queues_); 105 | for (auto& completion_queue : completion_queues_) { 106 | completion_queue.Start(); 107 | } 108 | initialized_ = true; 109 | } 110 | 111 | } // namespace async_grpc 112 | -------------------------------------------------------------------------------- /async_grpc/completion_queue_pool.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 The Cartographer Authors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #ifndef ASYNC_GRPC_COMMON_COMPLETION_QUEUE_POOL_H_ 18 | #define ASYNC_GRPC_COMMON_COMPLETION_QUEUE_POOL_H_ 19 | 20 | #include 21 | #include 22 | #include 23 | 24 | #include "common/mutex.h" 25 | #include "grpc++/grpc++.h" 26 | 27 | namespace async_grpc { 28 | 29 | class AsyncClientInterface; 30 | 31 | class CompletionQueue { 32 | public: 33 | struct ClientEvent { 34 | enum class Event { FINISH = 0, READ = 1, WRITE = 2 }; 35 | ClientEvent(Event event, AsyncClientInterface* async_client) 36 | : event(event), async_client(async_client) {} 37 | Event event; 38 | AsyncClientInterface* async_client; 39 | bool ok = false; 40 | }; 41 | 42 | public: 43 | CompletionQueue() = default; 44 | 45 | void Start(); 46 | void Shutdown(); 47 | 48 | ::grpc::CompletionQueue* completion_queue() { return &completion_queue_; } 49 | 50 | private: 51 | void RunCompletionQueue(); 52 | 53 | ::grpc::CompletionQueue completion_queue_; 54 | std::unique_ptr thread_; 55 | }; 56 | 57 | // TODO(cschuet): Add unit test for CompletionQueuePool. 58 | class CompletionQueuePool { 59 | public: 60 | static void SetNumberCompletionQueues(size_t number_completion_queues); 61 | static void Start(); 62 | static void Shutdown(); 63 | 64 | // Returns a random completion queue. 65 | static ::grpc::CompletionQueue* GetCompletionQueue(); 66 | 67 | private: 68 | CompletionQueuePool(); 69 | ~CompletionQueuePool(); 70 | 71 | void Initialize(); 72 | static CompletionQueuePool* completion_queue_pool(); 73 | 74 | common::Mutex mutex_; 75 | bool initialized_ = false; 76 | size_t number_completion_queues_; 77 | std::vector completion_queues_; 78 | }; 79 | 80 | } // namespace async_grpc 81 | 82 | #endif // ASYNC_GRPC_COMMON_COMPLETION_QUEUE_POOL_H_ 83 | -------------------------------------------------------------------------------- /async_grpc/completion_queue_thread.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 The Cartographer Authors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #include "async_grpc/completion_queue_thread.h" 18 | 19 | #include "async_grpc/common/make_unique.h" 20 | #include "glog/logging.h" 21 | 22 | namespace async_grpc { 23 | 24 | CompletionQueueThread::CompletionQueueThread( 25 | std::unique_ptr<::grpc::ServerCompletionQueue> completion_queue) 26 | : completion_queue_(std::move(completion_queue)) {} 27 | 28 | ::grpc::ServerCompletionQueue* CompletionQueueThread::completion_queue() { 29 | return completion_queue_.get(); 30 | } 31 | 32 | void CompletionQueueThread::Start(CompletionQueueRunner runner) { 33 | CHECK(!worker_thread_); 34 | worker_thread_ = common::make_unique( 35 | [this, runner]() { runner(this->completion_queue_.get()); }); 36 | } 37 | 38 | void CompletionQueueThread::Shutdown() { 39 | LOG(INFO) << "Shutting down completion queue " << completion_queue_.get(); 40 | completion_queue_->Shutdown(); 41 | worker_thread_->join(); 42 | } 43 | 44 | } // namespace async_grpc 45 | -------------------------------------------------------------------------------- /async_grpc/completion_queue_thread.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 The Cartographer Authors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #ifndef CPP_GRPC_COMMON_COMPLETION_QUEUE_THREAD_H_ 18 | #define CPP_GRPC_COMMON_COMPLETION_QUEUE_THREAD_H_ 19 | 20 | #include 21 | #include 22 | #include 23 | 24 | namespace async_grpc { 25 | 26 | class CompletionQueueThread { 27 | public: 28 | using CompletionQueueRunner = 29 | std::function; 30 | 31 | explicit CompletionQueueThread( 32 | std::unique_ptr<::grpc::ServerCompletionQueue> completion_queue); 33 | 34 | ::grpc::ServerCompletionQueue* completion_queue(); 35 | 36 | void Start(CompletionQueueRunner runner); 37 | void Shutdown(); 38 | 39 | private: 40 | std::unique_ptr<::grpc::ServerCompletionQueue> completion_queue_; 41 | std::unique_ptr worker_thread_; 42 | }; 43 | 44 | } // namespace async_grpc 45 | 46 | #endif // CPP_GRPC_COMMON_COMPLETION_QUEUE_THREAD_H_ 47 | -------------------------------------------------------------------------------- /async_grpc/event_queue_thread.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 The Cartographer Authors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #include "async_grpc/event_queue_thread.h" 18 | 19 | #include "async_grpc/common/make_unique.h" 20 | #include "glog/logging.h" 21 | 22 | namespace async_grpc { 23 | 24 | EventQueueThread::EventQueueThread() { 25 | event_queue_ = common::make_unique(); 26 | } 27 | 28 | EventQueue* EventQueueThread::event_queue() { return event_queue_.get(); } 29 | 30 | void EventQueueThread::Start(EventQueueRunner runner) { 31 | CHECK(!thread_); 32 | EventQueue* event_queue = event_queue_.get(); 33 | thread_ = common::make_unique( 34 | [event_queue, runner]() { runner(event_queue); }); 35 | } 36 | 37 | void EventQueueThread::Shutdown() { 38 | LOG(INFO) << "Shutting down event queue " << event_queue_.get(); 39 | thread_->join(); 40 | } 41 | 42 | } // namespace async_grpc 43 | -------------------------------------------------------------------------------- /async_grpc/event_queue_thread.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 The Cartographer Authors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #ifndef CPP_GRPC_EVENT_QUEUE_THREAD_H 18 | #define CPP_GRPC_EVENT_QUEUE_THREAD_H 19 | 20 | #include 21 | #include 22 | 23 | #include "async_grpc/common/blocking_queue.h" 24 | #include "async_grpc/rpc.h" 25 | 26 | namespace async_grpc { 27 | 28 | class EventQueueThread { 29 | public: 30 | using EventQueueRunner = std::function; 31 | 32 | EventQueueThread(); 33 | 34 | EventQueue* event_queue(); 35 | 36 | void Start(EventQueueRunner runner); 37 | void Shutdown(); 38 | 39 | private: 40 | std::unique_ptr event_queue_; 41 | std::unique_ptr thread_; 42 | }; 43 | 44 | } // namespace async_grpc 45 | 46 | #endif // CPP_GRPC_EVENT_QUEUE_THREAD_H 47 | -------------------------------------------------------------------------------- /async_grpc/execution_context.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 The Cartographer Authors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #ifndef CPP_GRPC_EXECUTION_CONTEXT_H 18 | #define CPP_GRPC_EXECUTION_CONTEXT_H 19 | 20 | #include "async_grpc/common/mutex.h" 21 | #include "glog/logging.h" 22 | 23 | namespace async_grpc { 24 | 25 | // Implementations of this class allow RPC handlers to share state among one 26 | // another. Using Server::SetExecutionContext(...) a server-wide 27 | // 'ExecutionContext' can be specified. This 'ExecutionContext' can be retrieved 28 | // by all implementations of 'RpcHandler' by calling 29 | // 'RpcHandler::GetContext()'. 30 | class ExecutionContext { 31 | public: 32 | // Automatically locks an ExecutionContext for shared use by RPC handlers. 33 | // This non-movable, non-copyable class is used to broker access from various 34 | // RPC handlers to the shared 'ExecutionContext'. 35 | template 36 | class Synchronized { 37 | public: 38 | ContextType* operator->() { 39 | return static_cast(execution_context_); 40 | } 41 | Synchronized(common::Mutex* lock, ExecutionContext* execution_context) 42 | : locker_(lock), execution_context_(execution_context) {} 43 | Synchronized(const Synchronized&) = delete; 44 | Synchronized(Synchronized&&) = delete; 45 | 46 | private: 47 | common::MutexLocker locker_; 48 | ExecutionContext* execution_context_; 49 | }; 50 | ExecutionContext() = default; 51 | virtual ~ExecutionContext() = default; 52 | ExecutionContext(const ExecutionContext&) = delete; 53 | ExecutionContext& operator=(const ExecutionContext&) = delete; 54 | common::Mutex* lock() { return &lock_; } 55 | 56 | private: 57 | common::Mutex lock_; 58 | }; 59 | 60 | } // namespace async_grpc 61 | 62 | #endif // CPP_GRPC_EXECUTION_CONTEXT_H 63 | -------------------------------------------------------------------------------- /async_grpc/opencensus_span.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 The Cartographer Authors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #include "async_grpc/opencensus_span.h" 18 | 19 | #if BUILD_TRACING 20 | 21 | namespace async_grpc { 22 | 23 | std::unique_ptr OpencensusSpan::StartSpan(const std::string& name, 24 | const OpencensusSpan* parent) { 25 | return std::unique_ptr(new OpencensusSpan(name, parent)); 26 | } 27 | 28 | std::unique_ptr OpencensusSpan::CreateChildSpan(const std::string& name) { 29 | return std::unique_ptr(new OpencensusSpan(name, this)); 30 | } 31 | 32 | void OpencensusSpan::SetStatus(const ::grpc::Status& status) { 33 | span_.SetStatus((opencensus::trace::StatusCode)status.error_code()); 34 | } 35 | 36 | void OpencensusSpan::End() { span_.End(); } 37 | 38 | OpencensusSpan::OpencensusSpan(const std::string& name, 39 | const OpencensusSpan* parent) 40 | : span_(opencensus::trace::Span::StartSpan(name, parent ? &parent->span_: nullptr)) {} 41 | 42 | } // namespace async_grpc 43 | 44 | #endif // BUILD_TRACING 45 | -------------------------------------------------------------------------------- /async_grpc/opencensus_span.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 The Cartographer Authors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #ifndef CPP_GRPC_OPENCENSUS_SPAN_H 18 | #define CPP_GRPC_OPENCENSUS_SPAN_H 19 | 20 | #if BUILD_TRACING 21 | 22 | #include 23 | #include 24 | 25 | #include "async_grpc/span.h" 26 | #include "opencensus/trace/span.h" 27 | 28 | namespace async_grpc { 29 | 30 | // An implementation of the Span interface backed by Opencensus. 31 | class OpencensusSpan : public Span { 32 | public: 33 | static std::unique_ptr StartSpan( 34 | const std::string& name, const OpencensusSpan* parent = nullptr); 35 | 36 | std::unique_ptr CreateChildSpan(const std::string& name) override; 37 | void SetStatus(const ::grpc::Status& status) override; 38 | void End() override; 39 | 40 | private: 41 | OpencensusSpan(const std::string& name, 42 | const OpencensusSpan* parent = nullptr); 43 | 44 | opencensus::trace::Span span_; 45 | }; 46 | 47 | } // namespace async_grpc 48 | 49 | #endif // BUILD_TRACING 50 | 51 | #endif // CPP_GRPC_OPENCENSUS_SPAN_H -------------------------------------------------------------------------------- /async_grpc/proto/math_service.proto: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Cartographer Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | syntax = "proto3"; 16 | 17 | package async_grpc.proto; 18 | 19 | message GetSumRequest { 20 | int32 input = 1; 21 | } 22 | 23 | message GetSumResponse { 24 | int32 output = 1; 25 | } 26 | 27 | message GetSquareRequest { 28 | int32 input = 1; 29 | } 30 | 31 | message GetSquareResponse { 32 | int32 output = 1; 33 | } 34 | 35 | message GetEchoRequest { 36 | int32 input = 1; 37 | } 38 | 39 | message GetEchoResponse { 40 | int32 output = 1; 41 | } 42 | 43 | message GetSequenceRequest { 44 | int32 input = 1; 45 | } 46 | 47 | message GetSequenceResponse { 48 | int32 output = 1; 49 | } 50 | 51 | // Provides information about the gRPC server. 52 | service Math { 53 | rpc GetSum(stream GetSumRequest) returns (GetSumResponse); 54 | rpc GetSquare(GetSquareRequest) returns (GetSquareResponse); 55 | rpc GetRunningSum(stream GetSumRequest) returns (stream GetSumResponse); 56 | rpc GetEcho(GetEchoRequest) returns (GetEchoResponse); 57 | rpc GetSequence(GetSequenceRequest) returns (stream GetSequenceResponse); 58 | } 59 | -------------------------------------------------------------------------------- /async_grpc/retry.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 The Cartographer Authors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #include 18 | #include 19 | #include 20 | 21 | #include "async_grpc/retry.h" 22 | #include "glog/logging.h" 23 | 24 | namespace async_grpc { 25 | 26 | RetryStrategy CreateRetryStrategy(RetryIndicator retry_indicator, 27 | RetryDelayCalculator retry_delay_calculator) { 28 | return [retry_indicator, retry_delay_calculator]( 29 | int failed_attempts, const ::grpc::Status &status) { 30 | if (!retry_indicator(failed_attempts, status)) { 31 | return optional(); 32 | } 33 | return optional(retry_delay_calculator(failed_attempts)); 34 | }; 35 | } 36 | 37 | RetryIndicator CreateLimitedRetryIndicator(int max_attempts) { 38 | return [max_attempts](int failed_attempts, const ::grpc::Status &status) { 39 | return failed_attempts < max_attempts; 40 | }; 41 | } 42 | 43 | RetryIndicator CreateUnlimitedRetryIndicator() { 44 | return [](int failed_attempts, const ::grpc::Status &status) { return true; }; 45 | } 46 | 47 | RetryIndicator CreateUnlimitedRetryIndicator( 48 | const std::set<::grpc::StatusCode> &unrecoverable_codes) { 49 | return 50 | [unrecoverable_codes](int failed_attempts, const ::grpc::Status &status) { 51 | return unrecoverable_codes.count(status.error_code()) <= 0; 52 | }; 53 | } 54 | 55 | RetryDelayCalculator CreateBackoffDelayCalculator(Duration min_delay, 56 | float backoff_factor) { 57 | return [min_delay, backoff_factor](int failed_attempts) -> Duration { 58 | CHECK_GE(failed_attempts, 0); 59 | using common::FromSeconds; 60 | using common::ToSeconds; 61 | return FromSeconds(std::pow(backoff_factor, failed_attempts - 1) * 62 | ToSeconds(min_delay)); 63 | }; 64 | } 65 | 66 | RetryDelayCalculator CreateConstantDelayCalculator(Duration delay) { 67 | return [delay](int failed_attempts) -> Duration { return delay; }; 68 | } 69 | 70 | RetryStrategy CreateLimitedBackoffStrategy(Duration min_delay, 71 | float backoff_factor, 72 | int max_attempts) { 73 | return CreateRetryStrategy( 74 | CreateLimitedRetryIndicator(max_attempts), 75 | CreateBackoffDelayCalculator(min_delay, backoff_factor)); 76 | } 77 | 78 | RetryStrategy CreateUnlimitedConstantDelayStrategy(Duration delay) { 79 | return CreateRetryStrategy(CreateUnlimitedRetryIndicator(), 80 | CreateConstantDelayCalculator(delay)); 81 | } 82 | 83 | RetryStrategy CreateUnlimitedConstantDelayStrategy( 84 | Duration delay, const std::set<::grpc::StatusCode> &unrecoverable_codes) { 85 | return CreateRetryStrategy(CreateUnlimitedRetryIndicator(unrecoverable_codes), 86 | CreateConstantDelayCalculator(delay)); 87 | } 88 | 89 | bool RetryWithStrategy(RetryStrategy retry_strategy, 90 | std::function<::grpc::Status()> op, 91 | std::function reset) { 92 | optional delay; 93 | int failed_attemps = 0; 94 | for (;;) { 95 | ::grpc::Status status = op(); 96 | if (status.ok()) { 97 | return true; 98 | } 99 | if (!retry_strategy) { 100 | return false; 101 | } 102 | delay = retry_strategy(++failed_attemps, status); 103 | if (!delay.has_value()) { 104 | break; 105 | } 106 | LOG(INFO) << "Retrying after " 107 | << std::chrono::duration_cast( 108 | delay.value()) 109 | .count() 110 | << " milliseconds."; 111 | std::this_thread::sleep_for(delay.value()); 112 | if (reset) { 113 | reset(); 114 | } 115 | } 116 | return false; 117 | } 118 | 119 | } // namespace async_grpc 120 | -------------------------------------------------------------------------------- /async_grpc/retry.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 The Cartographer Authors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #ifndef CPP_GRPC_RETRY_H 18 | #define CPP_GRPC_RETRY_H 19 | 20 | #include 21 | 22 | #include "async_grpc/common/optional.h" 23 | #include "async_grpc/common/time.h" 24 | #include "grpc++/grpc++.h" 25 | 26 | namespace async_grpc { 27 | 28 | using common::Duration; 29 | using common::optional; 30 | 31 | using RetryStrategy = std::function( 32 | int /* failed_attempts */, const ::grpc::Status &)>; 33 | using RetryIndicator = 34 | std::function; 35 | using RetryDelayCalculator = std::function; 36 | 37 | RetryStrategy CreateRetryStrategy(RetryIndicator retry_indicator, 38 | RetryDelayCalculator retry_delay_calculator); 39 | 40 | RetryIndicator CreateLimitedRetryIndicator(int max_attempts); 41 | RetryIndicator CreateUnlimitedRetryIndicator(); 42 | RetryIndicator CreateUnlimitedRetryIndicator( 43 | const std::set<::grpc::StatusCode> &unrecoverable_codes); 44 | RetryDelayCalculator CreateBackoffDelayCalculator(Duration min_delay, 45 | float backoff_factor); 46 | RetryDelayCalculator CreateConstantDelayCalculator(Duration delay); 47 | RetryStrategy CreateLimitedBackoffStrategy(Duration min_delay, 48 | float backoff_factor, 49 | int max_attempts); 50 | RetryStrategy CreateUnlimitedConstantDelayStrategy(Duration delay); 51 | RetryStrategy CreateUnlimitedConstantDelayStrategy( 52 | Duration delay, const std::set<::grpc::StatusCode> &unrecoverable_codes); 53 | 54 | bool RetryWithStrategy(RetryStrategy retry_strategy, 55 | std::function<::grpc::Status()> op, 56 | std::function reset = nullptr); 57 | 58 | } // namespace async_grpc 59 | 60 | #endif // CPP_GRPC_RETRY_H 61 | -------------------------------------------------------------------------------- /async_grpc/rpc.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 The Cartographer Authors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #include "async_grpc/rpc.h" 18 | #include "async_grpc/service.h" 19 | 20 | #include "async_grpc/common/make_unique.h" 21 | #include "glog/logging.h" 22 | 23 | namespace async_grpc { 24 | namespace { 25 | 26 | // Finishes the gRPC for non-streaming response RPCs, i.e. NORMAL_RPC and 27 | // CLIENT_STREAMING. If no 'msg' is passed, we signal an error to the client as 28 | // the server is not honoring the gRPC call signature. 29 | template 30 | void SendUnaryFinish(ReaderWriter* reader_writer, ::grpc::Status status, 31 | const google::protobuf::Message* msg, 32 | Rpc::EventBase* rpc_event) { 33 | if (msg) { 34 | reader_writer->Finish(*msg, status, rpc_event); 35 | } else { 36 | reader_writer->FinishWithError(status, rpc_event); 37 | } 38 | } 39 | 40 | } // namespace 41 | 42 | void Rpc::CompletionQueueRpcEvent::Handle() { 43 | pending = false; 44 | rpc_ptr->service()->HandleEvent(event, rpc_ptr, ok); 45 | } 46 | 47 | void Rpc::InternalRpcEvent::Handle() { 48 | if (auto rpc_shared = rpc.lock()) { 49 | rpc_shared->service()->HandleEvent(event, rpc_shared.get(), true); 50 | } else { 51 | LOG(WARNING) << "Ignoring stale event."; 52 | } 53 | } 54 | 55 | Rpc::Rpc(int method_index, 56 | ::grpc::ServerCompletionQueue* server_completion_queue, 57 | EventQueue* event_queue, ExecutionContext* execution_context, 58 | const RpcHandlerInfo& rpc_handler_info, Service* service, 59 | WeakPtrFactory weak_ptr_factory) 60 | : method_index_(method_index), 61 | server_completion_queue_(server_completion_queue), 62 | event_queue_(event_queue), 63 | execution_context_(execution_context), 64 | rpc_handler_info_(rpc_handler_info), 65 | service_(service), 66 | weak_ptr_factory_(weak_ptr_factory), 67 | new_connection_event_(Event::NEW_CONNECTION, this), 68 | read_event_(Event::READ, this), 69 | write_event_(Event::WRITE, this), 70 | finish_event_(Event::FINISH, this), 71 | done_event_(Event::DONE, this) { 72 | InitializeReadersAndWriters(rpc_handler_info_.rpc_type); 73 | 74 | // Initialize the prototypical request and response messages. 75 | request_.reset(::google::protobuf::MessageFactory::generated_factory() 76 | ->GetPrototype(rpc_handler_info_.request_descriptor) 77 | ->New()); 78 | response_.reset(::google::protobuf::MessageFactory::generated_factory() 79 | ->GetPrototype(rpc_handler_info_.response_descriptor) 80 | ->New()); 81 | } 82 | 83 | std::unique_ptr Rpc::Clone() { 84 | return common::make_unique( 85 | method_index_, server_completion_queue_, event_queue_, execution_context_, 86 | rpc_handler_info_, service_, weak_ptr_factory_); 87 | } 88 | 89 | void Rpc::OnConnection() { 90 | if (!handler_) { 91 | // Instantiate the handler. 92 | handler_ = rpc_handler_info_.rpc_handler_factory(this, execution_context_); 93 | } 94 | 95 | // For request-streaming RPCs ask the client to start sending requests. 96 | RequestStreamingReadIfNeeded(); 97 | } 98 | 99 | void Rpc::OnRequest() { handler_->OnRequestInternal(request_.get()); } 100 | 101 | void Rpc::OnReadsDone() { handler_->OnReadsDone(); } 102 | 103 | void Rpc::OnFinish() { handler_->OnFinish(); } 104 | 105 | void Rpc::RequestNextMethodInvocation() { 106 | // Ask gRPC to notify us when the connection terminates. 107 | SetRpcEventState(Event::DONE, true); 108 | // TODO(gaschler): Asan reports direct leak of this new from both calls 109 | // StartServing and HandleNewConnection. 110 | server_context_.AsyncNotifyWhenDone(GetRpcEvent(Event::DONE)); 111 | 112 | // Make sure after terminating the connection, gRPC notifies us with this 113 | // event. 114 | SetRpcEventState(Event::NEW_CONNECTION, true); 115 | switch (rpc_handler_info_.rpc_type) { 116 | case ::grpc::internal::RpcMethod::BIDI_STREAMING: 117 | service_->RequestAsyncBidiStreaming( 118 | method_index_, &server_context_, streaming_interface(), 119 | server_completion_queue_, server_completion_queue_, 120 | GetRpcEvent(Event::NEW_CONNECTION)); 121 | break; 122 | case ::grpc::internal::RpcMethod::CLIENT_STREAMING: 123 | service_->RequestAsyncClientStreaming( 124 | method_index_, &server_context_, streaming_interface(), 125 | server_completion_queue_, server_completion_queue_, 126 | GetRpcEvent(Event::NEW_CONNECTION)); 127 | break; 128 | case ::grpc::internal::RpcMethod::NORMAL_RPC: 129 | service_->RequestAsyncUnary( 130 | method_index_, &server_context_, request_.get(), 131 | streaming_interface(), server_completion_queue_, 132 | server_completion_queue_, GetRpcEvent(Event::NEW_CONNECTION)); 133 | break; 134 | case ::grpc::internal::RpcMethod::SERVER_STREAMING: 135 | service_->RequestAsyncServerStreaming( 136 | method_index_, &server_context_, request_.get(), 137 | streaming_interface(), server_completion_queue_, 138 | server_completion_queue_, GetRpcEvent(Event::NEW_CONNECTION)); 139 | break; 140 | } 141 | } 142 | 143 | void Rpc::RequestStreamingReadIfNeeded() { 144 | // For request-streaming RPCs ask the client to start sending requests. 145 | switch (rpc_handler_info_.rpc_type) { 146 | case ::grpc::internal::RpcMethod::BIDI_STREAMING: 147 | case ::grpc::internal::RpcMethod::CLIENT_STREAMING: 148 | SetRpcEventState(Event::READ, true); 149 | async_reader_interface()->Read(request_.get(), GetRpcEvent(Event::READ)); 150 | break; 151 | case ::grpc::internal::RpcMethod::NORMAL_RPC: 152 | case ::grpc::internal::RpcMethod::SERVER_STREAMING: 153 | // For NORMAL_RPC and SERVER_STREAMING we don't need to queue an event, 154 | // since gRPC automatically issues a READ request and places the request 155 | // into the 'Message' we provided to 'RequestAsyncUnary' above. 156 | OnRequest(); 157 | OnReadsDone(); 158 | break; 159 | } 160 | } 161 | 162 | void Rpc::Write(std::unique_ptr<::google::protobuf::Message> message) { 163 | EnqueueMessage(SendItem{std::move(message), ::grpc::Status::OK}); 164 | event_queue_->Push(UniqueEventPtr( 165 | new InternalRpcEvent(Event::WRITE_NEEDED, weak_ptr_factory_(this)))); 166 | } 167 | 168 | void Rpc::Finish(::grpc::Status status) { 169 | EnqueueMessage(SendItem{nullptr /* message */, status}); 170 | event_queue_->Push(UniqueEventPtr( 171 | new InternalRpcEvent(Event::WRITE_NEEDED, weak_ptr_factory_(this)))); 172 | } 173 | 174 | void Rpc::HandleSendQueue() { 175 | SendItem send_item; 176 | { 177 | common::MutexLocker locker(&send_queue_lock_); 178 | if (send_queue_.empty() || IsRpcEventPending(Event::WRITE) || 179 | IsRpcEventPending(Event::FINISH)) { 180 | return; 181 | } 182 | 183 | send_item = std::move(send_queue_.front()); 184 | send_queue_.pop(); 185 | } 186 | if (!send_item.msg || 187 | rpc_handler_info_.rpc_type == ::grpc::internal::RpcMethod::NORMAL_RPC || 188 | rpc_handler_info_.rpc_type == 189 | ::grpc::internal::RpcMethod::CLIENT_STREAMING) { 190 | PerformFinish(std::move(send_item.msg), send_item.status); 191 | return; 192 | } 193 | PerformWrite(std::move(send_item.msg), send_item.status); 194 | } 195 | 196 | ::grpc::internal::ServerAsyncStreamingInterface* Rpc::streaming_interface() { 197 | switch (rpc_handler_info_.rpc_type) { 198 | case ::grpc::internal::RpcMethod::BIDI_STREAMING: 199 | return server_async_reader_writer_.get(); 200 | case ::grpc::internal::RpcMethod::CLIENT_STREAMING: 201 | return server_async_reader_.get(); 202 | case ::grpc::internal::RpcMethod::NORMAL_RPC: 203 | return server_async_response_writer_.get(); 204 | case ::grpc::internal::RpcMethod::SERVER_STREAMING: 205 | return server_async_writer_.get(); 206 | } 207 | LOG(FATAL) << "Never reached."; 208 | } 209 | 210 | ::grpc::internal::AsyncReaderInterface<::google::protobuf::Message>* 211 | Rpc::async_reader_interface() { 212 | switch (rpc_handler_info_.rpc_type) { 213 | case ::grpc::internal::RpcMethod::BIDI_STREAMING: 214 | return server_async_reader_writer_.get(); 215 | case ::grpc::internal::RpcMethod::CLIENT_STREAMING: 216 | return server_async_reader_.get(); 217 | case ::grpc::internal::RpcMethod::NORMAL_RPC: 218 | LOG(FATAL) << "For NORMAL_RPC no streaming reader interface exists."; 219 | case ::grpc::internal::RpcMethod::SERVER_STREAMING: 220 | LOG(FATAL) 221 | << "For SERVER_STREAMING no streaming reader interface exists."; 222 | } 223 | LOG(FATAL) << "Never reached."; 224 | } 225 | 226 | ::grpc::internal::AsyncWriterInterface<::google::protobuf::Message>* 227 | Rpc::async_writer_interface() { 228 | switch (rpc_handler_info_.rpc_type) { 229 | case ::grpc::internal::RpcMethod::BIDI_STREAMING: 230 | return server_async_reader_writer_.get(); 231 | case ::grpc::internal::RpcMethod::CLIENT_STREAMING: 232 | case ::grpc::internal::RpcMethod::NORMAL_RPC: 233 | LOG(FATAL) << "For NORMAL_RPC and CLIENT_STREAMING no streaming writer " 234 | "interface exists."; 235 | break; 236 | case ::grpc::internal::RpcMethod::SERVER_STREAMING: 237 | return server_async_writer_.get(); 238 | } 239 | LOG(FATAL) << "Never reached."; 240 | } 241 | 242 | Rpc::CompletionQueueRpcEvent* Rpc::GetRpcEvent(Event event) { 243 | switch (event) { 244 | case Event::NEW_CONNECTION: 245 | return &new_connection_event_; 246 | case Event::READ: 247 | return &read_event_; 248 | case Event::WRITE_NEEDED: 249 | LOG(FATAL) << "Rpc does not store Event::WRITE_NEEDED."; 250 | break; 251 | case Event::WRITE: 252 | return &write_event_; 253 | case Event::FINISH: 254 | return &finish_event_; 255 | case Event::DONE: 256 | return &done_event_; 257 | } 258 | LOG(FATAL) << "Never reached."; 259 | } 260 | 261 | bool* Rpc::GetRpcEventState(Event event) { 262 | return &GetRpcEvent(event)->pending; 263 | } 264 | 265 | void Rpc::EnqueueMessage(SendItem&& send_item) { 266 | common::MutexLocker locker(&send_queue_lock_); 267 | send_queue_.emplace(std::move(send_item)); 268 | } 269 | 270 | void Rpc::PerformFinish(std::unique_ptr<::google::protobuf::Message> message, 271 | ::grpc::Status status) { 272 | SetRpcEventState(Event::FINISH, true); 273 | switch (rpc_handler_info_.rpc_type) { 274 | case ::grpc::internal::RpcMethod::BIDI_STREAMING: 275 | CHECK(!message); 276 | server_async_reader_writer_->Finish(status, GetRpcEvent(Event::FINISH)); 277 | break; 278 | case ::grpc::internal::RpcMethod::CLIENT_STREAMING: 279 | response_ = std::move(message); 280 | SendUnaryFinish(server_async_reader_.get(), status, response_.get(), 281 | GetRpcEvent(Event::FINISH)); 282 | break; 283 | case ::grpc::internal::RpcMethod::NORMAL_RPC: 284 | response_ = std::move(message); 285 | SendUnaryFinish(server_async_response_writer_.get(), status, 286 | response_.get(), GetRpcEvent(Event::FINISH)); 287 | break; 288 | case ::grpc::internal::RpcMethod::SERVER_STREAMING: 289 | CHECK(!message); 290 | server_async_writer_->Finish(status, GetRpcEvent(Event::FINISH)); 291 | break; 292 | } 293 | } 294 | 295 | void Rpc::PerformWrite(std::unique_ptr<::google::protobuf::Message> message, 296 | ::grpc::Status status) { 297 | CHECK(message) << "PerformWrite must be called with a non-null message"; 298 | CHECK_NE(rpc_handler_info_.rpc_type, ::grpc::internal::RpcMethod::NORMAL_RPC); 299 | CHECK_NE(rpc_handler_info_.rpc_type, 300 | ::grpc::internal::RpcMethod::CLIENT_STREAMING); 301 | SetRpcEventState(Event::WRITE, true); 302 | response_ = std::move(message); 303 | async_writer_interface()->Write(*response_, GetRpcEvent(Event::WRITE)); 304 | } 305 | 306 | void Rpc::SetRpcEventState(Event event, bool pending) { 307 | // TODO(gaschler): Since the only usage is setting this true at creation, 308 | // consider removing this method. 309 | *GetRpcEventState(event) = pending; 310 | } 311 | 312 | bool Rpc::IsRpcEventPending(Event event) { return *GetRpcEventState(event); } 313 | 314 | bool Rpc::IsAnyEventPending() { 315 | return IsRpcEventPending(Rpc::Event::DONE) || 316 | IsRpcEventPending(Rpc::Event::READ) || 317 | IsRpcEventPending(Rpc::Event::WRITE) || 318 | IsRpcEventPending(Rpc::Event::FINISH); 319 | } 320 | 321 | std::weak_ptr Rpc::GetWeakPtr() { return weak_ptr_factory_(this); } 322 | 323 | ActiveRpcs::ActiveRpcs() : lock_() {} 324 | 325 | void Rpc::InitializeReadersAndWriters( 326 | ::grpc::internal::RpcMethod::RpcType rpc_type) { 327 | switch (rpc_type) { 328 | case ::grpc::internal::RpcMethod::BIDI_STREAMING: 329 | server_async_reader_writer_ = 330 | common::make_unique<::grpc::ServerAsyncReaderWriter< 331 | google::protobuf::Message, google::protobuf::Message>>( 332 | &server_context_); 333 | break; 334 | case ::grpc::internal::RpcMethod::CLIENT_STREAMING: 335 | server_async_reader_ = common::make_unique<::grpc::ServerAsyncReader< 336 | google::protobuf::Message, google::protobuf::Message>>( 337 | &server_context_); 338 | break; 339 | case ::grpc::internal::RpcMethod::NORMAL_RPC: 340 | server_async_response_writer_ = common::make_unique< 341 | ::grpc::ServerAsyncResponseWriter>( 342 | &server_context_); 343 | break; 344 | case ::grpc::internal::RpcMethod::SERVER_STREAMING: 345 | server_async_writer_ = common::make_unique< 346 | ::grpc::ServerAsyncWriter>( 347 | &server_context_); 348 | break; 349 | } 350 | } 351 | 352 | ActiveRpcs::~ActiveRpcs() { 353 | common::MutexLocker locker(&lock_); 354 | if (!rpcs_.empty()) { 355 | LOG(FATAL) << "RPCs still in flight!"; 356 | } 357 | } 358 | 359 | std::shared_ptr ActiveRpcs::Add(std::unique_ptr rpc) { 360 | common::MutexLocker locker(&lock_); 361 | std::shared_ptr shared_ptr_rpc = std::move(rpc); 362 | const auto result = rpcs_.emplace(shared_ptr_rpc.get(), shared_ptr_rpc); 363 | CHECK(result.second) << "RPC already active."; 364 | return shared_ptr_rpc; 365 | } 366 | 367 | bool ActiveRpcs::Remove(Rpc* rpc) { 368 | common::MutexLocker locker(&lock_); 369 | auto it = rpcs_.find(rpc); 370 | if (it != rpcs_.end()) { 371 | rpcs_.erase(it); 372 | return true; 373 | } 374 | return false; 375 | } 376 | 377 | Rpc::WeakPtrFactory ActiveRpcs::GetWeakPtrFactory() { 378 | return [this](Rpc* rpc) { return GetWeakPtr(rpc); }; 379 | } 380 | 381 | std::weak_ptr ActiveRpcs::GetWeakPtr(Rpc* rpc) { 382 | common::MutexLocker locker(&lock_); 383 | auto it = rpcs_.find(rpc); 384 | CHECK(it != rpcs_.end()); 385 | return it->second; 386 | } 387 | 388 | } // namespace async_grpc 389 | -------------------------------------------------------------------------------- /async_grpc/rpc.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 The Cartographer Authors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #ifndef CPP_GRPC_RPC_H 18 | #define CPP_GRPC_RPC_H 19 | 20 | #include 21 | #include 22 | #include 23 | 24 | #include "async_grpc/common/blocking_queue.h" 25 | #include "async_grpc/common/mutex.h" 26 | #include "async_grpc/execution_context.h" 27 | #include "async_grpc/rpc_handler_interface.h" 28 | #include "google/protobuf/message.h" 29 | #include "grpc++/grpc++.h" 30 | #include "grpc++/impl/codegen/async_stream.h" 31 | #include "grpc++/impl/codegen/async_unary_call.h" 32 | #include "grpc++/impl/codegen/proto_utils.h" 33 | #include "grpc++/impl/codegen/service_type.h" 34 | 35 | namespace async_grpc { 36 | 37 | class Service; 38 | // TODO(cschuet): Add a unittest that tests the logic of this class. 39 | class Rpc { 40 | public: 41 | using WeakPtrFactory = std::function(Rpc*)>; 42 | enum class Event { 43 | NEW_CONNECTION = 0, 44 | READ, 45 | WRITE_NEEDED, 46 | WRITE, 47 | FINISH, 48 | DONE 49 | }; 50 | 51 | struct EventBase { 52 | explicit EventBase(Event event) : event(event) {} 53 | virtual ~EventBase(){}; 54 | virtual void Handle() = 0; 55 | 56 | const Event event; 57 | }; 58 | 59 | class EventDeleter { 60 | public: 61 | enum Action { DELETE = 0, DO_NOT_DELETE }; 62 | 63 | // The default action 'DELETE' is used implicitly, for instance for a 64 | // new UniqueEventPtr or a UniqueEventPtr that is created by 65 | // 'return nullptr'. 66 | EventDeleter() : action_(DELETE) {} 67 | explicit EventDeleter(Action action) : action_(action) {} 68 | void operator()(EventBase* e) { 69 | if (e != nullptr && action_ == DELETE) { 70 | delete e; 71 | } 72 | } 73 | 74 | private: 75 | Action action_; 76 | }; 77 | 78 | using UniqueEventPtr = std::unique_ptr; 79 | using EventQueue = common::BlockingQueue; 80 | 81 | // Flows through gRPC's CompletionQueue and then our EventQueue. 82 | struct CompletionQueueRpcEvent : public EventBase { 83 | CompletionQueueRpcEvent(Event event, Rpc* rpc) 84 | : EventBase(event), rpc_ptr(rpc), ok(false), pending(false) {} 85 | void PushToEventQueue() { 86 | rpc_ptr->event_queue()->Push( 87 | UniqueEventPtr(this, EventDeleter(EventDeleter::DO_NOT_DELETE))); 88 | } 89 | void Handle() override; 90 | 91 | Rpc* rpc_ptr; 92 | bool ok; 93 | bool pending; 94 | }; 95 | 96 | // Flows only through our EventQueue. 97 | struct InternalRpcEvent : public EventBase { 98 | InternalRpcEvent(Event event, std::weak_ptr rpc) 99 | : EventBase(event), rpc(rpc) {} 100 | void Handle() override; 101 | 102 | std::weak_ptr rpc; 103 | }; 104 | 105 | Rpc(int method_index, ::grpc::ServerCompletionQueue* server_completion_queue, 106 | EventQueue* event_queue, ExecutionContext* execution_context, 107 | const RpcHandlerInfo& rpc_handler_info, Service* service, 108 | WeakPtrFactory weak_ptr_factory); 109 | std::unique_ptr Clone(); 110 | void OnConnection(); 111 | void OnRequest(); 112 | void OnReadsDone(); 113 | void OnFinish(); 114 | void RequestNextMethodInvocation(); 115 | void RequestStreamingReadIfNeeded(); 116 | void HandleSendQueue(); 117 | void Write(std::unique_ptr<::google::protobuf::Message> message); 118 | void Finish(::grpc::Status status); 119 | Service* service() { return service_; } 120 | bool IsRpcEventPending(Event event); 121 | bool IsAnyEventPending(); 122 | void SetEventQueue(EventQueue* event_queue) { event_queue_ = event_queue; } 123 | EventQueue* event_queue() { return event_queue_; } 124 | std::weak_ptr GetWeakPtr(); 125 | RpcHandlerInterface* handler() { return handler_.get(); } 126 | 127 | private: 128 | struct SendItem { 129 | std::unique_ptr msg; 130 | ::grpc::Status status; 131 | }; 132 | 133 | Rpc(const Rpc&) = delete; 134 | Rpc& operator=(const Rpc&) = delete; 135 | void InitializeReadersAndWriters( 136 | ::grpc::internal::RpcMethod::RpcType rpc_type); 137 | CompletionQueueRpcEvent* GetRpcEvent(Event event); 138 | bool* GetRpcEventState(Event event); 139 | void SetRpcEventState(Event event, bool pending); 140 | void EnqueueMessage(SendItem&& send_item); 141 | void PerformFinish(std::unique_ptr<::google::protobuf::Message> message, 142 | ::grpc::Status status); 143 | void PerformWrite(std::unique_ptr<::google::protobuf::Message> message, 144 | ::grpc::Status status); 145 | 146 | ::grpc::internal::AsyncReaderInterface<::google::protobuf::Message>* 147 | async_reader_interface(); 148 | ::grpc::internal::AsyncWriterInterface<::google::protobuf::Message>* 149 | async_writer_interface(); 150 | 151 | ::grpc::internal::ServerAsyncStreamingInterface* streaming_interface(); 152 | 153 | int method_index_; 154 | ::grpc::ServerCompletionQueue* server_completion_queue_; 155 | EventQueue* event_queue_; 156 | ExecutionContext* execution_context_; 157 | RpcHandlerInfo rpc_handler_info_; 158 | Service* service_; 159 | WeakPtrFactory weak_ptr_factory_; 160 | ::grpc::ServerContext server_context_; 161 | 162 | CompletionQueueRpcEvent new_connection_event_; 163 | CompletionQueueRpcEvent read_event_; 164 | CompletionQueueRpcEvent write_event_; 165 | CompletionQueueRpcEvent finish_event_; 166 | CompletionQueueRpcEvent done_event_; 167 | 168 | std::unique_ptr request_; 169 | std::unique_ptr response_; 170 | 171 | std::unique_ptr handler_; 172 | 173 | std::unique_ptr<::grpc::ServerAsyncResponseWriter> 174 | server_async_response_writer_; 175 | std::unique_ptr<::grpc::ServerAsyncReader> 177 | server_async_reader_; 178 | std::unique_ptr<::grpc::ServerAsyncReaderWriter> 180 | server_async_reader_writer_; 181 | std::unique_ptr<::grpc::ServerAsyncWriter> 182 | server_async_writer_; 183 | 184 | common::Mutex send_queue_lock_; 185 | std::queue send_queue_; 186 | }; 187 | 188 | using EventQueue = Rpc::EventQueue; 189 | 190 | // This class keeps track of all in-flight RPCs for a 'Service'. Make sure that 191 | // all RPCs have been terminated and removed from this object before it goes out 192 | // of scope. 193 | class ActiveRpcs { 194 | public: 195 | ActiveRpcs(); 196 | ~ActiveRpcs() EXCLUDES(lock_); 197 | 198 | std::shared_ptr Add(std::unique_ptr rpc) EXCLUDES(lock_); 199 | bool Remove(Rpc* rpc) EXCLUDES(lock_); 200 | Rpc::WeakPtrFactory GetWeakPtrFactory(); 201 | 202 | private: 203 | std::weak_ptr GetWeakPtr(Rpc* rpc); 204 | 205 | common::Mutex lock_; 206 | std::map> rpcs_; 207 | }; 208 | 209 | } // namespace async_grpc 210 | 211 | #endif // CPP_GRPC_RPC_H 212 | -------------------------------------------------------------------------------- /async_grpc/rpc_handler.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 The Cartographer Authors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #ifndef CPP_GRPC_RPC_HANDLER_H 18 | #define CPP_GRPC_RPC_HANDLER_H 19 | 20 | #include "async_grpc/execution_context.h" 21 | #include "async_grpc/rpc.h" 22 | #include "async_grpc/rpc_handler_interface.h" 23 | #include "async_grpc/rpc_service_method_traits.h" 24 | #include "async_grpc/span.h" 25 | #include "glog/logging.h" 26 | #include "google/protobuf/message.h" 27 | #include "grpc++/grpc++.h" 28 | #if BUILD_TRACING 29 | #include "async_grpc/opencensus_span.h" 30 | #endif 31 | 32 | namespace async_grpc { 33 | 34 | template 35 | class RpcHandler : public RpcHandlerInterface { 36 | public: 37 | using RpcServiceMethod = RpcServiceMethodTraits; 38 | using RequestType = typename RpcServiceMethod::RequestType; 39 | using ResponseType = typename RpcServiceMethod::ResponseType; 40 | 41 | class Writer { 42 | public: 43 | explicit Writer(std::weak_ptr rpc) : rpc_(std::move(rpc)) {} 44 | bool Write(std::unique_ptr message) const { 45 | if (auto rpc = rpc_.lock()) { 46 | rpc->Write(std::move(message)); 47 | return true; 48 | } 49 | return false; 50 | } 51 | bool WritesDone() const { 52 | if (auto rpc = rpc_.lock()) { 53 | rpc->Finish(::grpc::Status::OK); 54 | return true; 55 | } 56 | return false; 57 | } 58 | bool Finish(const ::grpc::Status& status) const { 59 | if (auto rpc = rpc_.lock()) { 60 | rpc->Finish(status); 61 | auto* span = rpc->handler()->trace_span(); 62 | if (span) { 63 | span->SetStatus(status); 64 | } 65 | return true; 66 | } 67 | return false; 68 | } 69 | 70 | private: 71 | const std::weak_ptr rpc_; 72 | }; 73 | 74 | #if BUILD_TRACING 75 | RpcHandler() 76 | : span_( 77 | OpencensusSpan::StartSpan(RpcServiceMethodConcept::MethodName())) {} 78 | virtual ~RpcHandler() { span_->End(); } 79 | #endif 80 | 81 | Span* trace_span() override { return span_.get(); } 82 | void SetExecutionContext(ExecutionContext* execution_context) override { 83 | execution_context_ = execution_context; 84 | } 85 | void SetRpc(Rpc* rpc) override { rpc_ = rpc; } 86 | void OnRequestInternal(const ::google::protobuf::Message* request) override { 87 | DCHECK(dynamic_cast(request)); 88 | OnRequest(static_cast(*request)); 89 | } 90 | virtual void OnRequest(const RequestType& request) = 0; 91 | void Finish(::grpc::Status status) { 92 | rpc_->Finish(status); 93 | #if BUILD_TRACING 94 | span_->SetStatus(status); 95 | #endif 96 | } 97 | void Send(std::unique_ptr response) { 98 | rpc_->Write(std::move(response)); 99 | } 100 | template 101 | ExecutionContext::Synchronized GetContext() { 102 | return {execution_context_->lock(), execution_context_}; 103 | } 104 | template 105 | T* GetUnsynchronizedContext() { 106 | return dynamic_cast(execution_context_); 107 | } 108 | Writer GetWriter() { return Writer(rpc_->GetWeakPtr()); } 109 | 110 | private: 111 | Rpc* rpc_; 112 | ExecutionContext* execution_context_; 113 | std::unique_ptr span_; 114 | }; 115 | 116 | } // namespace async_grpc 117 | 118 | #endif // CPP_GRPC_RPC_HANDLER_H 119 | -------------------------------------------------------------------------------- /async_grpc/rpc_handler_interface.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 The Cartographer Authors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #ifndef CPP_GRPC_RPC_HANDLER_INTERFACE_H_ 18 | #define CPP_GRPC_RPC_HANDLER_INTERFACE_H_ 19 | 20 | #include "async_grpc/common/make_unique.h" 21 | #include "async_grpc/execution_context.h" 22 | #include "async_grpc/span.h" 23 | #include "google/protobuf/message.h" 24 | #include "grpc++/grpc++.h" 25 | 26 | namespace async_grpc { 27 | 28 | class Rpc; 29 | class RpcHandlerInterface { 30 | public: 31 | virtual ~RpcHandlerInterface() = default; 32 | virtual void SetExecutionContext(ExecutionContext* execution_context) = 0; 33 | virtual void SetRpc(Rpc* rpc) = 0; 34 | virtual void Initialize(){}; 35 | virtual void OnRequestInternal( 36 | const ::google::protobuf::Message* request) = 0; 37 | virtual void OnReadsDone(){}; 38 | virtual void OnFinish(){}; 39 | virtual Span* trace_span() = 0; 40 | template 41 | static std::unique_ptr Instantiate() { 42 | return common::make_unique(); 43 | } 44 | }; 45 | 46 | using RpcHandlerFactory = std::function( 47 | Rpc*, ExecutionContext*)>; 48 | 49 | struct RpcHandlerInfo { 50 | const google::protobuf::Descriptor* request_descriptor; 51 | const google::protobuf::Descriptor* response_descriptor; 52 | const RpcHandlerFactory rpc_handler_factory; 53 | const ::grpc::internal::RpcMethod::RpcType rpc_type; 54 | const std::string fully_qualified_name; 55 | }; 56 | 57 | } // namespace async_grpc 58 | 59 | #endif // CPP_GRPC_RPC_HANDLER_INTERFACE_H_ 60 | -------------------------------------------------------------------------------- /async_grpc/rpc_service_method_traits.h: -------------------------------------------------------------------------------- 1 | #ifndef CPP_GRPC_RPC_SERVICE_METHOD_TRAITS_H 2 | #define CPP_GRPC_RPC_SERVICE_METHOD_TRAITS_H 3 | 4 | #include "async_grpc/type_traits.h" 5 | #include "google/protobuf/message.h" 6 | 7 | namespace async_grpc { 8 | 9 | DEFINE_HAS_SIGNATURE(has_service_method_name, T::MethodName, 10 | const char* (*)(void)); 11 | 12 | DEFINE_HAS_MEMBER_TYPE(has_incoming_type, IncomingType); 13 | DEFINE_HAS_MEMBER_TYPE(has_outgoing_type, OutgoingType); 14 | 15 | // The RPC service method concept describes types from which properties of an 16 | // RPC service can be inferred. The type RpcServiceMethod satisfies the RPC 17 | // service concept if: 18 | // 1) it provides a static member function ' const char* MethodName()' 19 | // 2) it provides an 'IncomingType' typedef; i.e. the proto message passed to 20 | // the service method 21 | // 3) it provides an 'OutgoingType' typedef; i.e. the proto message passed to 22 | // the service method 23 | // Note: the IncomingType and OutgoingType specified above may be wrapped (or 24 | // tagged) by async_grpc::Stream. 25 | template 26 | struct RpcServiceMethodTraits { 27 | static_assert(has_service_method_name::value, 28 | "The RPC service method concept must provide a static member " 29 | "'const char* MethodName()'."); 30 | 31 | static_assert(has_incoming_type::value, 32 | "The RPC service method concept must provide an IncomingType."); 33 | 34 | static_assert(has_outgoing_type::value, 35 | "The RPC service method concept must provide an OutgoingType."); 36 | 37 | // Returns the fully qualified name of the gRPC method this handler is 38 | // implementing. The fully qualified name has the structure 39 | // '/<>/<>', where the service name is the 40 | // fully qualified proto package name of the service and method name the 41 | // name of the method as defined in the service definition of the proto. 42 | static constexpr const char* MethodName() { 43 | return RpcServiceMethodConcept::MethodName(); 44 | } 45 | 46 | // An object derived from ::google::protobuf::Message which is passed to a 47 | // specific service method. 48 | using RequestType = 49 | StripStream; 50 | 51 | // An object derived from ::google::protobuf::Message which is returned from a 52 | // specific service method. 53 | using ResponseType = 54 | StripStream; 55 | 56 | static_assert( 57 | std::is_base_of<::google::protobuf::Message, RequestType>::value, 58 | "The RPC request type must be derived from ::google::protobuf::Message."); 59 | 60 | static_assert( 61 | std::is_base_of<::google::protobuf::Message, ResponseType>::value, 62 | "The RPC response type must be derived from " 63 | "::google::protobuf::Message."); 64 | 65 | // The streaming type of the service method. See also 66 | // ::grpc::internal::RpcMethod. 67 | static constexpr auto StreamType = 68 | RpcType::value; 70 | }; 71 | 72 | } // namespace async_grpc 73 | 74 | #endif // CPP_GRPC_RPC_SERVICE_METHOD_TRAITS_H 75 | -------------------------------------------------------------------------------- /async_grpc/server.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 The Cartographer Authors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #include "async_grpc/server.h" 18 | 19 | #include "glog/logging.h" 20 | #if BUILD_TRACING 21 | #include "opencensus/exporters/trace/stackdriver/stackdriver_exporter.h" 22 | #include "opencensus/trace/trace_config.h" 23 | #endif 24 | 25 | namespace async_grpc { 26 | namespace { 27 | 28 | const common::Duration kPopEventTimeout = common::FromMilliseconds(100); 29 | constexpr unsigned int kDefaultTracingMaxAttributes = 128; 30 | constexpr unsigned int kDefaultTracingMaxAnnotations = 128; 31 | constexpr unsigned int kDefaultTracingMaxMessageEvents = 128; 32 | constexpr unsigned int kDefaultTracingMaxLinks = 128; 33 | 34 | } // namespace 35 | 36 | void Server::Builder::SetNumGrpcThreads(const size_t num_grpc_threads) { 37 | options_.num_grpc_threads = num_grpc_threads; 38 | } 39 | 40 | void Server::Builder::SetNumEventThreads(const std::size_t num_event_threads) { 41 | options_.num_event_threads = num_event_threads; 42 | } 43 | 44 | void Server::Builder::SetServerAddress(const std::string& server_address) { 45 | options_.server_address = server_address; 46 | } 47 | 48 | void Server::Builder::SetMaxReceiveMessageSize(int max_receive_message_size) { 49 | CHECK_GT(max_receive_message_size, 0) << "max_receive_message_size must be larger than 0."; 50 | options_.max_receive_message_size = max_receive_message_size; 51 | } 52 | 53 | void Server::Builder::SetMaxSendMessageSize(int max_send_message_size) { 54 | CHECK_GT(max_send_message_size, 0) << "max_send_message_size must be larger than 0."; 55 | options_.max_send_message_size = max_send_message_size; 56 | } 57 | 58 | void Server::Builder::EnableTracing() { 59 | #if BUILD_TRACING 60 | options_.enable_tracing = true; 61 | #else 62 | LOG(FATAL) << "Enable tracing support by compiling with -DBUILD_TRACING=1."; 63 | #endif 64 | } 65 | 66 | void Server::Builder::DisableTracing() { 67 | options_.enable_tracing = false; 68 | } 69 | 70 | void Server::Builder::SetTracingSamplerProbability(double tracing_sampler_probability) { 71 | options_.tracing_sampler_probability = tracing_sampler_probability; 72 | } 73 | 74 | void Server::Builder::SetTracingTaskName(const std::string& tracing_task_name) { 75 | options_.tracing_task_name = tracing_task_name; 76 | } 77 | 78 | void Server::Builder::SetTracingGcpProjectId(const std::string& tracing_gcp_project_id) { 79 | options_.tracing_gcp_project_id = tracing_gcp_project_id; 80 | } 81 | 82 | std::tuple Server::Builder::ParseMethodFullName( 83 | const std::string& method_full_name) { 84 | CHECK(method_full_name.at(0) == '/') << "Invalid method name."; 85 | std::stringstream stream(method_full_name.substr(1)); 86 | std::string service_full_name; 87 | std::getline(stream, service_full_name, '/'); 88 | std::string method_name; 89 | std::getline(stream, method_name, '/'); 90 | CHECK(!service_full_name.empty() && !method_name.empty()); 91 | return std::make_tuple(service_full_name, method_name); 92 | } 93 | 94 | std::unique_ptr Server::Builder::Build() { 95 | std::unique_ptr server(new Server(options_)); 96 | for (const auto& service_handlers : rpc_handlers_) { 97 | server->AddService(service_handlers.first, service_handlers.second); 98 | } 99 | return server; 100 | } 101 | 102 | Server::Server(const Options& options) : options_(options) { 103 | server_builder_.AddListeningPort(options_.server_address, 104 | ::grpc::InsecureServerCredentials()); 105 | 106 | // Set max message sizes. 107 | server_builder_.SetMaxReceiveMessageSize(options.max_receive_message_size); 108 | server_builder_.SetMaxSendMessageSize(options.max_send_message_size); 109 | 110 | // Set up event queue threads. 111 | event_queue_threads_ = 112 | std::vector(options_.num_event_threads); 113 | 114 | // Set up completion queues threads. 115 | for (size_t i = 0; i < options_.num_grpc_threads; ++i) { 116 | completion_queue_threads_.emplace_back( 117 | server_builder_.AddCompletionQueue()); 118 | } 119 | } 120 | 121 | void Server::AddService( 122 | const std::string& service_name, 123 | const std::map& rpc_handler_infos) { 124 | // Instantiate and register service. 125 | const auto result = services_.emplace( 126 | std::piecewise_construct, std::make_tuple(service_name), 127 | std::make_tuple(service_name, rpc_handler_infos, 128 | [this]() { return SelectNextEventQueueRoundRobin(); })); 129 | CHECK(result.second) << "A service named " << service_name 130 | << " already exists."; 131 | server_builder_.RegisterService(&result.first->second); 132 | } 133 | 134 | void Server::RunCompletionQueue( 135 | ::grpc::ServerCompletionQueue* completion_queue) { 136 | bool ok; 137 | void* tag; 138 | while (completion_queue->Next(&tag, &ok)) { 139 | auto* rpc_event = static_cast(tag); 140 | rpc_event->ok = ok; 141 | rpc_event->PushToEventQueue(); 142 | } 143 | } 144 | 145 | EventQueue* Server::SelectNextEventQueueRoundRobin() { 146 | common::MutexLocker locker(¤t_event_queue_id_lock_); 147 | current_event_queue_id_ = 148 | (current_event_queue_id_ + 1) % options_.num_event_threads; 149 | return event_queue_threads_.at(current_event_queue_id_).event_queue(); 150 | } 151 | 152 | void Server::RunEventQueue(EventQueue* event_queue) { 153 | while (!shutting_down_) { 154 | Rpc::UniqueEventPtr rpc_event = 155 | event_queue->PopWithTimeout(kPopEventTimeout); 156 | if (rpc_event) { 157 | rpc_event->Handle(); 158 | } 159 | } 160 | 161 | // Finish processing the rest of the items. 162 | while (Rpc::UniqueEventPtr rpc_event = 163 | event_queue->PopWithTimeout(kPopEventTimeout)) { 164 | rpc_event->Handle(); 165 | } 166 | } 167 | 168 | void Server::Start() { 169 | #if BUILD_TRACING 170 | if (options_.enable_tracing) { 171 | opencensus::exporters::trace::StackdriverExporter::Register( 172 | options_.tracing_gcp_project_id); 173 | opencensus::trace::TraceConfig::SetCurrentTraceParams( 174 | {kDefaultTracingMaxAttributes, kDefaultTracingMaxAnnotations, 175 | kDefaultTracingMaxMessageEvents, kDefaultTracingMaxLinks, 176 | opencensus::trace::ProbabilitySampler( 177 | options_.tracing_sampler_probability)}); 178 | } 179 | #endif 180 | 181 | 182 | // Start the gRPC server process. 183 | server_ = server_builder_.BuildAndStart(); 184 | 185 | // Start serving all services on all completion queues. 186 | for (auto& service : services_) { 187 | service.second.StartServing(completion_queue_threads_, 188 | execution_context_.get()); 189 | } 190 | 191 | // Start threads to process all event queues. 192 | for (auto& event_queue_thread : event_queue_threads_) { 193 | event_queue_thread.Start( 194 | [this](EventQueue* event_queue) { RunEventQueue(event_queue); }); 195 | } 196 | 197 | // Start threads to process all completion queues. 198 | for (auto& completion_queue_threads : completion_queue_threads_) { 199 | completion_queue_threads.Start( 200 | [this](::grpc::ServerCompletionQueue* completion_queue) { 201 | RunCompletionQueue(completion_queue); 202 | }); 203 | } 204 | } 205 | 206 | void Server::WaitForShutdown() { 207 | if (!server_) { 208 | return; 209 | } 210 | 211 | server_->Wait(); 212 | } 213 | 214 | void Server::Shutdown() { 215 | LOG(INFO) << "Shutting down server."; 216 | shutting_down_ = true; 217 | 218 | // Tell the services to stop serving RPCs. 219 | for (auto& service : services_) { 220 | service.second.StopServing(); 221 | } 222 | 223 | // Shut down the gRPC server waiting for RPCs to finish until the hard 224 | // deadline; then force a shutdown. 225 | server_->Shutdown(); 226 | 227 | // Shut down the server completion queues and wait for the processing threads 228 | // to join. 229 | for (auto& completion_queue_threads : completion_queue_threads_) { 230 | completion_queue_threads.Shutdown(); 231 | } 232 | 233 | for (auto& event_queue_thread : event_queue_threads_) { 234 | event_queue_thread.Shutdown(); 235 | } 236 | 237 | LOG(INFO) << "Shutdown complete."; 238 | } 239 | 240 | void Server::SetExecutionContext( 241 | std::unique_ptr execution_context) { 242 | // After the server has been started the 'ExecutionHandle' cannot be changed 243 | // anymore. 244 | CHECK(!server_); 245 | execution_context_ = std::move(execution_context); 246 | } 247 | 248 | } // namespace async_grpc 249 | -------------------------------------------------------------------------------- /async_grpc/server.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 The Cartographer Authors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #ifndef CPP_GRPC_SERVER_H 18 | #define CPP_GRPC_SERVER_H 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | #include "async_grpc/common/make_unique.h" 27 | #include "async_grpc/completion_queue_thread.h" 28 | #include "async_grpc/event_queue_thread.h" 29 | #include "async_grpc/execution_context.h" 30 | #include "async_grpc/rpc_handler.h" 31 | #include "async_grpc/rpc_service_method_traits.h" 32 | #include "async_grpc/service.h" 33 | 34 | #include "grpc++/grpc++.h" 35 | 36 | namespace async_grpc { 37 | namespace { 38 | 39 | constexpr int kDefaultMaxMessageSize = 10 * 1024 * 1024; // 10 MB 40 | constexpr double kDefaultTracingSamplerProbability = 0.01; // 1 Percent 41 | 42 | } // namespace 43 | 44 | class Server { 45 | protected: 46 | // All options that configure server behaviour such as number of threads, 47 | // ports etc. 48 | struct Options { 49 | Options() = default; 50 | Options(size_t num_grpc_threads, size_t num_event_threads, 51 | const std::string& server_address) 52 | : num_grpc_threads(num_grpc_threads), 53 | num_event_threads(num_event_threads), 54 | server_address(server_address) {} 55 | size_t num_grpc_threads; 56 | size_t num_event_threads; 57 | std::string server_address; 58 | int max_receive_message_size = kDefaultMaxMessageSize; 59 | int max_send_message_size = kDefaultMaxMessageSize; 60 | bool enable_tracing = false; 61 | double tracing_sampler_probability = kDefaultTracingSamplerProbability; 62 | std::string tracing_task_name; 63 | std::string tracing_gcp_project_id; 64 | }; 65 | 66 | public: 67 | // This 'Builder' is the only way to construct a 'Server'. 68 | class Builder { 69 | public: 70 | Builder() = default; 71 | 72 | std::unique_ptr Build(); 73 | void SetNumGrpcThreads(std::size_t num_grpc_threads); 74 | void SetNumEventThreads(std::size_t num_event_threads); 75 | void SetServerAddress(const std::string& server_address); 76 | void SetMaxReceiveMessageSize(int max_receive_message_size); 77 | void SetMaxSendMessageSize(int max_send_message_size); 78 | void EnableTracing(); 79 | void DisableTracing(); 80 | void SetTracingSamplerProbability(double tracing_sampler_probability); 81 | void SetTracingTaskName(const std::string& tracing_task_name); 82 | void SetTracingGcpProjectId(const std::string& tracing_gcp_project_id); 83 | 84 | template 85 | void RegisterHandler() { 86 | using RpcServiceMethod = typename RpcHandlerType::RpcServiceMethod; 87 | using RequestType = typename RpcServiceMethod::RequestType; 88 | using ResponseType = typename RpcServiceMethod::ResponseType; 89 | 90 | std::string method_full_name = RpcServiceMethod::MethodName(); 91 | std::string service_full_name; 92 | std::string method_name; 93 | std::tie(service_full_name, method_name) = 94 | ParseMethodFullName(method_full_name); 95 | CheckHandlerCompatibility(service_full_name, method_name); 96 | rpc_handlers_[service_full_name].emplace( 97 | method_name, 98 | RpcHandlerInfo{ 99 | RequestType::default_instance().GetDescriptor(), 100 | ResponseType::default_instance().GetDescriptor(), 101 | [](Rpc* const rpc, ExecutionContext* const execution_context) { 102 | std::unique_ptr rpc_handler = 103 | common::make_unique(); 104 | rpc_handler->SetRpc(rpc); 105 | rpc_handler->SetExecutionContext(execution_context); 106 | rpc_handler->Initialize(); 107 | return rpc_handler; 108 | }, 109 | RpcServiceMethod::StreamType, method_full_name}); 110 | } 111 | static std::tuple 113 | ParseMethodFullName(const std::string& method_full_name); 114 | 115 | private: 116 | using ServiceInfo = std::map; 117 | 118 | template 119 | void CheckHandlerCompatibility(const std::string& service_full_name, 120 | const std::string& method_name) { 121 | using RpcServiceMethod = typename RpcHandlerType::RpcServiceMethod; 122 | using RequestType = typename RpcServiceMethod::RequestType; 123 | using ResponseType = typename RpcServiceMethod::ResponseType; 124 | 125 | const auto* pool = google::protobuf::DescriptorPool::generated_pool(); 126 | const auto* service = pool->FindServiceByName(service_full_name); 127 | CHECK(service) << "Unknown service " << service_full_name; 128 | const auto* method_descriptor = service->FindMethodByName(method_name); 129 | CHECK(method_descriptor) << "Unknown method " << method_name 130 | << " in service " << service_full_name; 131 | const auto* request_type = method_descriptor->input_type(); 132 | CHECK_EQ(RequestType::default_instance().GetDescriptor(), request_type); 133 | const auto* response_type = method_descriptor->output_type(); 134 | CHECK_EQ(ResponseType::default_instance().GetDescriptor(), response_type); 135 | const auto rpc_type = RpcServiceMethod::StreamType; 136 | switch (rpc_type) { 137 | case ::grpc::internal::RpcMethod::NORMAL_RPC: 138 | CHECK(!method_descriptor->client_streaming()); 139 | CHECK(!method_descriptor->server_streaming()); 140 | break; 141 | case ::grpc::internal::RpcMethod::CLIENT_STREAMING: 142 | CHECK(method_descriptor->client_streaming()); 143 | CHECK(!method_descriptor->server_streaming()); 144 | break; 145 | case ::grpc::internal::RpcMethod::SERVER_STREAMING: 146 | CHECK(!method_descriptor->client_streaming()); 147 | CHECK(method_descriptor->server_streaming()); 148 | break; 149 | case ::grpc::internal::RpcMethod::BIDI_STREAMING: 150 | CHECK(method_descriptor->client_streaming()); 151 | CHECK(method_descriptor->server_streaming()); 152 | break; 153 | } 154 | } 155 | 156 | Options options_; 157 | std::map rpc_handlers_; 158 | }; 159 | friend class Builder; 160 | virtual ~Server() = default; 161 | 162 | // Starts a server starts serving the registered services. 163 | void Start(); 164 | 165 | // Waits for the server to shut down. Note: The server must be either shutting 166 | // down or some other thread must call 'Shutdown()' for this function to ever 167 | // return. 168 | void WaitForShutdown(); 169 | 170 | // Shuts down the server and all of its services. 171 | void Shutdown(); 172 | 173 | // Sets the server-wide context object shared between RPC handlers. 174 | void SetExecutionContext(std::unique_ptr execution_context); 175 | 176 | template 177 | ExecutionContext::Synchronized GetContext() { 178 | return {execution_context_->lock(), execution_context_.get()}; 179 | } 180 | 181 | template 182 | T* GetUnsynchronizedContext() { 183 | return dynamic_cast(execution_context_.get()); 184 | } 185 | 186 | protected: 187 | Server(const Options& options); 188 | void AddService( 189 | const std::string& service_name, 190 | const std::map& rpc_handler_infos); 191 | 192 | private: 193 | Server(const Server&) = delete; 194 | Server& operator=(const Server&) = delete; 195 | void RunCompletionQueue(::grpc::ServerCompletionQueue* completion_queue); 196 | void RunEventQueue(Rpc::EventQueue* event_queue); 197 | Rpc::EventQueue* SelectNextEventQueueRoundRobin(); 198 | 199 | Options options_; 200 | 201 | bool shutting_down_ = false; 202 | 203 | // gRPC objects needed to build a server. 204 | ::grpc::ServerBuilder server_builder_; 205 | std::unique_ptr<::grpc::Server> server_; 206 | 207 | // Threads processing the completion queues. 208 | std::vector completion_queue_threads_; 209 | 210 | // Threads processing RPC events. 211 | std::vector event_queue_threads_; 212 | common::Mutex current_event_queue_id_lock_; 213 | int current_event_queue_id_ = 0; 214 | 215 | // Map of service names to services. 216 | std::map services_; 217 | 218 | // A context object that is shared between all implementations of 219 | // 'RpcHandler'. 220 | std::unique_ptr execution_context_; 221 | }; 222 | 223 | } // namespace async_grpc 224 | 225 | #endif // CPP_GRPC_SERVER_H 226 | -------------------------------------------------------------------------------- /async_grpc/server_test.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 The Cartographer Authors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #include "async_grpc/server.h" 18 | 19 | #include 20 | #include 21 | #include 22 | 23 | #include "async_grpc/async_client.h" 24 | #include "async_grpc/client.h" 25 | #include "async_grpc/execution_context.h" 26 | #include "async_grpc/proto/math_service.pb.h" 27 | #include "async_grpc/retry.h" 28 | #include "async_grpc/rpc_handler.h" 29 | #include "glog/logging.h" 30 | #include "google/protobuf/descriptor.h" 31 | #include "grpc++/grpc++.h" 32 | #include "gtest/gtest.h" 33 | 34 | namespace async_grpc { 35 | namespace { 36 | 37 | using EchoResponder = std::function; 38 | 39 | class MathServerContext : public ExecutionContext { 40 | public: 41 | int additional_increment() { return 10; } 42 | std::promise echo_responder; 43 | }; 44 | 45 | struct GetSumMethod { 46 | static constexpr const char* MethodName() { 47 | return "/async_grpc.proto.Math/GetSum"; 48 | } 49 | using IncomingType = Stream; 50 | using OutgoingType = proto::GetSumResponse; 51 | }; 52 | 53 | class GetSumHandler : public RpcHandler { 54 | public: 55 | void OnRequest(const proto::GetSumRequest& request) override { 56 | sum_ += GetContext()->additional_increment(); 57 | sum_ += request.input(); 58 | } 59 | 60 | void OnReadsDone() override { 61 | auto response = common::make_unique(); 62 | response->set_output(sum_); 63 | Send(std::move(response)); 64 | } 65 | 66 | private: 67 | int sum_ = 0; 68 | }; 69 | 70 | struct GetRunningSumMethod { 71 | static constexpr const char* MethodName() { 72 | return "/async_grpc.proto.Math/GetRunningSum"; 73 | } 74 | using IncomingType = Stream; 75 | using OutgoingType = Stream; 76 | }; 77 | 78 | class GetRunningSumHandler : public RpcHandler { 79 | public: 80 | void OnRequest(const proto::GetSumRequest& request) override { 81 | sum_ += request.input(); 82 | 83 | // Respond twice to demonstrate bidirectional streaming. 84 | auto response = common::make_unique(); 85 | response->set_output(sum_); 86 | Send(std::move(response)); 87 | response = common::make_unique(); 88 | response->set_output(sum_); 89 | Send(std::move(response)); 90 | } 91 | 92 | void OnReadsDone() override { Finish(::grpc::Status::OK); } 93 | 94 | private: 95 | int sum_ = 0; 96 | }; 97 | 98 | struct GetSquareMethod { 99 | static constexpr const char* MethodName() { 100 | return "/async_grpc.proto.Math/GetSquare"; 101 | } 102 | using IncomingType = proto::GetSquareRequest; 103 | using OutgoingType = proto::GetSquareResponse; 104 | }; 105 | 106 | class GetSquareHandler : public RpcHandler { 107 | public: 108 | void OnRequest(const proto::GetSquareRequest& request) override { 109 | if (request.input() < 0) { 110 | Finish(::grpc::Status(::grpc::INTERNAL, "internal error")); 111 | } 112 | auto response = common::make_unique(); 113 | response->set_output(request.input() * request.input()); 114 | Send(std::move(response)); 115 | } 116 | }; 117 | 118 | struct GetEchoMethod { 119 | static constexpr const char* MethodName() { 120 | return "/async_grpc.proto.Math/GetEcho"; 121 | } 122 | using IncomingType = proto::GetEchoRequest; 123 | using OutgoingType = proto::GetEchoResponse; 124 | }; 125 | 126 | class GetEchoHandler : public RpcHandler { 127 | public: 128 | void OnRequest(const proto::GetEchoRequest& request) override { 129 | int value = request.input(); 130 | Writer writer = GetWriter(); 131 | GetContext()->echo_responder.set_value( 132 | [writer, value]() { 133 | auto response = common::make_unique(); 134 | response->set_output(value); 135 | return writer.Write(std::move(response)); 136 | }); 137 | } 138 | }; 139 | 140 | struct GetSequenceMethod { 141 | static constexpr const char* MethodName() { 142 | return "/async_grpc.proto.Math/GetSequence"; 143 | } 144 | using IncomingType = proto::GetSequenceRequest; 145 | using OutgoingType = Stream; 146 | }; 147 | 148 | class GetSequenceHandler : public RpcHandler { 149 | public: 150 | void OnRequest(const proto::GetSequenceRequest& request) override { 151 | for (int i = 0; i < request.input(); ++i) { 152 | auto response = common::make_unique(); 153 | response->set_output(i); 154 | Send(std::move(response)); 155 | } 156 | Finish(::grpc::Status::OK); 157 | } 158 | }; 159 | 160 | // TODO(cschuet): Due to the hard-coded part these tests will become flaky when 161 | // run in parallel. It would be nice to find a way to solve that. gRPC also 162 | // allows to communicate over UNIX domain sockets. 163 | const std::string kServerAddress = "localhost:50051"; 164 | const std::size_t kNumThreads = 1; 165 | 166 | class ServerTest : public ::testing::Test { 167 | protected: 168 | void SetUp() override { 169 | Server::Builder server_builder; 170 | server_builder.SetServerAddress(kServerAddress); 171 | server_builder.SetNumGrpcThreads(kNumThreads); 172 | server_builder.SetNumEventThreads(kNumThreads); 173 | server_builder.RegisterHandler(); 174 | server_builder.RegisterHandler(); 175 | server_builder.RegisterHandler(); 176 | server_builder.RegisterHandler(); 177 | server_builder.RegisterHandler(); 178 | server_ = server_builder.Build(); 179 | 180 | client_channel_ = ::grpc::CreateChannel( 181 | kServerAddress, ::grpc::InsecureChannelCredentials()); 182 | 183 | server_->SetExecutionContext(common::make_unique()); 184 | server_->Start(); 185 | } 186 | 187 | void TearDown() override { 188 | server_->Shutdown(); 189 | CompletionQueuePool::Shutdown(); 190 | } 191 | 192 | std::unique_ptr server_; 193 | std::shared_ptr<::grpc::Channel> client_channel_; 194 | }; 195 | 196 | TEST_F(ServerTest, StartAndStopServerTest) {} 197 | 198 | TEST_F(ServerTest, ProcessRpcStreamTest) { 199 | Client client(client_channel_); 200 | for (int i = 0; i < 3; ++i) { 201 | proto::GetSumRequest request; 202 | request.set_input(i); 203 | EXPECT_TRUE(client.Write(request)); 204 | } 205 | EXPECT_TRUE(client.StreamWritesDone()); 206 | EXPECT_TRUE(client.StreamFinish().ok()); 207 | EXPECT_EQ(client.response().output(), 33); 208 | } 209 | 210 | TEST_F(ServerTest, ProcessUnaryRpcTest) { 211 | Client client(client_channel_); 212 | proto::GetSquareRequest request; 213 | request.set_input(11); 214 | EXPECT_TRUE(client.Write(request)); 215 | EXPECT_EQ(client.response().output(), 121); 216 | } 217 | 218 | TEST_F(ServerTest, ProcessBidiStreamingRpcTest) { 219 | Client client(client_channel_); 220 | for (int i = 0; i < 3; ++i) { 221 | proto::GetSumRequest request; 222 | request.set_input(i); 223 | EXPECT_TRUE(client.Write(request)); 224 | } 225 | client.StreamWritesDone(); 226 | proto::GetSumResponse response; 227 | std::list expected_responses = {0, 0, 1, 1, 3, 3}; 228 | while (client.StreamRead(&response)) { 229 | EXPECT_EQ(expected_responses.front(), response.output()); 230 | expected_responses.pop_front(); 231 | } 232 | EXPECT_TRUE(expected_responses.empty()); 233 | EXPECT_TRUE(client.StreamFinish().ok()); 234 | } 235 | 236 | TEST_F(ServerTest, WriteFromOtherThread) { 237 | Server* server = server_.get(); 238 | std::thread response_thread([server]() { 239 | std::future responder_future = 240 | server->GetContext()->echo_responder.get_future(); 241 | responder_future.wait(); 242 | auto responder = responder_future.get(); 243 | CHECK(responder()); 244 | }); 245 | 246 | Client client(client_channel_); 247 | proto::GetEchoRequest request; 248 | request.set_input(13); 249 | EXPECT_TRUE(client.Write(request)); 250 | response_thread.join(); 251 | EXPECT_EQ(client.response().output(), 13); 252 | } 253 | 254 | TEST_F(ServerTest, ProcessServerStreamingRpcTest) { 255 | Client client(client_channel_); 256 | proto::GetSequenceRequest request; 257 | request.set_input(12); 258 | 259 | client.Write(request); 260 | proto::GetSequenceResponse response; 261 | for (int i = 0; i < 12; ++i) { 262 | EXPECT_TRUE(client.StreamRead(&response)); 263 | EXPECT_EQ(response.output(), i); 264 | } 265 | EXPECT_FALSE(client.StreamRead(&response)); 266 | EXPECT_TRUE(client.StreamFinish().ok()); 267 | } 268 | 269 | TEST_F(ServerTest, RetryWithUnrecoverableError) { 270 | Client client( 271 | client_channel_, common::FromSeconds(5), 272 | CreateUnlimitedConstantDelayStrategy(common::FromSeconds(1), 273 | {::grpc::INTERNAL})); 274 | proto::GetSquareRequest request; 275 | request.set_input(-11); 276 | EXPECT_FALSE(client.Write(request)); 277 | } 278 | 279 | TEST_F(ServerTest, AsyncClientUnary) { 280 | std::mutex m; 281 | std::condition_variable cv; 282 | bool done = false; 283 | 284 | AsyncClient async_client( 285 | client_channel_, 286 | [&done, &m, &cv](const ::grpc::Status& status, 287 | const proto::GetSquareResponse* response) { 288 | EXPECT_TRUE(status.ok()); 289 | EXPECT_EQ(response->output(), 121); 290 | { 291 | std::lock_guard lock(m); 292 | done = true; 293 | } 294 | cv.notify_all(); 295 | }); 296 | proto::GetSquareRequest request; 297 | request.set_input(11); 298 | async_client.WriteAsync(request); 299 | 300 | std::unique_lock lock(m); 301 | cv.wait(lock, [&done] { return done; }); 302 | } 303 | 304 | TEST_F(ServerTest, AsyncClientServerStreaming) { 305 | std::mutex m; 306 | std::condition_variable cv; 307 | bool done = false; 308 | int counter = 0; 309 | 310 | AsyncClient async_client( 311 | client_channel_, 312 | [&done, &m, &cv, &counter](const ::grpc::Status& status, 313 | const proto::GetSequenceResponse* response) { 314 | LOG(INFO) << status.error_code() << " " << status.error_message(); 315 | LOG(INFO) << (response ? response->DebugString() : "(nullptr)"); 316 | EXPECT_TRUE(status.ok()); 317 | 318 | if (!response) { 319 | { 320 | std::lock_guard lock(m); 321 | done = true; 322 | } 323 | cv.notify_all(); 324 | } else { 325 | EXPECT_EQ(response->output(), counter++); 326 | } 327 | }); 328 | proto::GetSequenceRequest request; 329 | request.set_input(10); 330 | async_client.WriteAsync(request); 331 | 332 | std::unique_lock lock(m); 333 | LOG(INFO) << "Waiting for responses..."; 334 | cv.wait(lock, [&done] { return done; }); 335 | } 336 | 337 | } // namespace 338 | } // namespace async_grpc 339 | -------------------------------------------------------------------------------- /async_grpc/service.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 The Cartographer Authors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #include "async_grpc/server.h" 18 | 19 | #include 20 | 21 | #include "glog/logging.h" 22 | #include "grpc++/impl/codegen/proto_utils.h" 23 | 24 | namespace async_grpc { 25 | 26 | Service::Service(const std::string& service_name, 27 | const std::map& rpc_handler_infos, 28 | EventQueueSelector event_queue_selector) 29 | : rpc_handler_infos_(rpc_handler_infos), 30 | event_queue_selector_(event_queue_selector) { 31 | for (const auto& rpc_handler_info : rpc_handler_infos_) { 32 | // The 'handler' below is set to 'nullptr' indicating that we want to 33 | // handle this method asynchronously. 34 | this->AddMethod(new ::grpc::internal::RpcServiceMethod( 35 | rpc_handler_info.second.fully_qualified_name.c_str(), 36 | rpc_handler_info.second.rpc_type, nullptr /* handler */)); 37 | } 38 | } 39 | 40 | void Service::StartServing( 41 | std::vector& completion_queue_threads, 42 | ExecutionContext* execution_context) { 43 | int i = 0; 44 | for (const auto& rpc_handler_info : rpc_handler_infos_) { 45 | for (auto& completion_queue_thread : completion_queue_threads) { 46 | std::shared_ptr rpc = active_rpcs_.Add(common::make_unique( 47 | i, completion_queue_thread.completion_queue(), 48 | event_queue_selector_(), execution_context, rpc_handler_info.second, 49 | this, active_rpcs_.GetWeakPtrFactory())); 50 | rpc->RequestNextMethodInvocation(); 51 | } 52 | ++i; 53 | } 54 | } 55 | 56 | void Service::StopServing() { shutting_down_ = true; } 57 | 58 | void Service::HandleEvent(Rpc::Event event, Rpc* rpc, bool ok) { 59 | switch (event) { 60 | case Rpc::Event::NEW_CONNECTION: 61 | HandleNewConnection(rpc, ok); 62 | break; 63 | case Rpc::Event::READ: 64 | HandleRead(rpc, ok); 65 | break; 66 | case Rpc::Event::WRITE_NEEDED: 67 | case Rpc::Event::WRITE: 68 | HandleWrite(rpc, ok); 69 | break; 70 | case Rpc::Event::FINISH: 71 | HandleFinish(rpc, ok); 72 | break; 73 | case Rpc::Event::DONE: 74 | HandleDone(rpc, ok); 75 | break; 76 | } 77 | } 78 | 79 | void Service::HandleNewConnection(Rpc* rpc, bool ok) { 80 | if (shutting_down_) { 81 | if (ok) { 82 | LOG(WARNING) << "Server shutting down. Refusing to handle new RPCs."; 83 | } 84 | active_rpcs_.Remove(rpc); 85 | return; 86 | } 87 | 88 | if (!ok) { 89 | LOG(ERROR) << "Failed to establish connection for unknown reason."; 90 | active_rpcs_.Remove(rpc); 91 | } 92 | 93 | if (ok) { 94 | rpc->OnConnection(); 95 | } 96 | 97 | // Create new active rpc to handle next connection and register it for the 98 | // incoming connection. Assign event queue in a round-robin fashion. 99 | std::unique_ptr new_rpc = rpc->Clone(); 100 | new_rpc->SetEventQueue(event_queue_selector_()); 101 | active_rpcs_.Add(std::move(new_rpc))->RequestNextMethodInvocation(); 102 | } 103 | 104 | void Service::HandleRead(Rpc* rpc, bool ok) { 105 | if (ok) { 106 | rpc->OnRequest(); 107 | rpc->RequestStreamingReadIfNeeded(); 108 | return; 109 | } 110 | 111 | // Reads completed. 112 | rpc->OnReadsDone(); 113 | 114 | RemoveIfNotPending(rpc); 115 | } 116 | 117 | void Service::HandleWrite(Rpc* rpc, bool ok) { 118 | if (!ok) { 119 | LOG(ERROR) << "Write failed"; 120 | } 121 | 122 | // Send the next message or potentially finish the connection. 123 | rpc->HandleSendQueue(); 124 | 125 | RemoveIfNotPending(rpc); 126 | } 127 | 128 | void Service::HandleFinish(Rpc* rpc, bool ok) { 129 | if (!ok) { 130 | LOG(ERROR) << "Finish failed"; 131 | } 132 | 133 | rpc->OnFinish(); 134 | 135 | RemoveIfNotPending(rpc); 136 | } 137 | 138 | void Service::HandleDone(Rpc* rpc, bool ok) { RemoveIfNotPending(rpc); } 139 | 140 | void Service::RemoveIfNotPending(Rpc* rpc) { 141 | if (!rpc->IsAnyEventPending()) { 142 | active_rpcs_.Remove(rpc); 143 | } 144 | } 145 | 146 | } // namespace async_grpc 147 | -------------------------------------------------------------------------------- /async_grpc/service.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 The Cartographer Authors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #ifndef CPP_GRPC_SERVICE_H 18 | #define CPP_GRPC_SERVICE_H 19 | 20 | #include "async_grpc/completion_queue_thread.h" 21 | #include "async_grpc/event_queue_thread.h" 22 | #include "async_grpc/execution_context.h" 23 | #include "async_grpc/rpc.h" 24 | #include "async_grpc/rpc_handler.h" 25 | #include "grpc++/impl/codegen/service_type.h" 26 | 27 | namespace async_grpc { 28 | 29 | // A 'Service' represents a generic service for gRPC asynchronous methods and is 30 | // responsible for managing the lifetime of active RPCs issued against methods 31 | // of the service and distributing incoming gRPC events to their respective 32 | // 'Rpc' handler objects. 33 | class Service : public ::grpc::Service { 34 | public: 35 | using EventQueueSelector = std::function; 36 | friend class Rpc; 37 | 38 | Service(const std::string& service_name, 39 | const std::map& rpc_handlers, 40 | EventQueueSelector event_queue_selector); 41 | void StartServing(std::vector& completion_queues, 42 | ExecutionContext* execution_context); 43 | void HandleEvent(Rpc::Event event, Rpc* rpc, bool ok); 44 | void StopServing(); 45 | 46 | private: 47 | void HandleNewConnection(Rpc* rpc, bool ok); 48 | void HandleRead(Rpc* rpc, bool ok); 49 | void HandleWrite(Rpc* rpc, bool ok); 50 | void HandleFinish(Rpc* rpc, bool ok); 51 | void HandleDone(Rpc* rpc, bool ok); 52 | 53 | void RemoveIfNotPending(Rpc* rpc); 54 | 55 | std::map rpc_handler_infos_; 56 | EventQueueSelector event_queue_selector_; 57 | ActiveRpcs active_rpcs_; 58 | bool shutting_down_ = false; 59 | }; 60 | 61 | } // namespace async_grpc 62 | 63 | #endif // CPP_GRPC_SERVICE_H 64 | -------------------------------------------------------------------------------- /async_grpc/span.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 The Cartographer Authors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #ifndef CPP_GRPC_SPAN_H 18 | #define CPP_GRPC_SPAN_H 19 | 20 | #include 21 | 22 | #include "grpc++/grpc++.h" 23 | 24 | namespace async_grpc { 25 | 26 | // Span represents a trace span. All implementations of the Span interface 27 | // must be thread-safe. 28 | class Span { 29 | public: 30 | Span() = default; 31 | virtual ~Span() = default; 32 | 33 | // Creates a new child span with this span as the parent. 34 | virtual std::unique_ptr CreateChildSpan(const std::string& name) = 0; 35 | 36 | // Sets the status of the Span. See status_code.h for canonical codes. 37 | virtual void SetStatus(const ::grpc::Status& status) = 0; 38 | 39 | // Marks the end of a Span. No further changes can be made to the Span after 40 | // End is called. 41 | virtual void End() = 0; 42 | }; 43 | 44 | } // namespace async_grpc 45 | 46 | #endif // CPP_GRPC_SPAN_H -------------------------------------------------------------------------------- /async_grpc/testing/rpc_handler_test_server.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 The Cartographer Authors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #ifndef CPP_GRPC_TESTING_RPC_HANDLER_TEST_SERVER_H_ 18 | #define CPP_GRPC_TESTING_RPC_HANDLER_TEST_SERVER_H_ 19 | 20 | #include 21 | #include 22 | 23 | #include "async_grpc/client.h" 24 | #include "async_grpc/common/blocking_queue.h" 25 | #include "async_grpc/rpc_handler_interface.h" 26 | #include "async_grpc/server.h" 27 | #include "async_grpc/testing/rpc_handler_wrapper.h" 28 | #include "grpc++/grpc++.h" 29 | #include "gtest/gtest.h" 30 | 31 | namespace async_grpc { 32 | namespace testing { 33 | 34 | namespace { 35 | const std::string kServerAddress = "localhost:50051"; 36 | } // namespace 37 | 38 | template 39 | class RpcHandlerTestServer : public Server { 40 | public: 41 | using RequestType = 42 | StripStream; 43 | using ResponseType = 44 | StripStream; 45 | 46 | RpcHandlerTestServer(std::unique_ptr execution_context) 47 | : Server(Options(1, 1, kServerAddress)), 48 | channel_(::grpc::CreateChannel(kServerAddress, 49 | ::grpc::InsecureChannelCredentials())), 50 | client_(channel_) { 51 | std::string method_full_name_under_test = 52 | RpcServiceMethodConcept::MethodName(); 53 | std::string service_full_name; 54 | std::string method_name; 55 | std::tie(service_full_name, method_name) = 56 | Server::Builder::ParseMethodFullName(method_full_name_under_test); 57 | this->AddService( 58 | service_full_name, 59 | {{method_name, GetRpcHandlerInfo(method_full_name_under_test)}}); 60 | this->SetExecutionContext(std::move(execution_context)); 61 | this->Start(); 62 | } 63 | 64 | ~RpcHandlerTestServer() { this->Shutdown(); }; 65 | 66 | bool SendWrite(const RequestType &message, ::grpc::Status *status = nullptr) { 67 | bool success = client_.Write(message, status); 68 | WaitForHandlerCompletion(RpcHandlerWrapper::ON_REQUEST); 69 | return success; 70 | } 71 | 72 | // Parses a request message from the passed string and issues the 73 | // request against the handler, waits for the handler to complete 74 | // processing before returning. 75 | void SendWrite(const std::string &serialized_message) { 76 | RequestType message; 77 | message.ParseFromString(serialized_message); 78 | Write(message); 79 | } 80 | 81 | // Sends a WRITES_DONE event to the handler, waits for the handler 82 | // to finish processing the READS_DONE event before returning. 83 | void SendWritesDone() { 84 | EXPECT_TRUE(client_.StreamWritesDone()); 85 | WaitForHandlerCompletion(RpcHandlerWrapper::ON_READS_DONE); 86 | } 87 | 88 | // Sends a FINISH event to the handler under test, waits for the 89 | // handler to finish processing the event before returning. 90 | void SendFinish() { 91 | EXPECT_TRUE(client_.StreamFinish().ok()); 92 | WaitForHandlerCompletion(RpcHandlerWrapper::ON_FINISH); 93 | } 94 | 95 | const ResponseType &response() { return client_.response(); } 96 | 97 | private: 98 | using ClientWriter = ::grpc::internal::ClientWriterFactory; 99 | 100 | void WaitForHandlerCompletion( 101 | typename RpcHandlerWrapper::RpcHandlerEvent event) { 102 | CHECK_EQ(rpc_handler_event_queue_.Pop(), event); 103 | } 104 | 105 | RpcHandlerInfo GetRpcHandlerInfo(const std::string &method_full_name) { 106 | ::grpc::internal::RpcMethod::RpcType rpc_type = 107 | RpcType::value; 109 | auto event_callback = 110 | [this]( 111 | typename RpcHandlerWrapper::RpcHandlerEvent event) { 112 | rpc_handler_event_queue_.Push(event); 113 | }; 114 | auto handler_instantiator = [event_callback]( 115 | Rpc *const rpc, 116 | ExecutionContext *const execution_context) { 117 | std::unique_ptr rpc_handler = 118 | common::make_unique>( 119 | event_callback); 120 | rpc_handler->SetRpc(rpc); 121 | rpc_handler->SetExecutionContext(execution_context); 122 | rpc_handler->Initialize(); 123 | return rpc_handler; 124 | }; 125 | return RpcHandlerInfo{RequestType::default_instance().GetDescriptor(), 126 | ResponseType::default_instance().GetDescriptor(), 127 | handler_instantiator, rpc_type, method_full_name}; 128 | } 129 | 130 | std::shared_ptr<::grpc::Channel> channel_; 131 | Client client_; 132 | common::BlockingQueue< 133 | typename RpcHandlerWrapper::RpcHandlerEvent> 134 | rpc_handler_event_queue_; 135 | }; 136 | 137 | } // namespace testing 138 | } // namespace async_grpc 139 | 140 | #endif // CPP_GRPC_TESTING_RPC_HANDLER_TEST_SERVER_H_ 141 | -------------------------------------------------------------------------------- /async_grpc/testing/rpc_handler_wrapper.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 The Cartographer Authors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #ifndef CPP_GRPC_TESTING_RPC_HANDLER_WRAPPER_H_ 18 | #define CPP_GRPC_TESTING_RPC_HANDLER_WRAPPER_H_ 19 | 20 | #include 21 | 22 | namespace async_grpc { 23 | namespace testing { 24 | 25 | template 26 | class RpcHandlerWrapper : public RpcHandlerType { 27 | public: 28 | enum RpcHandlerEvent { ON_REQUEST, ON_READS_DONE, ON_FINISH }; 29 | using EventCallback = std::function; 30 | 31 | RpcHandlerWrapper(EventCallback event_callback) 32 | : event_callback_(event_callback) {} 33 | 34 | void OnRequest(const typename RpcHandlerType::RequestType &request) override { 35 | RpcHandlerType::OnRequest(request); 36 | event_callback_(ON_REQUEST); 37 | } 38 | 39 | void OnReadsDone() override { 40 | RpcHandlerType::OnReadsDone(); 41 | event_callback_(ON_READS_DONE); 42 | } 43 | 44 | void OnFinish() override { 45 | RpcHandlerType::OnFinish(); 46 | event_callback_(ON_FINISH); 47 | } 48 | 49 | private: 50 | EventCallback event_callback_; 51 | }; 52 | 53 | } // namespace testing 54 | } // namespace async_grpc 55 | 56 | #endif // CPP_GRPC_TESTING_RPC_HANDLER_WRAPPER_H_ 57 | -------------------------------------------------------------------------------- /async_grpc/tools/bazel.rc: -------------------------------------------------------------------------------- 1 | build --copt -Werror 2 | -------------------------------------------------------------------------------- /async_grpc/type_traits.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 The Cartographer Authors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #ifndef CPP_GRPC_TYPE_TRAITS_H_ 18 | #define CPP_GRPC_TYPE_TRAITS_H_ 19 | 20 | #include 21 | 22 | #include 23 | #include 24 | 25 | // This helper allows us to stamp out traits structs which allow to check for 26 | // the existence of member functions. 27 | // Example: 28 | // struct Foo { static char* foo() { return nullptr; } }; 29 | // DEFINE_HAS_SIGNATURE(has_foo, T::foo, char*(*)(void)); 30 | // static_assert(has_foo_v, "foo() is not implemented") 31 | #define DEFINE_HAS_SIGNATURE(traitsName, funcName, signature) \ 32 | template \ 33 | class traitsName { \ 34 | private: \ 35 | template \ 36 | struct helper; \ 37 | \ 38 | template \ 39 | static std::uint8_t check(helper*); \ 40 | template \ 41 | static std::uint16_t check(...); \ 42 | \ 43 | public: \ 44 | static constexpr bool value = sizeof(check(0)) == sizeof(std::uint8_t); \ 45 | } 46 | 47 | #define DEFINE_HAS_MEMBER_TYPE(traitsName, Type) \ 48 | template \ 49 | class traitsName { \ 50 | private: \ 51 | struct Fallback { \ 52 | struct Type {}; \ 53 | }; \ 54 | struct Derived : T, Fallback {}; \ 55 | \ 56 | template \ 57 | static std::uint16_t& check(typename U::Type*); \ 58 | template \ 59 | static std::uint8_t& check(U*); \ 60 | \ 61 | public: \ 62 | static constexpr bool value = \ 63 | sizeof(check(0)) == sizeof(std::uint8_t); \ 64 | } 65 | 66 | #define DEFINE_HANDLER_SIGNATURE(traitsName, incomingType, outgoingType, \ 67 | methodName) \ 68 | struct traitsName { \ 69 | using IncomingType = incomingType; \ 70 | using OutgoingType = outgoingType; \ 71 | static const char* MethodName() { return methodName; } \ 72 | }; 73 | 74 | namespace async_grpc { 75 | 76 | template 77 | class Stream { 78 | using type = Request; 79 | }; 80 | 81 | template