Files
sdk/runtime/vm/message_handler.cc
T
turnidge@google.com c658fa60f9 Fix deadlock that can occur while handling service messages at a breakpoint.
Deadlock looks like:

  Regular Isolate
  -> paused at breakpoint in debug message loop
  -> holds debug message queue lock to get message notifications
  -> handles an service message
  -> waits for portmap lock to send response

  Service Isolate
  -> receives request for paused isolate
  -> holds portmap lock to send message
  -> runs custom message notifier to wake debug message loop
  -> waits for debug message queue lock

I've solved this by releasing the debug message queue lock while
handling service messages.  This requires me to poll for new service
messages after reacquiring the debug message queue lock to make sure I
haven't dropped any notifications.

It's a little weird that the embedder (runtime/bin) needs to be aware
of the locking in the core vm (runtime/vm), but this seemed like the
simplest fix for now.

BUG=
R=johnmccutchan@google.com

Review URL: https://codereview.chromium.org//629533002

git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart@41326 260f80e4-7a28-3924-810f-c04153c831b5
2014-10-27 16:57:21 +00:00

316 lines
8.2 KiB
C++

// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
#include "vm/message_handler.h"
#include "vm/dart.h"
#include "vm/lockers.h"
#include "vm/port.h"
namespace dart {
DECLARE_FLAG(bool, trace_isolates);
DECLARE_FLAG(bool, trace_service_pause_events);
class MessageHandlerTask : public ThreadPool::Task {
public:
explicit MessageHandlerTask(MessageHandler* handler)
: handler_(handler) {
ASSERT(handler != NULL);
}
virtual void Run() {
handler_->TaskCallback();
}
private:
MessageHandler* handler_;
DISALLOW_COPY_AND_ASSIGN(MessageHandlerTask);
};
MessageHandler::MessageHandler()
: queue_(new MessageQueue()),
oob_queue_(new MessageQueue()),
live_ports_(0),
paused_(0),
pause_on_start_(false),
pause_on_exit_(false),
paused_on_exit_(false),
pool_(NULL),
task_(NULL),
start_callback_(NULL),
end_callback_(NULL),
callback_data_(0) {
ASSERT(queue_ != NULL);
ASSERT(oob_queue_ != NULL);
}
MessageHandler::~MessageHandler() {
delete queue_;
delete oob_queue_;
}
const char* MessageHandler::name() const {
return "<unnamed>";
}
#if defined(DEBUG)
void MessageHandler::CheckAccess() {
// By default there is no checking.
}
#endif
void MessageHandler::MessageNotify(Message::Priority priority) {
// By default, there is no custom message notification.
}
void MessageHandler::Run(ThreadPool* pool,
StartCallback start_callback,
EndCallback end_callback,
CallbackData data) {
MonitorLocker ml(&monitor_);
if (FLAG_trace_isolates) {
OS::Print("[+] Starting message handler:\n"
"\thandler: %s\n",
name());
}
ASSERT(pool_ == NULL);
pool_ = pool;
start_callback_ = start_callback;
end_callback_ = end_callback;
callback_data_ = data;
task_ = new MessageHandlerTask(this);
pool_->Run(task_);
}
void MessageHandler::PostMessage(Message* message) {
MonitorLocker ml(&monitor_);
if (FLAG_trace_isolates) {
const char* source_name = "<native code>";
Isolate* source_isolate = Isolate::Current();
if (source_isolate) {
source_name = source_isolate->name();
}
OS::Print("[>] Posting message:\n"
"\tsource: %s\n"
"\tdest: %s\n"
"\tdest_port: %" Pd64 "\n",
source_name, name(), message->dest_port());
}
Message::Priority saved_priority = message->priority();
if (message->IsOOB()) {
oob_queue_->Enqueue(message);
} else {
queue_->Enqueue(message);
}
message = NULL; // Do not access message. May have been deleted.
if (pool_ != NULL && task_ == NULL) {
task_ = new MessageHandlerTask(this);
pool_->Run(task_);
}
// Invoke any custom message notification.
MessageNotify(saved_priority);
}
Message* MessageHandler::DequeueMessage(Message::Priority min_priority) {
// TODO(turnidge): Add assert that monitor_ is held here.
Message* message = oob_queue_->Dequeue();
if ((message == NULL) && (min_priority < Message::kOOBPriority)) {
message = queue_->Dequeue();
}
return message;
}
bool MessageHandler::HandleMessages(bool allow_normal_messages,
bool allow_multiple_normal_messages) {
// TODO(turnidge): Add assert that monitor_ is held here.
bool result = true;
Message::Priority min_priority = (allow_normal_messages && !paused()) ?
Message::kNormalPriority : Message::kOOBPriority;
Message* message = DequeueMessage(min_priority);
while (message != NULL) {
if (FLAG_trace_isolates) {
OS::Print("[<] Handling message:\n"
"\thandler: %s\n"
"\tport: %" Pd64 "\n",
name(), message->dest_port());
}
// Release the monitor_ temporarily while we handle the message.
// The monitor was acquired in MessageHandler::TaskCallback().
monitor_.Exit();
Message::Priority saved_priority = message->priority();
result = HandleMessage(message);
monitor_.Enter();
if (FLAG_trace_isolates) {
OS::Print("[.] Message handled:\n"
"\thandler: %s\n"
"\tport: %" Pd64 "\n",
name(), message->dest_port());
}
if (!result) {
// If we hit an error, we're done processing messages.
break;
}
// Some callers want to process only one normal message and then quit. At
// the same time it is OK to process multiple OOB messages.
if ((saved_priority == Message::kNormalPriority) &&
!allow_multiple_normal_messages) {
break;
}
// Reevaluate the minimum allowable priority as the paused state might
// have changed as part of handling the message.
min_priority = (allow_normal_messages && !paused()) ?
Message::kNormalPriority : Message::kOOBPriority;
message = DequeueMessage(min_priority);
}
return result;
}
bool MessageHandler::HandleNextMessage() {
// We can only call HandleNextMessage when this handler is not
// assigned to a thread pool.
MonitorLocker ml(&monitor_);
ASSERT(pool_ == NULL);
#if defined(DEBUG)
CheckAccess();
#endif
return HandleMessages(true, false);
}
bool MessageHandler::HandleOOBMessages() {
MonitorLocker ml(&monitor_);
#if defined(DEBUG)
CheckAccess();
#endif
return HandleMessages(false, false);
}
bool MessageHandler::HasOOBMessages() {
MonitorLocker ml(&monitor_);
return !oob_queue_->IsEmpty();
}
void MessageHandler::TaskCallback() {
ASSERT(Isolate::Current() == NULL);
bool ok = true;
bool run_end_callback = false;
{
MonitorLocker ml(&monitor_);
// Initialize the message handler by running its start function,
// if we have one. For an isolate, this will run the isolate's
// main() function.
if (pause_on_start()) {
HandleMessages(false, false);
if (pause_on_start()) {
// Still paused.
task_ = NULL; // No task in queue.
return;
}
}
if (start_callback_) {
// Release the monitor_ temporarily while we call the start callback.
// The monitor was acquired with the MonitorLocker above.
monitor_.Exit();
ok = start_callback_(callback_data_);
ASSERT(Isolate::Current() == NULL);
start_callback_ = NULL;
monitor_.Enter();
}
// Handle any pending messages for this message handler.
if (ok) {
ok = HandleMessages(true, true);
}
task_ = NULL; // No task in queue.
if (!ok || !HasLivePorts()) {
if (pause_on_exit()) {
if (FLAG_trace_service_pause_events && !paused_on_exit_) {
OS::PrintErr("Isolate %s paused before exiting. "
"Use the Observatory to release it.\n", name());
}
paused_on_exit_ = true;
} else {
if (FLAG_trace_isolates) {
OS::Print("[-] Stopping message handler (%s):\n"
"\thandler: %s\n",
(ok ? "no live ports" : "error"),
name());
}
pool_ = NULL;
run_end_callback = true;
paused_on_exit_ = false;
}
}
}
if (run_end_callback && end_callback_ != NULL) {
end_callback_(callback_data_);
// The handler may have been deleted after this point.
}
}
void MessageHandler::ClosePort(Dart_Port port) {
MonitorLocker ml(&monitor_);
if (FLAG_trace_isolates) {
OS::Print("[-] Closing port:\n"
"\thandler: %s\n"
"\tport: %" Pd64 "\n"
"\tports: live(%" Pd ")\n",
name(), port, live_ports_);
}
}
void MessageHandler::CloseAllPorts() {
MonitorLocker ml(&monitor_);
if (FLAG_trace_isolates) {
OS::Print("[-] Closing all ports:\n"
"\thandler: %s\n",
name());
}
queue_->Clear();
oob_queue_->Clear();
}
void MessageHandler::increment_live_ports() {
MonitorLocker ml(&monitor_);
#if defined(DEBUG)
CheckAccess();
#endif
live_ports_++;
}
void MessageHandler::decrement_live_ports() {
MonitorLocker ml(&monitor_);
#if defined(DEBUG)
CheckAccess();
#endif
live_ports_--;
}
} // namespace dart