// Copyright (C) 2020 The Android Open Source Project // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #include "GrpcGraph.h" #include #include #include #include "ClientConfig.pb.h" #include "GrpcGraph.h" #include "InputFrame.h" #include "RunnerComponent.h" #include "prebuilt_interface.h" #include "types/Status.h" namespace android { namespace automotive { namespace computepipe { namespace graph { namespace { constexpr int64_t kRpcDeadlineMilliseconds = 100; template std::pair FinishRpcAndGetResult( ::grpc::ClientAsyncResponseReader* rpc, ::grpc::CompletionQueue* cq, ResponseType* response) { int random_tag = rand(); ::grpc::Status grpcStatus; rpc->Finish(response, &grpcStatus, reinterpret_cast(random_tag)); bool ok = false; void* got_tag; if (!cq->Next(&got_tag, &ok)) { LOG(ERROR) << "Unexpected shutdown of the completion queue"; return std::pair(Status::FATAL_ERROR, "Unexpected shutdown of the completion queue"); } if (!ok) { LOG(ERROR) << "Unable to complete RPC request"; return std::pair(Status::FATAL_ERROR, "Unable to complete RPC request"); } CHECK_EQ(got_tag, reinterpret_cast(random_tag)); if (!grpcStatus.ok()) { std::string error_message = std::string("Grpc failed with error: ") + grpcStatus.error_message(); LOG(ERROR) << error_message; return std::pair(Status::FATAL_ERROR, std::move(error_message)); } return std::pair(Status::SUCCESS, std::string("")); } } // namespace GrpcGraph::~GrpcGraph() { mStreamSetObserver.reset(); } PrebuiltGraphState GrpcGraph::GetGraphState() const { std::lock_guard lock(mLock); return mGraphState; } Status GrpcGraph::GetStatus() const { std::lock_guard lock(mLock); return mStatus; } std::string GrpcGraph::GetErrorMessage() const { std::lock_guard lock(mLock); return mErrorMessage; } Status GrpcGraph::initialize(const std::string& address, std::weak_ptr engineInterface) { std::shared_ptr<::grpc::ChannelCredentials> creds = ::grpc::InsecureChannelCredentials(); std::shared_ptr<::grpc::Channel> channel = ::grpc::CreateChannel(address, creds); mGraphStub = proto::GrpcGraphService::NewStub(channel); mEngineInterface = engineInterface; ::grpc::ClientContext context; context.set_deadline(std::chrono::system_clock::now() + std::chrono::milliseconds(kRpcDeadlineMilliseconds)); ::grpc::CompletionQueue cq; proto::GraphOptionsRequest getGraphOptionsRequest; std::unique_ptr<::grpc::ClientAsyncResponseReader> rpc( mGraphStub->AsyncGetGraphOptions(&context, getGraphOptionsRequest, &cq)); proto::GraphOptionsResponse response; auto [mStatus, mErrorMessage] = FinishRpcAndGetResult(rpc.get(), &cq, &response); if (mStatus != Status::SUCCESS) { LOG(ERROR) << "Failed to get graph options: " << mErrorMessage; return Status::FATAL_ERROR; } std::string serialized_options = response.serialized_options(); if (!mGraphConfig.ParseFromString(serialized_options)) { mErrorMessage = "Failed to parse graph options"; LOG(ERROR) << "Failed to parse graph options"; return Status::FATAL_ERROR; } mGraphState = PrebuiltGraphState::STOPPED; return Status::SUCCESS; } // Function to confirm that there would be no further changes to the graph configuration. This // needs to be called before starting the graph. Status GrpcGraph::handleConfigPhase(const runner::ClientConfig& e) { std::lock_guard lock(mLock); if (mGraphState == PrebuiltGraphState::UNINITIALIZED) { mStatus = Status::ILLEGAL_STATE; return Status::ILLEGAL_STATE; } // handleConfigPhase is a blocking call, so abort call is pointless for this RunnerEvent. if (e.isAborted()) { mStatus = Status::INVALID_ARGUMENT; return mStatus; } else if (e.isTransitionComplete()) { mStatus = Status::SUCCESS; return mStatus; } ::grpc::ClientContext context; context.set_deadline(std::chrono::system_clock::now() + std::chrono::milliseconds(kRpcDeadlineMilliseconds)); ::grpc::CompletionQueue cq; std::string serializedConfig = e.getSerializedClientConfig(); proto::SetGraphConfigRequest setGraphConfigRequest; setGraphConfigRequest.set_serialized_config(std::move(serializedConfig)); std::unique_ptr<::grpc::ClientAsyncResponseReader> rpc( mGraphStub->AsyncSetGraphConfig(&context, setGraphConfigRequest, &cq)); proto::StatusResponse response; auto [mStatus, mErrorMessage] = FinishRpcAndGetResult(rpc.get(), &cq, &response); if (mStatus != Status::SUCCESS) { LOG(ERROR) << "Rpc failed while trying to set configuration"; return mStatus; } if (response.code() != proto::RemoteGraphStatusCode::SUCCESS) { LOG(ERROR) << "Failed to cofngure remote graph. " << response.message(); } mStatus = static_cast(static_cast(response.code())); mErrorMessage = response.message(); mStreamSetObserver = std::make_unique(e, this); return mStatus; } // Starts the graph. Status GrpcGraph::handleExecutionPhase(const runner::RunnerEvent& e) { std::lock_guard lock(mLock); if (mGraphState != PrebuiltGraphState::STOPPED || mStreamSetObserver == nullptr) { mStatus = Status::ILLEGAL_STATE; return mStatus; } if (e.isAborted()) { // Starting the graph is a blocking call and cannot be aborted in between. mStatus = Status::INVALID_ARGUMENT; return mStatus; } else if (e.isTransitionComplete()) { mStatus = Status::SUCCESS; return mStatus; } // Start observing the output streams mStatus = mStreamSetObserver->startObservingStreams(); if (mStatus != Status::SUCCESS) { mErrorMessage = "Failed to observe output streams"; return mStatus; } ::grpc::ClientContext context; context.set_deadline(std::chrono::system_clock::now() + std::chrono::milliseconds(kRpcDeadlineMilliseconds)); proto::StartGraphExecutionRequest startExecutionRequest; ::grpc::CompletionQueue cq; std::unique_ptr<::grpc::ClientAsyncResponseReader> rpc( mGraphStub->AsyncStartGraphExecution(&context, startExecutionRequest, &cq)); proto::StatusResponse response; auto [mStatus, mErrorMessage] = FinishRpcAndGetResult(rpc.get(), &cq, &response); if (mStatus != Status::SUCCESS) { LOG(ERROR) << "Failed to start graph execution"; return mStatus; } mStatus = static_cast(static_cast(response.code())); mErrorMessage = response.message(); if (mStatus == Status::SUCCESS) { mGraphState = PrebuiltGraphState::RUNNING; } return mStatus; } // Stops the graph while letting the graph flush output packets in flight. Status GrpcGraph::handleStopWithFlushPhase(const runner::RunnerEvent& e) { std::lock_guard lock(mLock); if (mGraphState != PrebuiltGraphState::RUNNING) { return Status::ILLEGAL_STATE; } if (e.isAborted()) { return Status::INVALID_ARGUMENT; } else if (e.isTransitionComplete()) { return Status::SUCCESS; } ::grpc::ClientContext context; context.set_deadline(std::chrono::system_clock::now() + std::chrono::milliseconds(kRpcDeadlineMilliseconds)); proto::StopGraphExecutionRequest stopExecutionRequest; stopExecutionRequest.set_stop_immediate(false); ::grpc::CompletionQueue cq; std::unique_ptr<::grpc::ClientAsyncResponseReader> rpc( mGraphStub->AsyncStopGraphExecution(&context, stopExecutionRequest, &cq)); proto::StatusResponse response; auto [mStatus, mErrorMessage] = FinishRpcAndGetResult(rpc.get(), &cq, &response); if (mStatus != Status::SUCCESS) { LOG(ERROR) << "Failed to stop graph execution"; return Status::FATAL_ERROR; } // Stop observing streams immendiately. mStreamSetObserver->stopObservingStreams(false); mStatus = static_cast(static_cast(response.code())); mErrorMessage = response.message(); if (mStatus == Status::SUCCESS) { mGraphState = PrebuiltGraphState::FLUSHING; } return mStatus; } // Stops the graph and cancels all the output packets. Status GrpcGraph::handleStopImmediatePhase(const runner::RunnerEvent& e) { std::lock_guard lock(mLock); if (mGraphState != PrebuiltGraphState::RUNNING) { return Status::ILLEGAL_STATE; } if (e.isAborted()) { return Status::INVALID_ARGUMENT; } else if (e.isTransitionComplete()) { return Status::SUCCESS; } ::grpc::ClientContext context; context.set_deadline(std::chrono::system_clock::now() + std::chrono::milliseconds(kRpcDeadlineMilliseconds)); proto::StopGraphExecutionRequest stopExecutionRequest; stopExecutionRequest.set_stop_immediate(true); ::grpc::CompletionQueue cq; std::unique_ptr<::grpc::ClientAsyncResponseReader> rpc( mGraphStub->AsyncStopGraphExecution(&context, stopExecutionRequest, &cq)); proto::StatusResponse response; auto [mStatus, mErrorMessage] = FinishRpcAndGetResult(rpc.get(), &cq, &response); if (mStatus != Status::SUCCESS) { LOG(ERROR) << "Failed to stop graph execution"; return Status::FATAL_ERROR; } mStatus = static_cast(static_cast(response.code())); mErrorMessage = response.message(); // Stop observing streams immendiately. mStreamSetObserver->stopObservingStreams(true); if (mStatus == Status::SUCCESS) { mGraphState = PrebuiltGraphState::STOPPED; } return mStatus; } Status GrpcGraph::handleResetPhase(const runner::RunnerEvent& e) { std::lock_guard lock(mLock); if (mGraphState != PrebuiltGraphState::STOPPED) { return Status::ILLEGAL_STATE; } if (e.isAborted()) { return Status::INVALID_ARGUMENT; } else if (e.isTransitionComplete()) { return Status::SUCCESS; } ::grpc::ClientContext context; context.set_deadline(std::chrono::system_clock::now() + std::chrono::milliseconds(kRpcDeadlineMilliseconds)); proto::ResetGraphRequest resetGraphRequest; ::grpc::CompletionQueue cq; std::unique_ptr<::grpc::ClientAsyncResponseReader> rpc( mGraphStub->AsyncResetGraph(&context, resetGraphRequest, &cq)); proto::StatusResponse response; auto [mStatus, mErrorMessage] = FinishRpcAndGetResult(rpc.get(), &cq, &response); if (mStatus != Status::SUCCESS) { LOG(ERROR) << "Failed to stop graph execution"; return Status::FATAL_ERROR; } mStatus = static_cast(static_cast(response.code())); mErrorMessage = response.message(); mStreamSetObserver.reset(); return mStatus; } Status GrpcGraph::SetInputStreamData(int /*streamIndex*/, int64_t /*timestamp*/, const std::string& /*streamData*/) { LOG(ERROR) << "Cannot set input stream for remote graphs"; return Status::FATAL_ERROR; } Status GrpcGraph::SetInputStreamPixelData(int /*streamIndex*/, int64_t /*timestamp*/, const runner::InputFrame& /*inputFrame*/) { LOG(ERROR) << "Cannot set input streams for remote graphs"; return Status::FATAL_ERROR; } Status GrpcGraph::StartGraphProfiling() { std::lock_guard lock(mLock); if (mGraphState != PrebuiltGraphState::RUNNING) { return Status::ILLEGAL_STATE; } ::grpc::ClientContext context; context.set_deadline(std::chrono::system_clock::now() + std::chrono::milliseconds(kRpcDeadlineMilliseconds)); proto::StartGraphProfilingRequest startProfilingRequest; ::grpc::CompletionQueue cq; std::unique_ptr<::grpc::ClientAsyncResponseReader> rpc( mGraphStub->AsyncStartGraphProfiling(&context, startProfilingRequest, &cq)); proto::StatusResponse response; auto [mStatus, mErrorMessage] = FinishRpcAndGetResult(rpc.get(), &cq, &response); if (mStatus != Status::SUCCESS) { LOG(ERROR) << "Failed to start graph profiling"; return Status::FATAL_ERROR; } mStatus = static_cast(static_cast(response.code())); mErrorMessage = response.message(); return mStatus; } Status GrpcGraph::StopGraphProfiling() { // Stopping profiling after graph has already stopped can be a no-op ::grpc::ClientContext context; context.set_deadline(std::chrono::system_clock::now() + std::chrono::milliseconds(kRpcDeadlineMilliseconds)); proto::StopGraphProfilingRequest stopProfilingRequest; ::grpc::CompletionQueue cq; std::unique_ptr<::grpc::ClientAsyncResponseReader> rpc( mGraphStub->AsyncStopGraphProfiling(&context, stopProfilingRequest, &cq)); proto::StatusResponse response; auto [mStatus, mErrorMessage] = FinishRpcAndGetResult(rpc.get(), &cq, &response); if (mStatus != Status::SUCCESS) { LOG(ERROR) << "Failed to stop graph profiling"; return Status::FATAL_ERROR; } mStatus = static_cast(static_cast(response.code())); mErrorMessage = response.message(); return mStatus; } std::string GrpcGraph::GetDebugInfo() { ::grpc::ClientContext context; context.set_deadline(std::chrono::system_clock::now() + std::chrono::milliseconds(kRpcDeadlineMilliseconds)); proto::ProfilingDataRequest profilingDataRequest; ::grpc::CompletionQueue cq; std::unique_ptr<::grpc::ClientAsyncResponseReader> rpc( mGraphStub->AsyncGetProfilingData(&context, profilingDataRequest, &cq)); proto::ProfilingDataResponse response; auto [mStatus, mErrorMessage] = FinishRpcAndGetResult(rpc.get(), &cq, &response); if (mStatus != Status::SUCCESS) { LOG(ERROR) << "Failed to get profiling info"; return ""; } return response.data(); } void GrpcGraph::dispatchPixelData(int streamId, int64_t timestamp_us, const runner::InputFrame& frame) { std::shared_ptr engineInterface = mEngineInterface.lock(); if (engineInterface) { return engineInterface->DispatchPixelData(streamId, timestamp_us, frame); } } void GrpcGraph::dispatchSerializedData(int streamId, int64_t timestamp_us, std::string&& serialized_data) { std::shared_ptr engineInterface = mEngineInterface.lock(); if (engineInterface) { return engineInterface->DispatchSerializedData(streamId, timestamp_us, std::move(serialized_data)); } } void GrpcGraph::dispatchGraphTerminationMessage(Status status, std::string&& errorMessage) { std::lock_guard lock(mLock); mErrorMessage = std::move(errorMessage); mStatus = status; mGraphState = PrebuiltGraphState::STOPPED; std::shared_ptr engineInterface = mEngineInterface.lock(); if (engineInterface) { std::string errorMessageTmp = mErrorMessage; engineInterface->DispatchGraphTerminationMessage(mStatus, std::move(errorMessageTmp)); } } std::unique_ptr GetRemoteGraphFromAddress( const std::string& address, std::weak_ptr engineInterface) { auto prebuiltGraph = std::make_unique(); Status status = prebuiltGraph->initialize(address, engineInterface); if (status != Status::SUCCESS) { return nullptr; } return prebuiltGraph; } } // namespace graph } // namespace computepipe } // namespace automotive } // namespace android