Fix RpcEvent lifetime (#793)

Fixes #788.

Uses two different types of events whether the event goes through the CompletionQueue or not.
CompletionQueueRpcEvent is again a member of Rpc.
master
gaschler 2018-01-08 12:42:19 +01:00 committed by Wally B. Feed
parent 91034eaf58
commit 920a34a938
6 changed files with 121 additions and 76 deletions

View File

@ -26,8 +26,6 @@
namespace cartographer_grpc { namespace cartographer_grpc {
namespace framework { namespace framework {
using EventQueue = cartographer::common::BlockingQueue<Rpc::RpcEvent*>;
class EventQueueThread { class EventQueueThread {
public: public:
using EventQueueRunner = std::function<void(EventQueue*)>; using EventQueueRunner = std::function<void(EventQueue*)>;

View File

@ -30,7 +30,7 @@ namespace {
template <typename ReaderWriter> template <typename ReaderWriter>
void SendUnaryFinish(ReaderWriter* reader_writer, ::grpc::Status status, void SendUnaryFinish(ReaderWriter* reader_writer, ::grpc::Status status,
const google::protobuf::Message* msg, const google::protobuf::Message* msg,
Rpc::RpcEvent* rpc_event) { Rpc::EventBase* rpc_event) {
if (msg) { if (msg) {
reader_writer->Finish(*msg, status, rpc_event); reader_writer->Finish(*msg, status, rpc_event);
} else { } else {
@ -40,6 +40,19 @@ void SendUnaryFinish(ReaderWriter* reader_writer, ::grpc::Status status,
} // namespace } // namespace
void Rpc::CompletionQueueRpcEvent::Handle() {
pending = false;
rpc_ptr->service()->HandleEvent(event, rpc_ptr, ok);
}
void Rpc::InternalRpcEvent::Handle() {
if (auto rpc_shared = rpc.lock()) {
rpc_shared->service()->HandleEvent(event, rpc_shared.get(), true);
} else {
LOG(WARNING) << "Ignoring stale event.";
}
}
Rpc::Rpc(int method_index, Rpc::Rpc(int method_index,
::grpc::ServerCompletionQueue* server_completion_queue, ::grpc::ServerCompletionQueue* server_completion_queue,
EventQueue* event_queue, ExecutionContext* execution_context, EventQueue* event_queue, ExecutionContext* execution_context,
@ -52,6 +65,11 @@ Rpc::Rpc(int method_index,
rpc_handler_info_(rpc_handler_info), rpc_handler_info_(rpc_handler_info),
service_(service), service_(service),
weak_ptr_factory_(weak_ptr_factory), weak_ptr_factory_(weak_ptr_factory),
new_connection_event_(Event::NEW_CONNECTION, this),
read_event_(Event::READ, this),
write_event_(Event::WRITE, this),
finish_event_(Event::FINISH, this),
done_event_(Event::DONE, this),
handler_(rpc_handler_info_.rpc_handler_factory(this, execution_context)) { handler_(rpc_handler_info_.rpc_handler_factory(this, execution_context)) {
InitializeReadersAndWriters(rpc_handler_info_.rpc_type); InitializeReadersAndWriters(rpc_handler_info_.rpc_type);
@ -81,8 +99,7 @@ void Rpc::RequestNextMethodInvocation() {
SetRpcEventState(Event::DONE, true); SetRpcEventState(Event::DONE, true);
// TODO(gaschler): Asan reports direct leak of this new from both calls // TODO(gaschler): Asan reports direct leak of this new from both calls
// StartServing and HandleNewConnection. // StartServing and HandleNewConnection.
server_context_.AsyncNotifyWhenDone( server_context_.AsyncNotifyWhenDone(GetRpcEvent(Event::DONE));
new RpcEvent{Event::DONE, weak_ptr_factory_(this), true});
// Make sure after terminating the connection, gRPC notifies us with this // Make sure after terminating the connection, gRPC notifies us with this
// event. // event.
@ -92,27 +109,25 @@ void Rpc::RequestNextMethodInvocation() {
service_->RequestAsyncBidiStreaming( service_->RequestAsyncBidiStreaming(
method_index_, &server_context_, streaming_interface(), method_index_, &server_context_, streaming_interface(),
server_completion_queue_, server_completion_queue_, server_completion_queue_, server_completion_queue_,
new RpcEvent{Event::NEW_CONNECTION, weak_ptr_factory_(this), true}); GetRpcEvent(Event::NEW_CONNECTION));
break; break;
case ::grpc::internal::RpcMethod::CLIENT_STREAMING: case ::grpc::internal::RpcMethod::CLIENT_STREAMING:
service_->RequestAsyncClientStreaming( service_->RequestAsyncClientStreaming(
method_index_, &server_context_, streaming_interface(), method_index_, &server_context_, streaming_interface(),
server_completion_queue_, server_completion_queue_, server_completion_queue_, server_completion_queue_,
new RpcEvent{Event::NEW_CONNECTION, weak_ptr_factory_(this), true}); GetRpcEvent(Event::NEW_CONNECTION));
break; break;
case ::grpc::internal::RpcMethod::NORMAL_RPC: case ::grpc::internal::RpcMethod::NORMAL_RPC:
service_->RequestAsyncUnary( service_->RequestAsyncUnary(
method_index_, &server_context_, request_.get(), method_index_, &server_context_, request_.get(),
streaming_interface(), server_completion_queue_, streaming_interface(), server_completion_queue_,
server_completion_queue_, server_completion_queue_, GetRpcEvent(Event::NEW_CONNECTION));
new RpcEvent{Event::NEW_CONNECTION, weak_ptr_factory_(this), true});
break; break;
case ::grpc::internal::RpcMethod::SERVER_STREAMING: case ::grpc::internal::RpcMethod::SERVER_STREAMING:
service_->RequestAsyncServerStreaming( service_->RequestAsyncServerStreaming(
method_index_, &server_context_, request_.get(), method_index_, &server_context_, request_.get(),
streaming_interface(), server_completion_queue_, streaming_interface(), server_completion_queue_,
server_completion_queue_, server_completion_queue_, GetRpcEvent(Event::NEW_CONNECTION));
new RpcEvent{Event::NEW_CONNECTION, weak_ptr_factory_(this), true});
break; break;
} }
} }
@ -123,9 +138,7 @@ void Rpc::RequestStreamingReadIfNeeded() {
case ::grpc::internal::RpcMethod::BIDI_STREAMING: case ::grpc::internal::RpcMethod::BIDI_STREAMING:
case ::grpc::internal::RpcMethod::CLIENT_STREAMING: case ::grpc::internal::RpcMethod::CLIENT_STREAMING:
SetRpcEventState(Event::READ, true); SetRpcEventState(Event::READ, true);
async_reader_interface()->Read( async_reader_interface()->Read(request_.get(), GetRpcEvent(Event::READ));
request_.get(),
new RpcEvent{Event::READ, weak_ptr_factory_(this), true});
break; break;
case ::grpc::internal::RpcMethod::NORMAL_RPC: case ::grpc::internal::RpcMethod::NORMAL_RPC:
case ::grpc::internal::RpcMethod::SERVER_STREAMING: case ::grpc::internal::RpcMethod::SERVER_STREAMING:
@ -140,14 +153,14 @@ void Rpc::RequestStreamingReadIfNeeded() {
void Rpc::Write(std::unique_ptr<::google::protobuf::Message> message) { void Rpc::Write(std::unique_ptr<::google::protobuf::Message> message) {
EnqueueMessage(SendItem{std::move(message), ::grpc::Status::OK}); EnqueueMessage(SendItem{std::move(message), ::grpc::Status::OK});
event_queue_->Push( event_queue_->Push(UniqueEventPtr(
new RpcEvent{Event::WRITE_NEEDED, weak_ptr_factory_(this), true}); new InternalRpcEvent(Event::WRITE_NEEDED, weak_ptr_factory_(this))));
} }
void Rpc::Finish(::grpc::Status status) { void Rpc::Finish(::grpc::Status status) {
EnqueueMessage(SendItem{nullptr /* message */, status}); EnqueueMessage(SendItem{nullptr /* message */, status});
event_queue_->Push( event_queue_->Push(UniqueEventPtr(
new RpcEvent{Event::WRITE_NEEDED, weak_ptr_factory_(this), true}); new InternalRpcEvent(Event::WRITE_NEEDED, weak_ptr_factory_(this))));
} }
void Rpc::HandleSendQueue() { void Rpc::HandleSendQueue() {
@ -218,24 +231,29 @@ Rpc::async_writer_interface() {
LOG(FATAL) << "Never reached."; LOG(FATAL) << "Never reached.";
} }
bool* Rpc::GetRpcEventState(Event event) { Rpc::CompletionQueueRpcEvent* Rpc::GetRpcEvent(Event event) {
switch (event) { switch (event) {
case Event::NEW_CONNECTION: case Event::NEW_CONNECTION:
return &new_connection_event_pending_; return &new_connection_event_;
case Event::READ: case Event::READ:
return &read_event_pending_; return &read_event_;
case Event::WRITE_NEEDED: case Event::WRITE_NEEDED:
return &write_needed_event_pending_; LOG(FATAL) << "Rpc does not store Event::WRITE_NEEDED.";
break;
case Event::WRITE: case Event::WRITE:
return &write_event_pending_; return &write_event_;
case Event::FINISH: case Event::FINISH:
return &finish_event_pending_; return &finish_event_;
case Event::DONE: case Event::DONE:
return &done_event_pending_; return &done_event_;
} }
LOG(FATAL) << "Never reached."; LOG(FATAL) << "Never reached.";
} }
bool* Rpc::GetRpcEventState(Event event) {
return &GetRpcEvent(event)->pending;
}
void Rpc::EnqueueMessage(SendItem&& send_item) { void Rpc::EnqueueMessage(SendItem&& send_item) {
cartographer::common::MutexLocker locker(&send_queue_lock_); cartographer::common::MutexLocker locker(&send_queue_lock_);
send_queue_.emplace(std::move(send_item)); send_queue_.emplace(std::move(send_item));
@ -247,25 +265,21 @@ void Rpc::PerformFinish(std::unique_ptr<::google::protobuf::Message> message,
switch (rpc_handler_info_.rpc_type) { switch (rpc_handler_info_.rpc_type) {
case ::grpc::internal::RpcMethod::BIDI_STREAMING: case ::grpc::internal::RpcMethod::BIDI_STREAMING:
CHECK(!message); CHECK(!message);
server_async_reader_writer_->Finish( server_async_reader_writer_->Finish(status, GetRpcEvent(Event::FINISH));
status, new RpcEvent{Event::FINISH, weak_ptr_factory_(this), true});
break; break;
case ::grpc::internal::RpcMethod::CLIENT_STREAMING: case ::grpc::internal::RpcMethod::CLIENT_STREAMING:
response_ = std::move(message); response_ = std::move(message);
SendUnaryFinish( SendUnaryFinish(server_async_reader_.get(), status, response_.get(),
server_async_reader_.get(), status, response_.get(), GetRpcEvent(Event::FINISH));
new RpcEvent{Event::FINISH, weak_ptr_factory_(this), true});
break; break;
case ::grpc::internal::RpcMethod::NORMAL_RPC: case ::grpc::internal::RpcMethod::NORMAL_RPC:
response_ = std::move(message); response_ = std::move(message);
SendUnaryFinish( SendUnaryFinish(server_async_response_writer_.get(), status,
server_async_response_writer_.get(), status, response_.get(), response_.get(), GetRpcEvent(Event::FINISH));
new RpcEvent{Event::FINISH, weak_ptr_factory_(this), true});
break; break;
case ::grpc::internal::RpcMethod::SERVER_STREAMING: case ::grpc::internal::RpcMethod::SERVER_STREAMING:
CHECK(!message); CHECK(!message);
server_async_writer_->Finish( server_async_writer_->Finish(status, GetRpcEvent(Event::FINISH));
status, new RpcEvent{Event::FINISH, weak_ptr_factory_(this), true});
break; break;
} }
} }
@ -278,11 +292,12 @@ void Rpc::PerformWrite(std::unique_ptr<::google::protobuf::Message> message,
::grpc::internal::RpcMethod::CLIENT_STREAMING); ::grpc::internal::RpcMethod::CLIENT_STREAMING);
SetRpcEventState(Event::WRITE, true); SetRpcEventState(Event::WRITE, true);
response_ = std::move(message); response_ = std::move(message);
async_writer_interface()->Write( async_writer_interface()->Write(*response_, GetRpcEvent(Event::WRITE));
*response_, new RpcEvent{Event::WRITE, weak_ptr_factory_(this), true});
} }
void Rpc::SetRpcEventState(Event event, bool pending) { void Rpc::SetRpcEventState(Event event, bool pending) {
// TODO(gaschler): Since the only usage is setting this true at creation,
// consider removing this method.
*GetRpcEventState(event) = pending; *GetRpcEventState(event) = pending;
} }

View File

@ -39,8 +39,6 @@ class Service;
// TODO(cschuet): Add a unittest that tests the logic of this class. // TODO(cschuet): Add a unittest that tests the logic of this class.
class Rpc { class Rpc {
public: public:
struct RpcEvent;
using EventQueue = cartographer::common::BlockingQueue<RpcEvent*>;
using WeakPtrFactory = std::function<std::weak_ptr<Rpc>(Rpc*)>; using WeakPtrFactory = std::function<std::weak_ptr<Rpc>(Rpc*)>;
enum class Event { enum class Event {
NEW_CONNECTION = 0, NEW_CONNECTION = 0,
@ -50,11 +48,61 @@ class Rpc {
FINISH, FINISH,
DONE DONE
}; };
struct RpcEvent {
struct EventBase {
explicit EventBase(Event event) : event(event) {}
virtual ~EventBase(){};
virtual void Handle() = 0;
const Event event; const Event event;
std::weak_ptr<Rpc> rpc;
bool ok;
}; };
class EventDeleter {
public:
enum Action { DELETE = 0, DO_NOT_DELETE };
// The default action 'DELETE' is used implicitly, for instance for a
// new UniqueEventPtr or a UniqueEventPtr that is created by
// 'return nullptr'.
EventDeleter() : action_(DELETE) {}
explicit EventDeleter(Action action) : action_(action) {}
void operator()(EventBase* e) {
if (e != nullptr && action_ == DELETE) {
delete e;
}
}
private:
Action action_;
};
using UniqueEventPtr = std::unique_ptr<EventBase, EventDeleter>;
using EventQueue = cartographer::common::BlockingQueue<UniqueEventPtr>;
// Flows through gRPC's CompletionQueue and then our EventQueue.
struct CompletionQueueRpcEvent : public EventBase {
CompletionQueueRpcEvent(Event event, Rpc* rpc)
: EventBase(event), rpc_ptr(rpc), ok(false), pending(false) {}
void PushToEventQueue() {
rpc_ptr->event_queue()->Push(
UniqueEventPtr(this, EventDeleter(EventDeleter::DO_NOT_DELETE)));
}
void Handle() override;
Rpc* rpc_ptr;
bool ok;
bool pending;
};
// Flows only through our EventQueue.
struct InternalRpcEvent : public EventBase {
InternalRpcEvent(Event event, std::weak_ptr<Rpc> rpc)
: EventBase(event), rpc(rpc) {}
void Handle() override;
std::weak_ptr<Rpc> rpc;
};
Rpc(int method_index, ::grpc::ServerCompletionQueue* server_completion_queue, Rpc(int method_index, ::grpc::ServerCompletionQueue* server_completion_queue,
EventQueue* event_queue, ExecutionContext* execution_context, EventQueue* event_queue, ExecutionContext* execution_context,
const RpcHandlerInfo& rpc_handler_info, Service* service, const RpcHandlerInfo& rpc_handler_info, Service* service,
@ -69,7 +117,6 @@ class Rpc {
void Write(std::unique_ptr<::google::protobuf::Message> message); void Write(std::unique_ptr<::google::protobuf::Message> message);
void Finish(::grpc::Status status); void Finish(::grpc::Status status);
Service* service() { return service_; } Service* service() { return service_; }
void SetRpcEventState(Event event, bool pending);
bool IsRpcEventPending(Event event); bool IsRpcEventPending(Event event);
bool IsAnyEventPending(); bool IsAnyEventPending();
void SetEventQueue(EventQueue* event_queue) { event_queue_ = event_queue; } void SetEventQueue(EventQueue* event_queue) { event_queue_ = event_queue; }
@ -86,7 +133,9 @@ class Rpc {
Rpc& operator=(const Rpc&) = delete; Rpc& operator=(const Rpc&) = delete;
void InitializeReadersAndWriters( void InitializeReadersAndWriters(
::grpc::internal::RpcMethod::RpcType rpc_type); ::grpc::internal::RpcMethod::RpcType rpc_type);
CompletionQueueRpcEvent* GetRpcEvent(Event event);
bool* GetRpcEventState(Event event); bool* GetRpcEventState(Event event);
void SetRpcEventState(Event event, bool pending);
void EnqueueMessage(SendItem&& send_item); void EnqueueMessage(SendItem&& send_item);
void PerformFinish(std::unique_ptr<::google::protobuf::Message> message, void PerformFinish(std::unique_ptr<::google::protobuf::Message> message,
::grpc::Status status); ::grpc::Status status);
@ -109,16 +158,11 @@ class Rpc {
WeakPtrFactory weak_ptr_factory_; WeakPtrFactory weak_ptr_factory_;
::grpc::ServerContext server_context_; ::grpc::ServerContext server_context_;
// These state variables indicate whether the corresponding event is currently CompletionQueueRpcEvent new_connection_event_;
// pending completion, e.g. 'read_event_pending_ = true' means that a read has CompletionQueueRpcEvent read_event_;
// been requested but hasn't completed yet. 'read_event_pending_ = false' CompletionQueueRpcEvent write_event_;
// indicates that the read has completed and currently no read is in-flight. CompletionQueueRpcEvent finish_event_;
bool new_connection_event_pending_ = false; CompletionQueueRpcEvent done_event_;
bool read_event_pending_ = false;
bool write_needed_event_pending_ = false;
bool write_event_pending_ = false;
bool finish_event_pending_ = false;
bool done_event_pending_ = false;
std::unique_ptr<google::protobuf::Message> request_; std::unique_ptr<google::protobuf::Message> request_;
std::unique_ptr<google::protobuf::Message> response_; std::unique_ptr<google::protobuf::Message> response_;
@ -140,6 +184,8 @@ class Rpc {
std::queue<SendItem> send_queue_; std::queue<SendItem> send_queue_;
}; };
using EventQueue = Rpc::EventQueue;
// This class keeps track of all in-flight RPCs for a 'Service'. Make sure that // This class keeps track of all in-flight RPCs for a 'Service'. Make sure that
// all RPCs have been terminated and removed from this object before it goes out // all RPCs have been terminated and removed from this object before it goes out
// of scope. // of scope.

View File

@ -80,23 +80,10 @@ void Server::RunCompletionQueue(
bool ok; bool ok;
void* tag; void* tag;
while (completion_queue->Next(&tag, &ok)) { while (completion_queue->Next(&tag, &ok)) {
auto* rpc_event = static_cast<Rpc::RpcEvent*>(tag); auto* rpc_event = static_cast<Rpc::CompletionQueueRpcEvent*>(tag);
rpc_event->ok = ok; rpc_event->ok = ok;
if (auto rpc = rpc_event->rpc.lock()) { rpc_event->PushToEventQueue();
rpc->event_queue()->Push(rpc_event);
} else {
LOG(WARNING) << "Ignoring stale event.";
} }
}
}
void Server::ProcessRpcEvent(Rpc::RpcEvent* rpc_event) {
if (auto rpc = rpc_event->rpc.lock()) {
rpc->service()->HandleEvent(rpc_event->event, rpc.get(), rpc_event->ok);
} else {
LOG(WARNING) << "Ignoring stale event.";
}
delete rpc_event;
} }
EventQueue* Server::SelectNextEventQueueRoundRobin() { EventQueue* Server::SelectNextEventQueueRoundRobin() {
@ -108,16 +95,17 @@ EventQueue* Server::SelectNextEventQueueRoundRobin() {
void Server::RunEventQueue(EventQueue* event_queue) { void Server::RunEventQueue(EventQueue* event_queue) {
while (!shutting_down_) { while (!shutting_down_) {
Rpc::RpcEvent* rpc_event = event_queue->PopWithTimeout(kPopEventTimeout); Rpc::UniqueEventPtr rpc_event =
event_queue->PopWithTimeout(kPopEventTimeout);
if (rpc_event) { if (rpc_event) {
ProcessRpcEvent(rpc_event); rpc_event->Handle();
} }
} }
// Finish processing the rest of the items. // Finish processing the rest of the items.
while (Rpc::RpcEvent* rpc_event = while (Rpc::UniqueEventPtr rpc_event =
event_queue->PopWithTimeout(kPopEventTimeout)) { event_queue->PopWithTimeout(kPopEventTimeout)) {
ProcessRpcEvent(rpc_event); rpc_event->Handle();
} }
} }

View File

@ -112,9 +112,8 @@ class Server {
const std::string& service_name, const std::string& service_name,
const std::map<std::string, RpcHandlerInfo>& rpc_handler_infos); const std::map<std::string, RpcHandlerInfo>& rpc_handler_infos);
void RunCompletionQueue(::grpc::ServerCompletionQueue* completion_queue); void RunCompletionQueue(::grpc::ServerCompletionQueue* completion_queue);
void RunEventQueue(EventQueue* event_queue); void RunEventQueue(Rpc::EventQueue* event_queue);
void ProcessRpcEvent(Rpc::RpcEvent* rpc_event); Rpc::EventQueue* SelectNextEventQueueRoundRobin();
EventQueue* SelectNextEventQueueRoundRobin();
Options options_; Options options_;

View File

@ -58,7 +58,6 @@ void Service::StartServing(
void Service::StopServing() { shutting_down_ = true; } void Service::StopServing() { shutting_down_ = true; }
void Service::HandleEvent(Rpc::Event event, Rpc* rpc, bool ok) { void Service::HandleEvent(Rpc::Event event, Rpc* rpc, bool ok) {
rpc->SetRpcEventState(event, false);
switch (event) { switch (event) {
case Rpc::Event::NEW_CONNECTION: case Rpc::Event::NEW_CONNECTION:
HandleNewConnection(rpc, ok); HandleNewConnection(rpc, ok);