From e1a676b62821b9599e5d46e330f26dee5dd44c79 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Cristi=C3=A1n=20Donoso?= <donosoc@google.com>
Date: Wed, 17 Apr 2019 00:29:48 +0000
Subject: [PATCH] [debugger] Removed MessageLoopZircon

This also remove the MessageLoopTarget as an interface and makes what
previous was MessageLoopAsync that. This means that now there is no
abstract interface for the target but rather a concrete implementation.

TC-447 # Removed from debug agent.

TEST=Manual: Running binaries and components.
     Unit & Integration tests (fx run-test debug_agent_tests)

Change-Id: Ia2b41ca660cebae70d8d35e1348c97cbddffab68
---
 .../debug/debug_agent/debug_agent.cc          |   5 -
 .../integration_tests/message_loop_wrapper.cc |   4 +-
 src/developer/debug/debug_agent/main.cc       |  36 +-
 src/developer/debug/shared/BUILD.gn           |  10 -
 src/developer/debug/shared/event_handlers.cc  |   6 +-
 .../debug/shared/message_loop_async.cc        | 580 ------------------
 .../debug/shared/message_loop_async.h         | 183 ------
 .../debug/shared/message_loop_target.cc       | 577 ++++++++++++++++-
 .../debug/shared/message_loop_target.h        | 173 +++++-
 .../debug/shared/message_loop_zircon.cc       | 534 ----------------
 .../debug/shared/message_loop_zircon.h        |  94 ---
 11 files changed, 700 insertions(+), 1502 deletions(-)
 delete mode 100644 src/developer/debug/shared/message_loop_async.cc
 delete mode 100644 src/developer/debug/shared/message_loop_async.h
 delete mode 100644 src/developer/debug/shared/message_loop_zircon.cc
 delete mode 100644 src/developer/debug/shared/message_loop_zircon.h

diff --git a/src/developer/debug/debug_agent/debug_agent.cc b/src/developer/debug/debug_agent/debug_agent.cc
index 40106851973..66c5ee5027e 100644
--- a/src/developer/debug/debug_agent/debug_agent.cc
+++ b/src/developer/debug/debug_agent/debug_agent.cc
@@ -536,11 +536,6 @@ void DebugAgent::LaunchComponent(const debug_ipc::LaunchRequest& request,
   *reply = {};
   reply->inferior_type = debug_ipc::InferiorType::kComponent;
 
-  if (!debug_ipc::MessageLoopTarget::Current()->SupportsFidl()) {
-    reply->status = ZX_ERR_NOT_SUPPORTED;
-    return;
-  }
-
   ComponentLauncher component_launcher(services_);
 
   ComponentDescription description;
diff --git a/src/developer/debug/debug_agent/integration_tests/message_loop_wrapper.cc b/src/developer/debug/debug_agent/integration_tests/message_loop_wrapper.cc
index a171d98d431..aedfd114eea 100644
--- a/src/developer/debug/debug_agent/integration_tests/message_loop_wrapper.cc
+++ b/src/developer/debug/debug_agent/integration_tests/message_loop_wrapper.cc
@@ -4,12 +4,12 @@
 
 #include "src/developer/debug/debug_agent/integration_tests/message_loop_wrapper.h"
 
-#include "src/developer/debug/shared/message_loop_async.h"
+#include "src/developer/debug/shared/message_loop_target.h"
 
 namespace debug_agent {
 
 MessageLoopWrapper::MessageLoopWrapper() {
-  loop_ = std::make_unique<debug_ipc::MessageLoopAsync>();
+  loop_ = std::make_unique<debug_ipc::MessageLoopTarget>();
   loop_->Init();
 }
 
diff --git a/src/developer/debug/debug_agent/main.cc b/src/developer/debug/debug_agent/main.cc
index 98e1d567dba..63115b1bbfe 100644
--- a/src/developer/debug/debug_agent/main.cc
+++ b/src/developer/debug/debug_agent/main.cc
@@ -20,9 +20,7 @@
 #include "src/developer/debug/debug_agent/unwind.h"
 #include "src/developer/debug/shared/buffered_fd.h"
 #include "src/developer/debug/shared/logging/logging.h"
-#include "src/developer/debug/shared/message_loop_async.h"
 #include "src/developer/debug/shared/message_loop_target.h"
-#include "src/developer/debug/shared/message_loop_zircon.h"
 #include "src/developer/debug/shared/zx_status.h"
 #include "src/lib/files/unique_fd.h"
 
@@ -31,21 +29,6 @@ using namespace debug_ipc;
 namespace debug_agent {
 namespace {
 
-std::unique_ptr<debug_ipc::MessageLoopTarget> GetMessageLoop(
-    MessageLoopTarget::Type type) {
-  switch (type) {
-    case MessageLoopTarget::Type::kAsync:
-      return std::make_unique<debug_ipc::MessageLoopAsync>();
-    case MessageLoopTarget::Type::kZircon:
-      return std::make_unique<debug_ipc::MessageLoopZircon>();
-    case MessageLoopTarget::Type::kLast:
-      break;
-  }
-
-  FXL_NOTREACHED();
-  return nullptr;
-}
-
 // SocketConnection ------------------------------------------------------------
 
 // Represents one connection to a client.
@@ -173,9 +156,6 @@ const std::map<std::string, std::string>& GetOptions() {
   static std::map<std::string, std::string> options = {
       {"auwind", R"(
       [Experimental] Use the unwinder from AOSP.)"},
-      {"legacy-message-loop", R"(
-      [DEPRECATED] Use the originalbackend message loop.
-                   Will be removed eventually.)"},
       {"debug-mode", R"(
       Run the agent on debug mode. This will enable conditional logging messages
       and timing profiling. Mainly useful for people developing zxdb.)"},
@@ -241,13 +221,6 @@ int main(int argc, char* argv[]) {
     debug_agent::SetUnwinderType(debug_agent::UnwinderType::kAndroid);
   }
 
-  // By default use the async agent message loop.
-  auto message_loop_type = MessageLoopTarget::Type::kAsync;
-  if (cmdline.HasOption("legacy-message-loop")) {
-    // Use new async loop.
-    message_loop_type = MessageLoopTarget::Type::kZircon;
-  }
-
   // TODO(donosoc): Do correct category setup.
   debug_ipc::SetLogCategories({LogCategory::kAll});
   if (cmdline.HasOption("debug-mode")) {
@@ -267,14 +240,11 @@ int main(int argc, char* argv[]) {
 
     auto services = sys::ServiceDirectory::CreateFromNamespace();
 
-    printf("Using %s message loop.\n",
-           MessageLoopTarget::TypeToString(message_loop_type));
-    auto message_loop = debug_agent::GetMessageLoop(message_loop_type);
+    auto message_loop = std::make_unique<MessageLoopTarget>();
     zx_status_t status = message_loop->InitTarget();
     if (status != ZX_OK) {
-      const char* type = MessageLoopTarget::TypeToString(message_loop_type);
-      FXL_LOG(ERROR) << "Could not initialize message loop (type: " << type
-                     << "): " << debug_ipc::ZxStatusToString(status);
+      FXL_LOG(ERROR) << "Could not initialize message loop: "
+                     << debug_ipc::ZxStatusToString(status);
     }
 
     // The scope ensures the objects are destroyed before calling Cleanup on the
diff --git a/src/developer/debug/shared/BUILD.gn b/src/developer/debug/shared/BUILD.gn
index f65d5b2b3df..28a077c3073 100644
--- a/src/developer/debug/shared/BUILD.gn
+++ b/src/developer/debug/shared/BUILD.gn
@@ -49,18 +49,8 @@ static_library("shared") {
       "buffered_zx_socket.h",
       "event_handlers.cc",
       "event_handlers.h",
-
-      # Implementation of message loop using async-loop.
-      "message_loop_async.cc",
-      "message_loop_async.h",
-
-      # New interface to abstract message loop from the debug agent.
       "message_loop_target.cc",
       "message_loop_target.h",
-
-      # Original message loop (previously MessageLoopZircon).
-      "message_loop_zircon.cc",
-      "message_loop_zircon.h",
       "zircon_utils.cc",
       "zircon_utils.h",
     ]
diff --git a/src/developer/debug/shared/event_handlers.cc b/src/developer/debug/shared/event_handlers.cc
index ed27c47ee0c..7088e4d7773 100644
--- a/src/developer/debug/shared/event_handlers.cc
+++ b/src/developer/debug/shared/event_handlers.cc
@@ -7,7 +7,7 @@
 #include <lib/async-loop/loop.h>
 #include <lib/async/default.h>
 
-#include "src/developer/debug/shared/message_loop_async.h"
+#include "src/developer/debug/shared/message_loop_target.h"
 #include "src/developer/debug/shared/logging/logging.h"
 #include "src/developer/debug/shared/zircon_utils.h"
 #include "src/developer/debug/shared/zx_status.h"
@@ -53,7 +53,7 @@ void SignalHandler::Handler(async_dispatcher_t*, async_wait_t* wait,
                             const zx_packet_signal_t* signal) {
   FXL_DCHECK(status == ZX_OK);
 
-  auto* loop = MessageLoopAsync::Current();
+  auto* loop = MessageLoopTarget::Current();
   FXL_DCHECK(loop);
 
   // Search for the AsyncHandle that triggered this signal.
@@ -140,7 +140,7 @@ void ExceptionHandler::Handler(async_dispatcher_t*,
                            << ExceptionTypeToString(packet->type);
   }
 
-  auto* loop = MessageLoopAsync::Current();
+  auto* loop = MessageLoopTarget::Current();
   FXL_DCHECK(loop);
 
   auto handler_it = loop->exception_handlers().find(exception);
diff --git a/src/developer/debug/shared/message_loop_async.cc b/src/developer/debug/shared/message_loop_async.cc
deleted file mode 100644
index 8a99a64dd89..00000000000
--- a/src/developer/debug/shared/message_loop_async.cc
+++ /dev/null
@@ -1,580 +0,0 @@
-// Copyright 2018 The Fuchsia Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "src/developer/debug/shared/message_loop_async.h"
-
-#include <lib/fdio/io.h>
-#include <lib/zx/handle.h>
-#include <lib/zx/job.h>
-#include <lib/zx/process.h>
-#include <stdio.h>
-#include <zircon/syscalls/exception.h>
-
-#include "src/developer/debug/shared/event_handlers.h"
-#include "src/developer/debug/shared/fd_watcher.h"
-#include "src/developer/debug/shared/logging/logging.h"
-#include "src/developer/debug/shared/socket_watcher.h"
-#include "src/developer/debug/shared/zircon_exception_watcher.h"
-#include "src/developer/debug/shared/zx_status.h"
-#include "src/lib/fxl/compiler_specific.h"
-#include "src/lib/fxl/logging.h"
-
-namespace debug_ipc {
-
-namespace {
-
-thread_local MessageLoopAsync* current_message_loop_async = nullptr;
-
-}  // namespace
-
-// Exception ------------------------------------------------------------
-
-struct MessageLoopAsync::Exception {
-  zx_koid_t thread_koid = 0;
-  // Not-owning. Must outlive.
-  async_exception_t* exception_token = nullptr;
-};
-
-// MessageLoopAsync -----------------------------------------------------------
-
-MessageLoopAsync::MessageLoopAsync() : loop_(&kAsyncLoopConfigAttachToThread) {}
-
-MessageLoopAsync::~MessageLoopAsync() {
-  FXL_DCHECK(Current() != this);  // Cleanup should have been called.
-}
-
-void MessageLoopAsync::Init() { InitTarget(); }
-
-zx_status_t MessageLoopAsync::InitTarget() {
-  MessageLoop::Init();
-
-  FXL_DCHECK(!current_message_loop_async);
-  current_message_loop_async = this;
-  MessageLoopTarget::current_message_loop_type =
-      MessageLoopTarget::Type::kAsync;
-
-  zx::event::create(0, &task_event_);
-
-  WatchInfo info;
-  info.type = WatchType::kTask;
-  zx_status_t status =
-      AddSignalHandler(kTaskSignalKey, task_event_.get(), kTaskSignal, &info);
-
-  if (status != ZX_OK)
-    return status;
-
-  watches_[kTaskSignalKey] = std::move(info);
-  return ZX_OK;
-}
-
-void MessageLoopAsync::Cleanup() {
-  // We need to remove the signal/exception handlers before the message loop
-  // goes away.
-  signal_handlers_.clear();
-  exception_handlers_.clear();
-
-  FXL_DCHECK(current_message_loop_async == this);
-  current_message_loop_async = nullptr;
-  MessageLoopTarget::current_message_loop_type = MessageLoopTarget::Type::kLast;
-
-  MessageLoop::Cleanup();
-}
-
-// static
-MessageLoopAsync* MessageLoopAsync::Current() {
-  return current_message_loop_async;
-}
-
-const MessageLoopAsync::WatchInfo* MessageLoopAsync::FindWatchInfo(
-    int id) const {
-  auto it = watches_.find(id);
-  if (it == watches_.end())
-    return nullptr;
-  return &it->second;
-}
-
-zx_status_t MessageLoopAsync::AddSignalHandler(int id, zx_handle_t object,
-                                               zx_signals_t signals,
-                                               WatchInfo* associated_info) {
-  SignalHandler handler;
-  zx_status_t status = handler.Init(id, object, signals);
-  if (status != ZX_OK)
-    return status;
-
-  // The handler should not be there already.
-  FXL_DCHECK(signal_handlers_.find(handler.handle()) == signal_handlers_.end());
-
-  associated_info->signal_handler_key = handler.handle();
-  signal_handlers_[handler.handle()] = std::move(handler);
-
-  return ZX_OK;
-}
-
-zx_status_t MessageLoopAsync::AddExceptionHandler(int id, zx_handle_t object,
-                                                  uint32_t options,
-                                                  WatchInfo* associated_info) {
-  ExceptionHandler handler;
-  zx_status_t status = handler.Init(id, object, options);
-  if (status != ZX_OK)
-    return status;
-
-  // The handler should not be there already.
-  FXL_DCHECK(exception_handlers_.find(handler.handle()) ==
-             exception_handlers_.end());
-
-  associated_info->exception_handler_key = handler.handle();
-  exception_handlers_[handler.handle()] = std::move(handler);
-
-  return ZX_OK;
-}
-
-MessageLoop::WatchHandle MessageLoopAsync::WatchFD(WatchMode mode, int fd,
-                                                   FDWatcher* watcher) {
-  WatchInfo info;
-  info.type = WatchType::kFdio;
-  info.fd_watcher = watcher;
-  info.fd = fd;
-  info.fdio = fdio_unsafe_fd_to_io(fd);
-  if (!info.fdio)
-    return WatchHandle();
-
-  uint32_t events = 0;
-  switch (mode) {
-    case WatchMode::kRead:
-      events = POLLIN;
-      break;
-    case WatchMode::kWrite:
-      events = POLLOUT;
-      break;
-    case WatchMode::kReadWrite:
-      events = POLLIN | POLLOUT;
-      break;
-  }
-
-  zx_signals_t signals = ZX_SIGNAL_NONE;
-  fdio_unsafe_wait_begin(info.fdio, events, &info.fd_handle, &signals);
-  if (info.fd_handle == ZX_HANDLE_INVALID)
-    return WatchHandle();
-
-  int watch_id;
-  {
-    std::lock_guard<std::mutex> guard(mutex_);
-
-    watch_id = next_watch_id_;
-    next_watch_id_++;
-  }
-
-  zx_status_t status =
-      AddSignalHandler(watch_id, info.fd_handle, signals, &info);
-  if (status != ZX_OK)
-    return WatchHandle();
-
-  watches_[watch_id] = info;
-  return WatchHandle(this, watch_id);
-}
-
-zx_status_t MessageLoopAsync::WatchSocket(WatchMode mode,
-                                          zx_handle_t socket_handle,
-                                          SocketWatcher* watcher,
-                                          MessageLoop::WatchHandle* out) {
-  WatchInfo info;
-  info.type = WatchType::kSocket;
-  info.socket_watcher = watcher;
-  info.socket_handle = socket_handle;
-
-  int watch_id;
-  {
-    std::lock_guard<std::mutex> guard(mutex_);
-
-    watch_id = next_watch_id_;
-    next_watch_id_++;
-  }
-
-  zx_signals_t signals = 0;
-  if (mode == WatchMode::kRead || mode == WatchMode::kReadWrite)
-    signals |= ZX_SOCKET_READABLE;
-
-  if (mode == WatchMode::kWrite || mode == WatchMode::kReadWrite)
-    signals |= ZX_SOCKET_WRITABLE;
-
-  zx_status_t status =
-      AddSignalHandler(watch_id, socket_handle, ZX_SOCKET_WRITABLE, &info);
-  if (status != ZX_OK)
-    return status;
-
-  watches_[watch_id] = info;
-  *out = WatchHandle(this, watch_id);
-  return ZX_OK;
-}
-
-zx_status_t MessageLoopAsync::WatchProcessExceptions(
-    WatchProcessConfig config, MessageLoop::WatchHandle* out) {
-  WatchInfo info;
-  info.resource_name = config.process_name;
-  info.type = WatchType::kProcessExceptions;
-  info.exception_watcher = config.watcher;
-  info.task_koid = config.process_koid;
-  info.task_handle = config.process_handle;
-
-  int watch_id;
-  {
-    std::lock_guard<std::mutex> guard(mutex_);
-
-    watch_id = next_watch_id_;
-    next_watch_id_++;
-  }
-
-  // Watch all exceptions for the process.
-  zx_status_t status;
-  status = AddExceptionHandler(watch_id, config.process_handle,
-                               ZX_EXCEPTION_PORT_DEBUGGER, &info);
-  if (status != ZX_OK)
-    return status;
-
-  // Watch for the process terminated signal.
-  status = AddSignalHandler(watch_id, config.process_handle,
-                            ZX_PROCESS_TERMINATED, &info);
-  if (status != ZX_OK)
-    return status;
-
-  DEBUG_LOG(MessageLoop) << "Watching process " << info.resource_name;
-
-  watches_[watch_id] = info;
-  *out = WatchHandle(this, watch_id);
-  return ZX_OK;
-}
-
-zx_status_t MessageLoopAsync::WatchJobExceptions(
-    WatchJobConfig config, MessageLoop::WatchHandle* out) {
-  WatchInfo info;
-  info.resource_name = config.job_name;
-  info.type = WatchType::kJobExceptions;
-  info.exception_watcher = config.watcher;
-  info.task_koid = config.job_koid;
-  info.task_handle = config.job_handle;
-
-  int watch_id;
-  {
-    std::lock_guard<std::mutex> guard(mutex_);
-
-    watch_id = next_watch_id_;
-    next_watch_id_++;
-  }
-
-  // Create and track the exception handle.
-  zx_status_t status = AddExceptionHandler(watch_id, config.job_handle,
-                                           ZX_EXCEPTION_PORT_DEBUGGER, &info);
-  if (status != ZX_OK)
-    return status;
-
-  DEBUG_LOG(MessageLoop) << "Watching job " << info.resource_name;
-
-  watches_[watch_id] = info;
-  *out = WatchHandle(this, watch_id);
-  return ZX_OK;
-}
-
-zx_status_t MessageLoopAsync::ResumeFromException(zx_koid_t thread_koid,
-                                                  zx::thread& thread,
-                                                  uint32_t options) {
-  auto it = thread_exception_map_.find(thread_koid);
-  FXL_DCHECK(it != thread_exception_map_.end());
-  zx_status_t res = async_resume_from_exception(async_get_default_dispatcher(),
-                                                it->second.exception_token,
-                                                thread.get(), options);
-  thread_exception_map_.erase(thread_koid);
-  return res;
-}
-
-bool MessageLoopAsync::CheckAndProcessPendingTasks() {
-  std::lock_guard<std::mutex> guard(mutex_);
-  // Do a C++ task.
-  if (ProcessPendingTask()) {
-    SetHasTasks();  // Enqueue another task signal.
-    return true;
-  }
-  return false;
-}
-
-void MessageLoopAsync::HandleException(const ExceptionHandler& handler,
-                                       zx_port_packet_t packet) {
-  WatchInfo* watch_info = nullptr;
-  {
-    // Some event being watched.
-    std::lock_guard<std::mutex> guard(mutex_);
-    auto found = watches_.find(packet.key);
-    if (found == watches_.end()) {
-      // It is possible to get an exception that doesn't have a watch handle.
-      // A case is a race between detaching from a process and getting an
-      // exception on that process.
-      //
-      // The normal process looks like this:
-      //
-      // 1. In order to correctly detach, the debug agent has to resume threads
-      //    from their exceptions. Otherwise that exception will be treated as
-      //    unhandled when the agent detaches and will bubble up.
-      // 2. The agent detaches from the exception port. This means that the
-      //    watch handle is no longer listening.
-      //
-      // It is possible between (1) and (2) to get an exception, which will be
-      // queued in the exception port of the thread. Now, the agent won't read
-      // from the port until *after* it has detached from the exception port.
-      // This means that this new exception is not handled and will be bubbled
-      // up, which is correct as the debug agent stated that it has nothing more
-      // to do with the process.
-      //
-      // Now the problem is that zircon does not clean stale packets from a
-      // queue, meaning that the next time the message loop waits on the port,
-      // it will find a stale packet. In this context a stale packet means one
-      // that does not have a watch handle, as it was deleted in (1). Hence we
-      // get into this case and we simply log it for posperity.
-      //
-      // TODO(ZX-2623): zircon is going to clean up stale packets from ports
-      //                in the future. When that is done, this case should not
-      //                happen and we should go back into asserting it.
-      FXL_LOG(WARNING) << "Got stale port packet. This is most probably due to "
-                          "a race between detaching from a process and an "
-                          "exception ocurring.";
-      return;
-    }
-    watch_info = &found->second;
-  }
-
-  // Dispatch the watch callback outside of the lock. This depends on the
-  // stability of the WatchInfo pointer in the map (std::map is stable across
-  // updates) and the watch not getting unregistered from another thread
-  // asynchronously (which the API requires and is enforced by a DCHECK in
-  // the StopWatching impl).
-  switch (watch_info->type) {
-    case WatchType::kProcessExceptions:
-      OnProcessException(handler, *watch_info, packet);
-      break;
-    case WatchType::kJobExceptions:
-      OnJobException(handler, *watch_info, packet);
-      break;
-    case WatchType::kTask:
-    case WatchType::kFdio:
-    case WatchType::kSocket:
-      FXL_NOTREACHED();
-  }
-}
-
-uint64_t MessageLoopAsync::GetMonotonicNowNS() const {
-  zx::time ret;
-  zx::clock::get(&ret);
-
-  return ret.get();
-}
-
-// Previously, the approach was to first look for C++ tasks and when handled
-// look for WatchHandle work and finally wait for an event. This worked because
-// handle events didn't post C++ tasks.
-//
-// But some tests do post tasks on handle events. Because C++ tasks are signaled
-// by explicitly signaling an zx::event, without manually checking, the C++
-// tasks will never be checked and we would get blocked until a watch handled
-// is triggered.
-//
-// In order to handle the events properly, we need to check for C++ tasks before
-// and *after* handling watch handle events. This way we always process C++
-// tasks before handle events and will get signaled if one of them posted a new
-// task.
-void MessageLoopAsync::RunImpl() {
-  // Init should have been called.
-  FXL_DCHECK(Current() == this);
-  zx_status_t status;
-
-  zx::time time;
-  uint64_t delay = DelayNS();
-  if (delay == MessageLoop::kMaxDelay) {
-    time = zx::time::infinite();
-  } else {
-    time = zx::deadline_after(zx::nsec(delay));
-  }
-
-  for (;;) {
-    status = loop_.ResetQuit();
-    FXL_DCHECK(status != ZX_ERR_BAD_STATE);
-    status = loop_.Run(time);
-    FXL_DCHECK(status == ZX_OK || status == ZX_ERR_CANCELED ||
-               status == ZX_ERR_TIMED_OUT)
-        << "Expected ZX_OK || ZX_ERR_CANCELED || ZX_ERR_TIMED_OUT, got "
-        << ZxStatusToString(status);
-
-    if (status != ZX_ERR_TIMED_OUT) {
-      return;
-    }
-
-    std::lock_guard<std::mutex> guard(mutex_);
-    if (ProcessPendingTask())
-      SetHasTasks();
-  }
-}
-
-void MessageLoopAsync::QuitNow() {
-  MessageLoop::QuitNow();
-  loop_.Quit();
-}
-
-void MessageLoopAsync::StopWatching(int id) {
-  // The dispatch code for watch callbacks requires this be called on the
-  // same thread as the message loop is.
-  FXL_DCHECK(Current() == this);
-
-  std::lock_guard<std::mutex> guard(mutex_);
-
-  auto found = watches_.find(id);
-  FXL_DCHECK(found != watches_.end());
-
-  WatchInfo& info = found->second;
-  // BufferedFD constantly creates and destroys FD handles, flooding the log
-  // with non-helpful logging statements.
-  if (info.type != WatchType::kFdio) {
-    DEBUG_LOG(MessageLoop) << "Stop watching " << WatchTypeToString(info.type)
-                           << " " << info.resource_name;
-  }
-
-  switch (info.type) {
-    case WatchType::kProcessExceptions: {
-      RemoveExceptionHandler(info.exception_handler_key);
-      RemoveSignalHandler(info.signal_handler_key);
-      break;
-    }
-    case WatchType::kJobExceptions: {
-      RemoveExceptionHandler(info.exception_handler_key);
-      break;
-    }
-    case WatchType::kTask:
-    case WatchType::kFdio:
-    case WatchType::kSocket:
-      RemoveSignalHandler(info.signal_handler_key);
-      break;
-  }
-  watches_.erase(found);
-}
-
-void MessageLoopAsync::SetHasTasks() { task_event_.signal(0, kTaskSignal); }
-
-void MessageLoopAsync::OnFdioSignal(int watch_id, const WatchInfo& info,
-                                    zx_signals_t observed) {
-  uint32_t events = 0;
-  fdio_unsafe_wait_end(info.fdio, observed, &events);
-
-  if ((events & POLLERR) || (events & POLLHUP) || (events & POLLNVAL) ||
-      (events & POLLRDHUP)) {
-    info.fd_watcher->OnFDReady(info.fd, false, false, true);
-
-    // Don't dispatch any other notifications when there's an error. Zircon
-    // seems to set readable and writable on error even if there's nothing
-    // there.
-    return;
-  }
-
-  bool readable = !!(events & POLLIN);
-  bool writable = !!(events & POLLOUT);
-  info.fd_watcher->OnFDReady(info.fd, readable, writable, false);
-}
-
-void MessageLoopAsync::RemoveSignalHandler(const async_wait_t* key) {
-  FXL_DCHECK(key);
-  size_t erase_count = signal_handlers_.erase(key);
-  FXL_DCHECK(erase_count == 1u);
-}
-
-void MessageLoopAsync::RemoveExceptionHandler(const async_exception_t* key) {
-  FXL_DCHECK(key);
-  size_t erase_count = exception_handlers_.erase(key);
-  FXL_DCHECK(erase_count == 1u);
-}
-
-void MessageLoopAsync::AddException(const ExceptionHandler& handler,
-                                    zx_koid_t thread_koid) {
-  FXL_DCHECK(thread_exception_map_.find(thread_koid) ==
-             thread_exception_map_.end());
-
-  Exception exception;
-  exception.thread_koid = thread_koid;
-  exception.exception_token = const_cast<async_exception_t*>(handler.handle());
-  thread_exception_map_[thread_koid] = std::move(exception);
-}
-
-void MessageLoopAsync::OnProcessException(const ExceptionHandler& handler,
-                                          const WatchInfo& info,
-                                          const zx_port_packet_t& packet) {
-  if (ZX_PKT_IS_EXCEPTION(packet.type)) {
-    // All debug exceptions.
-    switch (packet.type) {
-      case ZX_EXCP_THREAD_STARTING:
-        AddException(handler, packet.exception.tid);
-        info.exception_watcher->OnThreadStarting(info.task_koid,
-                                                 packet.exception.tid);
-        break;
-      case ZX_EXCP_THREAD_EXITING:
-        AddException(handler, packet.exception.tid);
-        info.exception_watcher->OnThreadExiting(info.task_koid,
-                                                packet.exception.tid);
-        break;
-      case ZX_EXCP_GENERAL:
-      case ZX_EXCP_FATAL_PAGE_FAULT:
-      case ZX_EXCP_UNDEFINED_INSTRUCTION:
-      case ZX_EXCP_SW_BREAKPOINT:
-      case ZX_EXCP_HW_BREAKPOINT:
-      case ZX_EXCP_UNALIGNED_ACCESS:
-      case ZX_EXCP_POLICY_ERROR:
-        AddException(handler, packet.exception.tid);
-        info.exception_watcher->OnException(info.task_koid,
-                                            packet.exception.tid, packet.type);
-        break;
-      default:
-        FXL_NOTREACHED();
-    }
-  } else {
-    FXL_NOTREACHED();
-  }
-}
-
-void MessageLoopAsync::OnProcessTerminated(const WatchInfo& info,
-                                           zx_signals_t observed) {
-  FXL_DCHECK(observed & ZX_PROCESS_TERMINATED);
-  info.exception_watcher->OnProcessTerminated(info.task_koid);
-}
-
-void MessageLoopAsync::OnJobException(const ExceptionHandler& handler,
-                                      const WatchInfo& info,
-                                      const zx_port_packet_t& packet) {
-  if (ZX_PKT_IS_EXCEPTION(packet.type)) {
-    // All debug exceptions.
-    switch (packet.type) {
-      case ZX_EXCP_PROCESS_STARTING:
-        AddException(handler, packet.exception.tid);
-        info.exception_watcher->OnProcessStarting(
-            info.task_koid, packet.exception.pid, packet.exception.tid);
-        return;
-      default:
-        break;
-    }
-  }
-  FXL_NOTREACHED();
-}
-
-void MessageLoopAsync::OnSocketSignal(int watch_id, const WatchInfo& info,
-                                      zx_signals_t observed) {
-  // Dispatch readable signal.
-  if (observed & ZX_SOCKET_READABLE)
-    info.socket_watcher->OnSocketReadable(info.socket_handle);
-
-  // When signaling both readable and writable, make sure the readable handler
-  // didn't remove the watch.
-  if ((observed & ZX_SOCKET_READABLE) && (observed & ZX_SOCKET_WRITABLE)) {
-    std::lock_guard<std::mutex> guard(mutex_);
-    if (watches_.find(watch_id) == watches_.end())
-      return;
-  }
-
-  // Dispatch writable signal.
-  if (observed & ZX_SOCKET_WRITABLE)
-    info.socket_watcher->OnSocketWritable(info.socket_handle);
-}
-
-}  // namespace debug_ipc
diff --git a/src/developer/debug/shared/message_loop_async.h b/src/developer/debug/shared/message_loop_async.h
deleted file mode 100644
index f25d4909d2c..00000000000
--- a/src/developer/debug/shared/message_loop_async.h
+++ /dev/null
@@ -1,183 +0,0 @@
-// Copyright 2018 The Fuchsia Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#pragma once
-
-#include <lib/async-loop/cpp/loop.h>
-#include <lib/fdio/unsafe.h>
-#include <lib/zx/event.h>
-#include <lib/zx/port.h>
-#include <lib/zx/thread.h>
-
-#include <vector>
-
-#include "src/developer/debug/shared/event_handlers.h"
-#include "src/developer/debug/shared/message_loop_target.h"
-
-namespace debug_ipc {
-
-class ExceptionHandler;
-class SignalHandler;
-class SocketWatcher;
-class ZirconExceptionWatcher;
-
-class MessageLoopAsync : public MessageLoopTarget {
- public:
-  // Associated struct to track information about what type of resource a watch
-  // handle is following.
-  // EventHandlers need access to the WatchInfo implementation, hence the reason
-  // for it to be public.
-  // Definition at the end of the header.
-  struct WatchInfo;
-
-  using SignalHandlerMap = std::map<const async_wait_t*, SignalHandler>;
-  using ExceptionHandlerMap =
-      std::map<const async_exception_t*, ExceptionHandler>;
-
-  MessageLoopAsync();
-  ~MessageLoopAsync();
-
-  void Init() override;
-  zx_status_t InitTarget() override;
-
-  Type GetType() const override { return Type::kAsync; }
-  bool SupportsFidl() const override { return true; }
-
-  void Cleanup() override;
-
-  // Returns the current message loop or null if there isn't one.
-  static MessageLoopAsync* Current();
-
-  // MessageLoop implementation.
-  WatchHandle WatchFD(WatchMode mode, int fd, FDWatcher* watcher) override;
-
-  zx_status_t WatchSocket(WatchMode mode, zx_handle_t socket_handle,
-                          SocketWatcher* watcher, WatchHandle* out) override;
-
-  // Attaches to the exception port of the given process and issues callbacks
-  // on the given watcher. The watcher must outlive the returned WatchHandle.
-  // Must only be called on the message loop thread.
-  zx_status_t WatchProcessExceptions(WatchProcessConfig config,
-                                     WatchHandle* out) override;
-
-  // Attaches to the exception port of the given job and issues callbacks
-  // on the given watcher. The watcher must outlive the returned WatchHandle.
-  // Must only be called on the message loop thread.
-  zx_status_t WatchJobExceptions(WatchJobConfig config,
-                                 WatchHandle* out) override;
-
-  // When this class issues an exception notification, the code should call
-  // this function to resume the thread from the exception. This is a wrapper
-  // for zx_task_resume_from_exception.
-  /* zx_status_t ResumeFromException(zx::thread& thread, uint32_t options); */
-  zx_status_t ResumeFromException(zx_koid_t thread_koid, zx::thread&,
-                                  uint32_t options) override;
-
-  void QuitNow() override;
-
-  const SignalHandlerMap& signal_handlers() const { return signal_handlers_; }
-
-  const ExceptionHandlerMap& exception_handlers() const {
-    return exception_handlers_;
-  }
-
- private:
-  const WatchInfo* FindWatchInfo(int id) const;
-
-  // MessageLoop protected implementation.
-  uint64_t GetMonotonicNowNS() const override;
-  void RunImpl() override;
-  void StopWatching(int id) override;
-  // Triggers an event signaling that there is a pending event.
-  void SetHasTasks() override;
-
-  // Check for any pending C++ tasks and process them.
-  // Returns true if there was an event pending to be processed.
-  bool CheckAndProcessPendingTasks();
-
-  // Handles WatchHandles event. These are all the events that are not C++ tasks
-  // posted to the message loop.
-  void HandleException(const ExceptionHandler&, zx_port_packet_t packet);
-
-  // Handle an event of the given type.
-  void OnFdioSignal(int watch_id, const WatchInfo& info, zx_signals_t observed);
-  void OnProcessException(const ExceptionHandler&, const WatchInfo& info,
-                          const zx_port_packet_t& packet);
-  void OnProcessTerminated(const WatchInfo&, zx_signals_t observed);
-
-  void OnJobException(const ExceptionHandler&, const WatchInfo& info,
-                      const zx_port_packet_t& packet);
-  void OnSocketSignal(int watch_id, const WatchInfo& info,
-                      zx_signals_t observed);
-
-  using WatchMap = std::map<int, WatchInfo>;
-  WatchMap watches_;
-
-  // ID used as an index into watches_.
-  int next_watch_id_ = 1;
-
-  async::Loop loop_;
-  zx::event task_event_;
-
-  SignalHandlerMap signal_handlers_;
-  // See SignalHandler constructor.
-  // |associated_info| needs to be updated with the fact that it has an
-  // associated SignalHandler.
-  zx_status_t AddSignalHandler(int, zx_handle_t, zx_signals_t, WatchInfo* info);
-  void RemoveSignalHandler(const async_wait_t* id);
-
-  ExceptionHandlerMap exception_handlers_;
-  // See ExceptionHandler constructor.
-  // |associated_info| needs to be updated with the fact that it has an
-  // associated ExceptionHandler.
-  zx_status_t AddExceptionHandler(int, zx_handle_t, uint32_t, WatchInfo* info);
-  void RemoveExceptionHandler(const async_exception_t*);
-
-  // Every exception source (ExceptionHandler) will get an async_exception_t*
-  // that works as a "key" for the async_loop. This async_exception_t* is what
-  // you give back to the loop to return from an exception.
-  //
-  // So, everytime there is an exception, there needs to be a tracking from
-  // thread_koid to this async_exception_t*, so that when a thread is resumed,
-  // we can pass to the loop the correct key by just using the thread koid.
-  struct Exception;
-  std::map<zx_koid_t, Exception> thread_exception_map_;
-  void AddException(const ExceptionHandler&, zx_koid_t thread_koid);
-
-  FXL_DISALLOW_COPY_AND_ASSIGN(MessageLoopAsync);
-
-  friend class SignalHandler;
-  friend class ExceptionHandler;
-};
-
-// EventHandlers need access to the WatchInfo implementation.
-struct MessageLoopAsync::WatchInfo {
-  // Name of the resource being watched.
-  // Mostly tracked for debugging purposes.
-  std::string resource_name;
-
-  WatchType type = WatchType::kFdio;
-
-  // FDIO-specific watcher parameters.
-  int fd = -1;
-  fdio_t* fdio = nullptr;
-  FDWatcher* fd_watcher = nullptr;
-  zx_handle_t fd_handle = ZX_HANDLE_INVALID;
-
-  // Socket-specific parameters.
-  SocketWatcher* socket_watcher = nullptr;
-  zx_handle_t socket_handle = ZX_HANDLE_INVALID;
-
-  // Task-exception-specific parameters, can be of job or process type.
-  ZirconExceptionWatcher* exception_watcher = nullptr;
-  zx_koid_t task_koid = 0;
-  zx_handle_t task_handle = ZX_HANDLE_INVALID;
-
-  // This makes easier the lookup of the associated ExceptionHandler with this
-  // watch id.
-  const async_wait_t* signal_handler_key = nullptr;
-  const async_exception_t* exception_handler_key = nullptr;
-};
-
-}  // namespace debug_ipc
diff --git a/src/developer/debug/shared/message_loop_target.cc b/src/developer/debug/shared/message_loop_target.cc
index 7295eb06d27..6dafa464a8c 100644
--- a/src/developer/debug/shared/message_loop_target.cc
+++ b/src/developer/debug/shared/message_loop_target.cc
@@ -1,48 +1,577 @@
-// Copyright 2019 The Fuchsia Authors. All rights reserved.
+// Copyright 2018 The Fuchsia Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "src/developer/debug/shared/message_loop_target.h"
 
-#include "src/developer/debug/shared/message_loop_async.h"
-#include "src/developer/debug/shared/message_loop_zircon.h"
+#include <lib/fdio/io.h>
+#include <lib/zx/handle.h>
+#include <lib/zx/job.h>
+#include <lib/zx/process.h>
+#include <stdio.h>
+#include <zircon/syscalls/exception.h>
 
+#include "src/developer/debug/shared/event_handlers.h"
+#include "src/developer/debug/shared/fd_watcher.h"
+#include "src/developer/debug/shared/logging/logging.h"
+#include "src/developer/debug/shared/socket_watcher.h"
+#include "src/developer/debug/shared/zircon_exception_watcher.h"
+#include "src/developer/debug/shared/zx_status.h"
+#include "src/lib/fxl/compiler_specific.h"
 #include "src/lib/fxl/logging.h"
 
 namespace debug_ipc {
 
-MessageLoopTarget::~MessageLoopTarget() = default;
+namespace {
 
-MessageLoopTarget::Type MessageLoopTarget::current_message_loop_type =
-    MessageLoopTarget::Type::kLast;
+thread_local MessageLoopTarget* current_message_loop = nullptr;
 
+}  // namespace
+
+// Exception ------------------------------------------------------------
+
+struct MessageLoopTarget::Exception {
+  zx_koid_t thread_koid = 0;
+  // Not-owning. Must outlive.
+  async_exception_t* exception_token = nullptr;
+};
+
+// MessageLoopTarget -----------------------------------------------------------
+
+MessageLoopTarget::MessageLoopTarget() : loop_(&kAsyncLoopConfigAttachToThread) {}
+
+MessageLoopTarget::~MessageLoopTarget() {
+  FXL_DCHECK(Current() != this);  // Cleanup should have been called.
+}
+
+void MessageLoopTarget::Init() { InitTarget(); }
+
+zx_status_t MessageLoopTarget::InitTarget() {
+  MessageLoop::Init();
+
+  FXL_DCHECK(!current_message_loop);
+  current_message_loop = this;
+
+  zx::event::create(0, &task_event_);
+
+  WatchInfo info;
+  info.type = WatchType::kTask;
+  zx_status_t status =
+      AddSignalHandler(kTaskSignalKey, task_event_.get(), kTaskSignal, &info);
+
+  if (status != ZX_OK)
+    return status;
+
+  watches_[kTaskSignalKey] = std::move(info);
+  return ZX_OK;
+}
+
+void MessageLoopTarget::Cleanup() {
+  // We need to remove the signal/exception handlers before the message loop
+  // goes away.
+  signal_handlers_.clear();
+  exception_handlers_.clear();
+
+  FXL_DCHECK(current_message_loop == this);
+  current_message_loop = nullptr;
+
+  MessageLoop::Cleanup();
+}
+
+// static
 MessageLoopTarget* MessageLoopTarget::Current() {
-  FXL_DCHECK(current_message_loop_type != MessageLoopTarget::Type::kLast);
-  switch (current_message_loop_type) {
-    case MessageLoopTarget::Type::kAsync:
-      return MessageLoopAsync::Current();
-    case MessageLoopTarget::Type::kZircon:
-      return MessageLoopZircon::Current();
-    case MessageLoopTarget::Type::kLast:
+  return current_message_loop;
+}
+
+const MessageLoopTarget::WatchInfo* MessageLoopTarget::FindWatchInfo(
+    int id) const {
+  auto it = watches_.find(id);
+  if (it == watches_.end())
+    return nullptr;
+  return &it->second;
+}
+
+zx_status_t MessageLoopTarget::AddSignalHandler(int id, zx_handle_t object,
+                                               zx_signals_t signals,
+                                               WatchInfo* associated_info) {
+  SignalHandler handler;
+  zx_status_t status = handler.Init(id, object, signals);
+  if (status != ZX_OK)
+    return status;
+
+  // The handler should not be there already.
+  FXL_DCHECK(signal_handlers_.find(handler.handle()) == signal_handlers_.end());
+
+  associated_info->signal_handler_key = handler.handle();
+  signal_handlers_[handler.handle()] = std::move(handler);
+
+  return ZX_OK;
+}
+
+zx_status_t MessageLoopTarget::AddExceptionHandler(int id, zx_handle_t object,
+                                                  uint32_t options,
+                                                  WatchInfo* associated_info) {
+  ExceptionHandler handler;
+  zx_status_t status = handler.Init(id, object, options);
+  if (status != ZX_OK)
+    return status;
+
+  // The handler should not be there already.
+  FXL_DCHECK(exception_handlers_.find(handler.handle()) ==
+             exception_handlers_.end());
+
+  associated_info->exception_handler_key = handler.handle();
+  exception_handlers_[handler.handle()] = std::move(handler);
+
+  return ZX_OK;
+}
+
+MessageLoop::WatchHandle MessageLoopTarget::WatchFD(WatchMode mode, int fd,
+                                                   FDWatcher* watcher) {
+  WatchInfo info;
+  info.type = WatchType::kFdio;
+  info.fd_watcher = watcher;
+  info.fd = fd;
+  info.fdio = fdio_unsafe_fd_to_io(fd);
+  if (!info.fdio)
+    return WatchHandle();
+
+  uint32_t events = 0;
+  switch (mode) {
+    case WatchMode::kRead:
+      events = POLLIN;
+      break;
+    case WatchMode::kWrite:
+      events = POLLOUT;
+      break;
+    case WatchMode::kReadWrite:
+      events = POLLIN | POLLOUT;
       break;
   }
 
-  FXL_NOTREACHED();
-  return nullptr;
+  zx_signals_t signals = ZX_SIGNAL_NONE;
+  fdio_unsafe_wait_begin(info.fdio, events, &info.fd_handle, &signals);
+  if (info.fd_handle == ZX_HANDLE_INVALID)
+    return WatchHandle();
+
+  int watch_id;
+  {
+    std::lock_guard<std::mutex> guard(mutex_);
+
+    watch_id = next_watch_id_;
+    next_watch_id_++;
+  }
+
+  zx_status_t status =
+      AddSignalHandler(watch_id, info.fd_handle, signals, &info);
+  if (status != ZX_OK)
+    return WatchHandle();
+
+  watches_[watch_id] = info;
+  return WatchHandle(this, watch_id);
 }
 
-const char* MessageLoopTarget::TypeToString(Type type) {
-  switch (type) {
-    case MessageLoopTarget::Type::kAsync:
-      return "Async";
-    case MessageLoopTarget::Type::kZircon:
-      return "Zircon";
-    case MessageLoopTarget::Type::kLast:
-      return "Last";
+zx_status_t MessageLoopTarget::WatchSocket(WatchMode mode,
+                                          zx_handle_t socket_handle,
+                                          SocketWatcher* watcher,
+                                          MessageLoop::WatchHandle* out) {
+  WatchInfo info;
+  info.type = WatchType::kSocket;
+  info.socket_watcher = watcher;
+  info.socket_handle = socket_handle;
+
+  int watch_id;
+  {
+    std::lock_guard<std::mutex> guard(mutex_);
+
+    watch_id = next_watch_id_;
+    next_watch_id_++;
+  }
+
+  zx_signals_t signals = 0;
+  if (mode == WatchMode::kRead || mode == WatchMode::kReadWrite)
+    signals |= ZX_SOCKET_READABLE;
+
+  if (mode == WatchMode::kWrite || mode == WatchMode::kReadWrite)
+    signals |= ZX_SOCKET_WRITABLE;
+
+  zx_status_t status =
+      AddSignalHandler(watch_id, socket_handle, ZX_SOCKET_WRITABLE, &info);
+  if (status != ZX_OK)
+    return status;
+
+  watches_[watch_id] = info;
+  *out = WatchHandle(this, watch_id);
+  return ZX_OK;
+}
+
+zx_status_t MessageLoopTarget::WatchProcessExceptions(
+    WatchProcessConfig config, MessageLoop::WatchHandle* out) {
+  WatchInfo info;
+  info.resource_name = config.process_name;
+  info.type = WatchType::kProcessExceptions;
+  info.exception_watcher = config.watcher;
+  info.task_koid = config.process_koid;
+  info.task_handle = config.process_handle;
+
+  int watch_id;
+  {
+    std::lock_guard<std::mutex> guard(mutex_);
+
+    watch_id = next_watch_id_;
+    next_watch_id_++;
+  }
+
+  // Watch all exceptions for the process.
+  zx_status_t status;
+  status = AddExceptionHandler(watch_id, config.process_handle,
+                               ZX_EXCEPTION_PORT_DEBUGGER, &info);
+  if (status != ZX_OK)
+    return status;
+
+  // Watch for the process terminated signal.
+  status = AddSignalHandler(watch_id, config.process_handle,
+                            ZX_PROCESS_TERMINATED, &info);
+  if (status != ZX_OK)
+    return status;
+
+  DEBUG_LOG(MessageLoop) << "Watching process " << info.resource_name;
+
+  watches_[watch_id] = info;
+  *out = WatchHandle(this, watch_id);
+  return ZX_OK;
+}
+
+zx_status_t MessageLoopTarget::WatchJobExceptions(
+    WatchJobConfig config, MessageLoop::WatchHandle* out) {
+  WatchInfo info;
+  info.resource_name = config.job_name;
+  info.type = WatchType::kJobExceptions;
+  info.exception_watcher = config.watcher;
+  info.task_koid = config.job_koid;
+  info.task_handle = config.job_handle;
+
+  int watch_id;
+  {
+    std::lock_guard<std::mutex> guard(mutex_);
+
+    watch_id = next_watch_id_;
+    next_watch_id_++;
+  }
+
+  // Create and track the exception handle.
+  zx_status_t status = AddExceptionHandler(watch_id, config.job_handle,
+                                           ZX_EXCEPTION_PORT_DEBUGGER, &info);
+  if (status != ZX_OK)
+    return status;
+
+  DEBUG_LOG(MessageLoop) << "Watching job " << info.resource_name;
+
+  watches_[watch_id] = info;
+  *out = WatchHandle(this, watch_id);
+  return ZX_OK;
+}
+
+zx_status_t MessageLoopTarget::ResumeFromException(zx_koid_t thread_koid,
+                                                  zx::thread& thread,
+                                                  uint32_t options) {
+  auto it = thread_exception_map_.find(thread_koid);
+  FXL_DCHECK(it != thread_exception_map_.end());
+  zx_status_t res = async_resume_from_exception(async_get_default_dispatcher(),
+                                                it->second.exception_token,
+                                                thread.get(), options);
+  thread_exception_map_.erase(thread_koid);
+  return res;
+}
+
+bool MessageLoopTarget::CheckAndProcessPendingTasks() {
+  std::lock_guard<std::mutex> guard(mutex_);
+  // Do a C++ task.
+  if (ProcessPendingTask()) {
+    SetHasTasks();  // Enqueue another task signal.
+    return true;
+  }
+  return false;
+}
+
+void MessageLoopTarget::HandleException(const ExceptionHandler& handler,
+                                       zx_port_packet_t packet) {
+  WatchInfo* watch_info = nullptr;
+  {
+    // Some event being watched.
+    std::lock_guard<std::mutex> guard(mutex_);
+    auto found = watches_.find(packet.key);
+    if (found == watches_.end()) {
+      // It is possible to get an exception that doesn't have a watch handle.
+      // A case is a race between detaching from a process and getting an
+      // exception on that process.
+      //
+      // The normal process looks like this:
+      //
+      // 1. In order to correctly detach, the debug agent has to resume threads
+      //    from their exceptions. Otherwise that exception will be treated as
+      //    unhandled when the agent detaches and will bubble up.
+      // 2. The agent detaches from the exception port. This means that the
+      //    watch handle is no longer listening.
+      //
+      // It is possible between (1) and (2) to get an exception, which will be
+      // queued in the exception port of the thread. Now, the agent won't read
+      // from the port until *after* it has detached from the exception port.
+      // This means that this new exception is not handled and will be bubbled
+      // up, which is correct as the debug agent stated that it has nothing more
+      // to do with the process.
+      //
+      // Now the problem is that zircon does not clean stale packets from a
+      // queue, meaning that the next time the message loop waits on the port,
+      // it will find a stale packet. In this context a stale packet means one
+      // that does not have a watch handle, as it was deleted in (1). Hence we
+      // get into this case and we simply log it for posperity.
+      //
+      // TODO(ZX-2623): zircon is going to clean up stale packets from ports
+      //                in the future. When that is done, this case should not
+      //                happen and we should go back into asserting it.
+      FXL_LOG(WARNING) << "Got stale port packet. This is most probably due to "
+                          "a race between detaching from a process and an "
+                          "exception ocurring.";
+      return;
+    }
+    watch_info = &found->second;
+  }
+
+  // Dispatch the watch callback outside of the lock. This depends on the
+  // stability of the WatchInfo pointer in the map (std::map is stable across
+  // updates) and the watch not getting unregistered from another thread
+  // asynchronously (which the API requires and is enforced by a DCHECK in
+  // the StopWatching impl).
+  switch (watch_info->type) {
+    case WatchType::kProcessExceptions:
+      OnProcessException(handler, *watch_info, packet);
+      break;
+    case WatchType::kJobExceptions:
+      OnJobException(handler, *watch_info, packet);
+      break;
+    case WatchType::kTask:
+    case WatchType::kFdio:
+    case WatchType::kSocket:
+      FXL_NOTREACHED();
+  }
+}
+
+uint64_t MessageLoopTarget::GetMonotonicNowNS() const {
+  zx::time ret;
+  zx::clock::get(&ret);
+
+  return ret.get();
+}
+
+// Previously, the approach was to first look for C++ tasks and when handled
+// look for WatchHandle work and finally wait for an event. This worked because
+// handle events didn't post C++ tasks.
+//
+// But some tests do post tasks on handle events. Because C++ tasks are signaled
+// by explicitly signaling an zx::event, without manually checking, the C++
+// tasks will never be checked and we would get blocked until a watch handled
+// is triggered.
+//
+// In order to handle the events properly, we need to check for C++ tasks before
+// and *after* handling watch handle events. This way we always process C++
+// tasks before handle events and will get signaled if one of them posted a new
+// task.
+void MessageLoopTarget::RunImpl() {
+  // Init should have been called.
+  FXL_DCHECK(Current() == this);
+  zx_status_t status;
+
+  zx::time time;
+  uint64_t delay = DelayNS();
+  if (delay == MessageLoop::kMaxDelay) {
+    time = zx::time::infinite();
+  } else {
+    time = zx::deadline_after(zx::nsec(delay));
+  }
+
+  for (;;) {
+    status = loop_.ResetQuit();
+    FXL_DCHECK(status != ZX_ERR_BAD_STATE);
+    status = loop_.Run(time);
+    FXL_DCHECK(status == ZX_OK || status == ZX_ERR_CANCELED ||
+               status == ZX_ERR_TIMED_OUT)
+        << "Expected ZX_OK || ZX_ERR_CANCELED || ZX_ERR_TIMED_OUT, got "
+        << ZxStatusToString(status);
+
+    if (status != ZX_ERR_TIMED_OUT) {
+      return;
+    }
+
+    std::lock_guard<std::mutex> guard(mutex_);
+    if (ProcessPendingTask())
+      SetHasTasks();
+  }
+}
+
+void MessageLoopTarget::QuitNow() {
+  MessageLoop::QuitNow();
+  loop_.Quit();
+}
+
+void MessageLoopTarget::StopWatching(int id) {
+  // The dispatch code for watch callbacks requires this be called on the
+  // same thread as the message loop is.
+  FXL_DCHECK(Current() == this);
+
+  std::lock_guard<std::mutex> guard(mutex_);
+
+  auto found = watches_.find(id);
+  FXL_DCHECK(found != watches_.end());
+
+  WatchInfo& info = found->second;
+  // BufferedFD constantly creates and destroys FD handles, flooding the log
+  // with non-helpful logging statements.
+  if (info.type != WatchType::kFdio) {
+    DEBUG_LOG(MessageLoop) << "Stop watching " << WatchTypeToString(info.type)
+                           << " " << info.resource_name;
   }
 
+  switch (info.type) {
+    case WatchType::kProcessExceptions: {
+      RemoveExceptionHandler(info.exception_handler_key);
+      RemoveSignalHandler(info.signal_handler_key);
+      break;
+    }
+    case WatchType::kJobExceptions: {
+      RemoveExceptionHandler(info.exception_handler_key);
+      break;
+    }
+    case WatchType::kTask:
+    case WatchType::kFdio:
+    case WatchType::kSocket:
+      RemoveSignalHandler(info.signal_handler_key);
+      break;
+  }
+  watches_.erase(found);
+}
+
+void MessageLoopTarget::SetHasTasks() { task_event_.signal(0, kTaskSignal); }
+
+void MessageLoopTarget::OnFdioSignal(int watch_id, const WatchInfo& info,
+                                    zx_signals_t observed) {
+  uint32_t events = 0;
+  fdio_unsafe_wait_end(info.fdio, observed, &events);
+
+  if ((events & POLLERR) || (events & POLLHUP) || (events & POLLNVAL) ||
+      (events & POLLRDHUP)) {
+    info.fd_watcher->OnFDReady(info.fd, false, false, true);
+
+    // Don't dispatch any other notifications when there's an error. Zircon
+    // seems to set readable and writable on error even if there's nothing
+    // there.
+    return;
+  }
+
+  bool readable = !!(events & POLLIN);
+  bool writable = !!(events & POLLOUT);
+  info.fd_watcher->OnFDReady(info.fd, readable, writable, false);
+}
+
+void MessageLoopTarget::RemoveSignalHandler(const async_wait_t* key) {
+  FXL_DCHECK(key);
+  size_t erase_count = signal_handlers_.erase(key);
+  FXL_DCHECK(erase_count == 1u);
+}
+
+void MessageLoopTarget::RemoveExceptionHandler(const async_exception_t* key) {
+  FXL_DCHECK(key);
+  size_t erase_count = exception_handlers_.erase(key);
+  FXL_DCHECK(erase_count == 1u);
+}
+
+void MessageLoopTarget::AddException(const ExceptionHandler& handler,
+                                    zx_koid_t thread_koid) {
+  FXL_DCHECK(thread_exception_map_.find(thread_koid) ==
+             thread_exception_map_.end());
+
+  Exception exception;
+  exception.thread_koid = thread_koid;
+  exception.exception_token = const_cast<async_exception_t*>(handler.handle());
+  thread_exception_map_[thread_koid] = std::move(exception);
+}
+
+void MessageLoopTarget::OnProcessException(const ExceptionHandler& handler,
+                                          const WatchInfo& info,
+                                          const zx_port_packet_t& packet) {
+  if (ZX_PKT_IS_EXCEPTION(packet.type)) {
+    // All debug exceptions.
+    switch (packet.type) {
+      case ZX_EXCP_THREAD_STARTING:
+        AddException(handler, packet.exception.tid);
+        info.exception_watcher->OnThreadStarting(info.task_koid,
+                                                 packet.exception.tid);
+        break;
+      case ZX_EXCP_THREAD_EXITING:
+        AddException(handler, packet.exception.tid);
+        info.exception_watcher->OnThreadExiting(info.task_koid,
+                                                packet.exception.tid);
+        break;
+      case ZX_EXCP_GENERAL:
+      case ZX_EXCP_FATAL_PAGE_FAULT:
+      case ZX_EXCP_UNDEFINED_INSTRUCTION:
+      case ZX_EXCP_SW_BREAKPOINT:
+      case ZX_EXCP_HW_BREAKPOINT:
+      case ZX_EXCP_UNALIGNED_ACCESS:
+      case ZX_EXCP_POLICY_ERROR:
+        AddException(handler, packet.exception.tid);
+        info.exception_watcher->OnException(info.task_koid,
+                                            packet.exception.tid, packet.type);
+        break;
+      default:
+        FXL_NOTREACHED();
+    }
+  } else {
+    FXL_NOTREACHED();
+  }
+}
+
+void MessageLoopTarget::OnProcessTerminated(const WatchInfo& info,
+                                           zx_signals_t observed) {
+  FXL_DCHECK(observed & ZX_PROCESS_TERMINATED);
+  info.exception_watcher->OnProcessTerminated(info.task_koid);
+}
+
+void MessageLoopTarget::OnJobException(const ExceptionHandler& handler,
+                                      const WatchInfo& info,
+                                      const zx_port_packet_t& packet) {
+  if (ZX_PKT_IS_EXCEPTION(packet.type)) {
+    // All debug exceptions.
+    switch (packet.type) {
+      case ZX_EXCP_PROCESS_STARTING:
+        AddException(handler, packet.exception.tid);
+        info.exception_watcher->OnProcessStarting(
+            info.task_koid, packet.exception.pid, packet.exception.tid);
+        return;
+      default:
+        break;
+    }
+  }
   FXL_NOTREACHED();
-  return nullptr;
+}
+
+void MessageLoopTarget::OnSocketSignal(int watch_id, const WatchInfo& info,
+                                      zx_signals_t observed) {
+  // Dispatch readable signal.
+  if (observed & ZX_SOCKET_READABLE)
+    info.socket_watcher->OnSocketReadable(info.socket_handle);
+
+  // When signaling both readable and writable, make sure the readable handler
+  // didn't remove the watch.
+  if ((observed & ZX_SOCKET_READABLE) && (observed & ZX_SOCKET_WRITABLE)) {
+    std::lock_guard<std::mutex> guard(mutex_);
+    if (watches_.find(watch_id) == watches_.end())
+      return;
+  }
+
+  // Dispatch writable signal.
+  if (observed & ZX_SOCKET_WRITABLE)
+    info.socket_watcher->OnSocketWritable(info.socket_handle);
 }
 
 const char* WatchTypeToString(WatchType type) {
diff --git a/src/developer/debug/shared/message_loop_target.h b/src/developer/debug/shared/message_loop_target.h
index 2ca33804bc6..0d738971d43 100644
--- a/src/developer/debug/shared/message_loop_target.h
+++ b/src/developer/debug/shared/message_loop_target.h
@@ -1,17 +1,26 @@
-// Copyright 2019 The Fuchsia Authors. All rights reserved.
+// Copyright 2018 The Fuchsia Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #pragma once
 
+#include <lib/async-loop/cpp/loop.h>
+#include <lib/fdio/unsafe.h>
+#include <lib/zx/event.h>
+#include <lib/zx/port.h>
 #include <lib/zx/thread.h>
 
+#include <vector>
+
+#include "src/developer/debug/shared/event_handlers.h"
 #include "src/developer/debug/shared/message_loop.h"
 
 namespace debug_ipc {
 
-class ZirconExceptionWatcher;
+class ExceptionHandler;
+class SignalHandler;
 class SocketWatcher;
+class ZirconExceptionWatcher;
 
 enum class WatchType : uint32_t {
   kTask,
@@ -22,39 +31,32 @@ enum class WatchType : uint32_t {
 };
 const char* WatchTypeToString(WatchType);
 
-// MessageLoopTarget is an abstract interface for all message loops that can
-// be used in the debug agent.
-class MessageLoopTarget : public MessageLoop {
+class MessageLoopTarget final : public MessageLoop {
  public:
-  // New message loops must be suscribed here.
-  enum class Type {
-    kAsync,
-    kZircon,
-    kLast,
-  };
-  static const char* TypeToString(Type);
-
-  // Set by a message loop at InitTarget();
-  static Type current_message_loop_type;
+  // Associated struct to track information about what type of resource a watch
+  // handle is following.
+  // EventHandlers need access to the WatchInfo implementation, hence the reason
+  // for it to be public.
+  // Definition at the end of the header.
+  struct WatchInfo;
 
-  // Target message loops can call wither Init or InitTarget. The difference is
-  // that InitTarget will return a status about what happened, whether Init
-  // will silently ignore it.
-  virtual zx_status_t InitTarget() = 0;
+  using SignalHandlerMap = std::map<const async_wait_t*, SignalHandler>;
+  using ExceptionHandlerMap =
+      std::map<const async_exception_t*, ExceptionHandler>;
 
-  virtual ~MessageLoopTarget();
+  MessageLoopTarget();
+  ~MessageLoopTarget();
 
-  virtual Type GetType() const = 0;
+  void Init() override;
+  zx_status_t InitTarget();
 
-  // Fidl requires a special dispatcher to be setup. Not all message loops
-  // support it.
-  virtual bool SupportsFidl() const = 0;
+  void Cleanup() override;
 
   // Returns the current message loop or null if there isn't one.
   static MessageLoopTarget* Current();
 
   // MessageLoop implementation.
-  WatchHandle WatchFD(WatchMode mode, int fd, FDWatcher* watcher) override = 0;
+  WatchHandle WatchFD(WatchMode mode, int fd, FDWatcher* watcher) override;
 
   // Watches the given socket for read/write status. The watcher must outlive
   // the returned WatchHandle. Must only be called on the message loop thread.
@@ -63,8 +65,8 @@ class MessageLoopTarget : public MessageLoop {
   // become both readable and writable at the same time which will necessitate
   // calling both callbacks. The code does not expect the FDWatcher to
   // disappear in between these callbacks.
-  virtual zx_status_t WatchSocket(WatchMode mode, zx_handle_t socket_handle,
-                                  SocketWatcher* watcher, WatchHandle* out) = 0;
+  zx_status_t WatchSocket(WatchMode mode, zx_handle_t socket_handle,
+                          SocketWatcher* watcher, WatchHandle* out);
 
   // Attaches to the exception port of the given process and issues callbacks
   // on the given watcher. The watcher must outlive the returned WatchHandle.
@@ -75,8 +77,8 @@ class MessageLoopTarget : public MessageLoop {
     zx_koid_t process_koid;
     ZirconExceptionWatcher* watcher = nullptr;
   };
-  virtual zx_status_t WatchProcessExceptions(WatchProcessConfig config,
-                                             WatchHandle* out) = 0;
+  zx_status_t WatchProcessExceptions(WatchProcessConfig config,
+                                     WatchHandle* out);
 
   // Attaches to the exception port of the given job and issues callbacks
   // on the given watcher. The watcher must outlive the returned WatchHandle.
@@ -87,17 +89,120 @@ class MessageLoopTarget : public MessageLoop {
     zx_koid_t job_koid;
     ZirconExceptionWatcher* watcher;
   };
-  virtual zx_status_t WatchJobExceptions(WatchJobConfig config,
-                                         WatchHandle* out) = 0;
+  zx_status_t WatchJobExceptions(WatchJobConfig config, WatchHandle* out);
 
   // When this class issues an exception notification, the code should call
   // this function to resume the thread from the exception. This is a wrapper
   // for zx_task_resume_from_exception or it's async-loop equivalent.
   // |thread_koid| is needed to identify the exception in some message loop
   // implementations.
-  virtual zx_status_t ResumeFromException(zx_koid_t thread_koid,
-                                          zx::thread& thread,
-                                          uint32_t options) = 0;
+  zx_status_t ResumeFromException(zx_koid_t thread_koid, zx::thread& thread,
+                                  uint32_t options);
+
+  void QuitNow() override;
+
+  const SignalHandlerMap& signal_handlers() const { return signal_handlers_; }
+
+  const ExceptionHandlerMap& exception_handlers() const {
+    return exception_handlers_;
+  }
+
+ private:
+  const WatchInfo* FindWatchInfo(int id) const;
+
+  // MessageLoop protected implementation.
+  uint64_t GetMonotonicNowNS() const override;
+  void RunImpl() override;
+  void StopWatching(int id) override;
+  // Triggers an event signaling that there is a pending event.
+  void SetHasTasks() override;
+
+  // Check for any pending C++ tasks and process them.
+  // Returns true if there was an event pending to be processed.
+  bool CheckAndProcessPendingTasks();
+
+  // Handles WatchHandles event. These are all the events that are not C++ tasks
+  // posted to the message loop.
+  void HandleException(const ExceptionHandler&, zx_port_packet_t packet);
+
+  // Handle an event of the given type.
+  void OnFdioSignal(int watch_id, const WatchInfo& info, zx_signals_t observed);
+  void OnProcessException(const ExceptionHandler&, const WatchInfo& info,
+                          const zx_port_packet_t& packet);
+  void OnProcessTerminated(const WatchInfo&, zx_signals_t observed);
+
+  void OnJobException(const ExceptionHandler&, const WatchInfo& info,
+                      const zx_port_packet_t& packet);
+  void OnSocketSignal(int watch_id, const WatchInfo& info,
+                      zx_signals_t observed);
+
+  using WatchMap = std::map<int, WatchInfo>;
+  WatchMap watches_;
+
+  // ID used as an index into watches_.
+  int next_watch_id_ = 1;
+
+  async::Loop loop_;
+  zx::event task_event_;
+
+  SignalHandlerMap signal_handlers_;
+  // See SignalHandler constructor.
+  // |associated_info| needs to be updated with the fact that it has an
+  // associated SignalHandler.
+  zx_status_t AddSignalHandler(int, zx_handle_t, zx_signals_t, WatchInfo* info);
+  void RemoveSignalHandler(const async_wait_t* id);
+
+  ExceptionHandlerMap exception_handlers_;
+  // See ExceptionHandler constructor.
+  // |associated_info| needs to be updated with the fact that it has an
+  // associated ExceptionHandler.
+  zx_status_t AddExceptionHandler(int, zx_handle_t, uint32_t, WatchInfo* info);
+  void RemoveExceptionHandler(const async_exception_t*);
+
+  // Every exception source (ExceptionHandler) will get an async_exception_t*
+  // that works as a "key" for the async_loop. This async_exception_t* is what
+  // you give back to the loop to return from an exception.
+  //
+  // So, everytime there is an exception, there needs to be a tracking from
+  // thread_koid to this async_exception_t*, so that when a thread is resumed,
+  // we can pass to the loop the correct key by just using the thread koid.
+  struct Exception;
+  std::map<zx_koid_t, Exception> thread_exception_map_;
+  void AddException(const ExceptionHandler&, zx_koid_t thread_koid);
+
+  FXL_DISALLOW_COPY_AND_ASSIGN(MessageLoopTarget);
+
+  friend class SignalHandler;
+  friend class ExceptionHandler;
+};
+
+// EventHandlers need access to the WatchInfo implementation.
+struct MessageLoopTarget::WatchInfo {
+  // Name of the resource being watched.
+  // Mostly tracked for debugging purposes.
+  std::string resource_name;
+
+  WatchType type = WatchType::kFdio;
+
+  // FDIO-specific watcher parameters.
+  int fd = -1;
+  fdio_t* fdio = nullptr;
+  FDWatcher* fd_watcher = nullptr;
+  zx_handle_t fd_handle = ZX_HANDLE_INVALID;
+
+  // Socket-specific parameters.
+  SocketWatcher* socket_watcher = nullptr;
+  zx_handle_t socket_handle = ZX_HANDLE_INVALID;
+
+  // Task-exception-specific parameters, can be of job or process type.
+  ZirconExceptionWatcher* exception_watcher = nullptr;
+  zx_koid_t task_koid = 0;
+  zx_handle_t task_handle = ZX_HANDLE_INVALID;
+
+  // This makes easier the lookup of the associated ExceptionHandler with this
+  // watch id.
+  const async_wait_t* signal_handler_key = nullptr;
+  const async_exception_t* exception_handler_key = nullptr;
 };
 
 }  // namespace debug_ipc
diff --git a/src/developer/debug/shared/message_loop_zircon.cc b/src/developer/debug/shared/message_loop_zircon.cc
deleted file mode 100644
index c78ecd3e412..00000000000
--- a/src/developer/debug/shared/message_loop_zircon.cc
+++ /dev/null
@@ -1,534 +0,0 @@
-// Copyright 2018 The Fuchsia Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "src/developer/debug/shared/message_loop_zircon.h"
-
-#include <lib/fdio/io.h>
-#include <lib/fdio/unsafe.h>
-#include <lib/zx/handle.h>
-#include <lib/zx/job.h>
-#include <lib/zx/process.h>
-#include <zircon/syscalls/exception.h>
-
-#include "src/developer/debug/shared/fd_watcher.h"
-#include "src/developer/debug/shared/socket_watcher.h"
-#include "src/developer/debug/shared/zircon_exception_watcher.h"
-#include "src/lib/fxl/logging.h"
-
-namespace debug_ipc {
-
-namespace {
-
-// This signal on the task_event_ indicates there is work to do.
-constexpr uint32_t kTaskSignal = ZX_USER_SIGNAL_0;
-
-// 0 is an invalid ID for watchers, so is safe to use here.
-constexpr uint64_t kTaskSignalKey = 0;
-
-thread_local MessageLoopZircon* current_message_loop_zircon = nullptr;
-
-}  // namespace
-
-// Everything in this class must be simple and copyable since we copy this
-// structure for every call (to avoid locking problems).
-struct MessageLoopZircon::WatchInfo {
-  // Mostly for debugging purposes.
-  std::string resource_name;
-
-  WatchType type = WatchType::kFdio;
-
-  // FDIO-specific watcher parameters.
-  int fd = -1;
-  fdio_t* fdio = nullptr;
-  FDWatcher* fd_watcher = nullptr;
-  zx_handle_t fd_handle = ZX_HANDLE_INVALID;
-
-  // Socket-specific parameters.
-  SocketWatcher* socket_watcher = nullptr;
-  zx_handle_t socket_handle = ZX_HANDLE_INVALID;
-
-  // Task-exception-specific parameters, can be of job or process type.
-  ZirconExceptionWatcher* exception_watcher = nullptr;
-  zx_koid_t task_koid = 0;
-  zx_handle_t task_handle = ZX_HANDLE_INVALID;
-};
-
-MessageLoopZircon::MessageLoopZircon() {
-  zx::port::create(0, &port_);
-
-  zx::event::create(0, &task_event_);
-  task_event_.wait_async(port_, kTaskSignalKey, kTaskSignal,
-                         ZX_WAIT_ASYNC_REPEATING);
-}
-
-MessageLoopZircon::~MessageLoopZircon() {
-  FXL_DCHECK(Current() != this);  // Cleanup should have been called.
-}
-
-void MessageLoopZircon::Init() { InitTarget(); }
-
-zx_status_t MessageLoopZircon::InitTarget() {
-  MessageLoop::Init();
-
-  FXL_DCHECK(!current_message_loop_zircon);
-  current_message_loop_zircon = this;
-  MessageLoopTarget::current_message_loop_type =
-      MessageLoopTarget::Type::kZircon;
-
-  return ZX_OK;
-}
-
-void MessageLoopZircon::Cleanup() {
-  FXL_DCHECK(current_message_loop_zircon == this);
-  current_message_loop_zircon = nullptr;
-  MessageLoopTarget::current_message_loop_type = MessageLoopTarget::Type::kLast;
-
-  MessageLoop::Cleanup();
-}
-
-void MessageLoopZircon::QuitNow() { MessageLoop::QuitNow(); }
-
-// static
-MessageLoopZircon* MessageLoopZircon::Current() {
-  return current_message_loop_zircon;
-}
-
-MessageLoop::WatchHandle MessageLoopZircon::WatchFD(WatchMode mode, int fd,
-                                                    FDWatcher* watcher) {
-  WatchInfo info;
-  info.type = WatchType::kFdio;
-  info.fd_watcher = watcher;
-  info.fd = fd;
-  info.fdio = fdio_unsafe_fd_to_io(fd);
-  if (!info.fdio)
-    return WatchHandle();
-
-  uint32_t events = 0;
-  switch (mode) {
-    case WatchMode::kRead:
-      events = POLLIN;
-      break;
-    case WatchMode::kWrite:
-      events = POLLOUT;
-      break;
-    case WatchMode::kReadWrite:
-      events = POLLIN | POLLOUT;
-      break;
-  }
-
-  zx_signals_t signals = ZX_SIGNAL_NONE;
-  fdio_unsafe_wait_begin(info.fdio, events, &info.fd_handle, &signals);
-  if (info.fd_handle == ZX_HANDLE_INVALID)
-    return WatchHandle();
-
-  int watch_id;
-  {
-    std::lock_guard<std::mutex> guard(mutex_);
-
-    watch_id = next_watch_id_;
-    next_watch_id_++;
-    if (zx_object_wait_async(info.fd_handle, port_.get(),
-                             static_cast<uint64_t>(watch_id), signals,
-                             ZX_WAIT_ASYNC_REPEATING) != ZX_OK)
-      return WatchHandle();
-
-    watches_[watch_id] = info;
-  }
-
-  return WatchHandle(this, watch_id);
-}
-
-zx_status_t MessageLoopZircon::WatchSocket(WatchMode mode,
-                                           zx_handle_t socket_handle,
-                                           SocketWatcher* watcher,
-                                           MessageLoop::WatchHandle* out) {
-  WatchInfo info;
-  info.type = WatchType::kSocket;
-  info.socket_watcher = watcher;
-  info.socket_handle = socket_handle;
-
-  int watch_id;
-  {
-    std::lock_guard<std::mutex> guard(mutex_);
-
-    watch_id = next_watch_id_;
-    next_watch_id_++;
-
-    if (mode == WatchMode::kRead || mode == WatchMode::kReadWrite) {
-      zx_status_t status =
-          zx_object_wait_async(socket_handle, port_.get(), watch_id,
-                               ZX_SOCKET_READABLE, ZX_WAIT_ASYNC_REPEATING);
-      if (status != ZX_OK)
-        return status;
-    }
-
-    if (mode == WatchMode::kWrite || mode == WatchMode::kReadWrite) {
-      zx_status_t status =
-          zx_object_wait_async(socket_handle, port_.get(), watch_id,
-                               ZX_SOCKET_WRITABLE, ZX_WAIT_ASYNC_REPEATING);
-      if (status != ZX_OK)
-        return status;
-    }
-
-    watches_[watch_id] = info;
-  }
-
-  *out = WatchHandle(this, watch_id);
-  return ZX_OK;
-}
-
-zx_status_t MessageLoopZircon::WatchProcessExceptions(
-    WatchProcessConfig config, MessageLoop::WatchHandle* out) {
-  WatchInfo info;
-  info.type = WatchType::kProcessExceptions;
-  info.resource_name = config.process_name;
-  info.exception_watcher = config.watcher;
-  info.task_koid = config.process_koid;
-  info.task_handle = config.process_handle;
-
-  int watch_id;
-  {
-    std::lock_guard<std::mutex> guard(mutex_);
-
-    watch_id = next_watch_id_;
-    next_watch_id_++;
-
-    // Bind to the exception port.
-    zx_status_t status =
-        zx_task_bind_exception_port(config.process_handle, port_.get(),
-                                    watch_id, ZX_EXCEPTION_PORT_DEBUGGER);
-    if (status != ZX_OK)
-      return status;
-
-    // Also watch for process termination.
-    status =
-        zx_object_wait_async(config.process_handle, port_.get(), watch_id,
-                             ZX_PROCESS_TERMINATED, ZX_WAIT_ASYNC_REPEATING);
-    if (status != ZX_OK)
-      return status;
-
-    watches_[watch_id] = info;
-  }
-
-  *out = WatchHandle(this, watch_id);
-  return ZX_OK;
-}
-
-zx_status_t MessageLoopZircon::WatchJobExceptions(
-    WatchJobConfig config, MessageLoop::WatchHandle* out) {
-  WatchInfo info;
-  info.type = WatchType::kJobExceptions;
-  info.resource_name = config.job_name;
-  info.exception_watcher = config.watcher;
-  info.task_koid = config.job_koid;
-  info.task_handle = config.job_handle;
-
-  int watch_id;
-  {
-    std::lock_guard<std::mutex> guard(mutex_);
-
-    watch_id = next_watch_id_;
-    next_watch_id_++;
-
-    // Bind to the exception port.
-    zx_status_t status = zx_task_bind_exception_port(
-        config.job_handle, port_.get(), watch_id, ZX_EXCEPTION_PORT_DEBUGGER);
-    if (status != ZX_OK)
-      return status;
-
-    watches_[watch_id] = info;
-  }
-
-  *out = WatchHandle(this, watch_id);
-  return ZX_OK;
-}
-
-// |thread_koid| is unused in this message loop.
-zx_status_t MessageLoopZircon::ResumeFromException(zx_koid_t,
-                                                   zx::thread& thread,
-                                                   uint32_t options) {
-  return thread.resume_from_exception(port_, options);
-}
-
-bool MessageLoopZircon::CheckAndProcessPendingTasks() {
-  std::lock_guard<std::mutex> guard(mutex_);
-  // Do a C++ task.
-  if (ProcessPendingTask()) {
-    SetHasTasks();  // Enqueue another task signal.
-    return true;
-  }
-  return false;
-}
-
-void MessageLoopZircon::HandleException(zx_port_packet_t packet) {
-  WatchInfo* watch_info = nullptr;
-  {
-    // Some event being watched.
-    std::lock_guard<std::mutex> guard(mutex_);
-    auto found = watches_.find(packet.key);
-    if (found == watches_.end()) {
-      // It is possible to get an exception that doesn't have a watch handle.
-      // A case is a race between detaching from a process and getting an
-      // exception on that process.
-      //
-      // The normal process looks like this:
-      //
-      // 1. In order to correctly detach, the debug agent has to resume threads
-      //    from their exceptions. Otherwise that exception will be treated as
-      //    unhandled when the agent detaches and will bubble up.
-      // 2. The agent detaches from the exception port. This means that the
-      //    watch handle is no longer listening.
-      //
-      // It is possible between (1) and (2) to get an exception, which will be
-      // queued in the exception port of the thread. Now, the agent won't read
-      // from the port until *after* it has detached from the exception port.
-      // This means that this new exception is not handled and will be bubbled
-      // up, which is correct as the debug agent stated that it has nothing more
-      // to do with the process.
-      //
-      // Now the problem is that zircon does not clean stale packets from a
-      // queue, meaning that the next time the message loop waits on the port,
-      // it will find a stale packet. In this context a stale packet means one
-      // that does not have a watch handle, as it was deleted in (1). Hence we
-      // get into this case and we simply log it for posperity.
-      //
-      // TODO(zX-2623): zircon is going to clean up stale packets from ports
-      //                in the future. When that is done, this case should not
-      //                happen and we should go back into asserting it.
-      FXL_LOG(WARNING) << "Got stale port packet. This is most probably due to "
-                          "a race between detaching from a process and an "
-                          "exception ocurring.";
-      return;
-    }
-    watch_info = &found->second;
-  }
-
-  // Dispatch the watch callback outside of the lock. This depends on the
-  // stability of the WatchInfo pointer in the map (std::map is stable across
-  // updates) and the watch not getting unregistered from another thread
-  // asynchronously (which the API requires and is enforced by a DCHECK in
-  // the StopWatching impl).
-  switch (watch_info->type) {
-    case WatchType::kFdio:
-      OnFdioSignal(packet.key, *watch_info, packet);
-      break;
-    case WatchType::kProcessExceptions:
-      OnProcessException(*watch_info, packet);
-      break;
-    case WatchType::kJobExceptions:
-      OnJobException(*watch_info, packet);
-      break;
-    case WatchType::kSocket:
-      OnSocketSignal(packet.key, *watch_info, packet);
-      break;
-    default:
-      FXL_NOTREACHED();
-  }
-}
-
-uint64_t MessageLoopZircon::GetMonotonicNowNS() const {
-  zx::time ret;
-  zx::clock::get(&ret);
-
-  return ret.get();
-}
-
-// Previously, the approach was to first look for C++ tasks and when handled
-// look for WatchHandle work and finally wait for an event. This worked because
-// handle events didn't post C++ tasks.
-//
-// But some tests do post tasks on handle events. Because C++ tasks are signaled
-// by explicitly signaling an zx::event, without manually checking, the C++
-// tasks will never be checked and we would get blocked until a watch handled
-// is triggered.
-//
-// In order to handle the events properly, we need to check for C++ tasks before
-// and *after* handling watch handle events. This way we always process C++
-// tasks before handle events and will get signaled if one of them posted a new
-// task.
-void MessageLoopZircon::RunImpl() {
-  // Init should have been called.
-  FXL_DCHECK(Current() == this);
-
-  zx::time time;
-  uint64_t delay = DelayNS();
-  if (delay == MessageLoop::kMaxDelay) {
-    time = zx::time::infinite();
-  } else {
-    time = zx::deadline_after(zx::nsec(delay));
-  }
-
-  zx_port_packet_t packet;
-  while (!should_quit() && port_.wait(time, &packet) == ZX_OK) {
-    // We check first for pending C++ tasks. If an event was handled, it will
-    // signal the associated zx::event in order to trigger the port once more
-    // (this is the way we process an enqueued event). If there is no enqueued
-    // event, we won't trigger the event and go back to wait on the port.
-    if (packet.key == kTaskSignalKey) {
-      CheckAndProcessPendingTasks();
-      continue;
-    }
-
-    // If it wasn't a task, we check for what kind of exception it was and
-    // handle it.
-    HandleException(packet);
-
-    // The exception handling could have added more pending work, so we have to
-    // re-check in order to correctly signal for new work.
-    CheckAndProcessPendingTasks();
-  }
-}
-
-void MessageLoopZircon::StopWatching(int id) {
-  // The dispatch code for watch callbacks requires this be called on the
-  // same thread as the message loop is.
-  FXL_DCHECK(Current() == this);
-
-  std::lock_guard<std::mutex> guard(mutex_);
-
-  auto found = watches_.find(id);
-  if (found == watches_.end()) {
-    FXL_NOTREACHED();
-    return;
-  }
-
-  WatchInfo& info = found->second;
-  switch (info.type) {
-    case WatchType::kFdio:
-      port_.cancel(*zx::unowned_handle(info.fd_handle),
-                   static_cast<uint64_t>(id));
-      break;
-    case WatchType::kProcessExceptions: {
-      zx::unowned_process process(info.task_handle);
-
-      // Binding an invalid port will detach from the exception port.
-      process->bind_exception_port(zx::port(), 0, ZX_EXCEPTION_PORT_DEBUGGER);
-      // Stop watching for process events.
-      port_.cancel(*process, id);
-      break;
-    }
-    case WatchType::kJobExceptions: {
-      zx::unowned_job job(info.task_handle);
-      // Binding an invalid port will detach from the exception port.
-      job->bind_exception_port(zx::port(), 0, ZX_EXCEPTION_PORT_DEBUGGER);
-      // Stop watching for job events.
-      port_.cancel(*job, id);
-      break;
-    }
-    case WatchType::kSocket:
-      port_.cancel(*zx::unowned_handle(info.socket_handle), id);
-      break;
-    default:
-      FXL_NOTREACHED();
-      break;
-  }
-  watches_.erase(found);
-}
-
-void MessageLoopZircon::SetHasTasks() { task_event_.signal(0, kTaskSignal); }
-
-void MessageLoopZircon::OnFdioSignal(int watch_id, const WatchInfo& info,
-                                     const zx_port_packet_t& packet) {
-  uint32_t events = 0;
-  fdio_unsafe_wait_end(info.fdio, packet.signal.observed, &events);
-
-  if ((events & POLLERR) || (events & POLLHUP) || (events & POLLNVAL) ||
-      (events & POLLRDHUP)) {
-    info.fd_watcher->OnFDReady(info.fd, false, false, true);
-
-    // Don't dispatch any other notifications when there's an error. Zircon
-    // seems to set readable and writable on error even if there's nothing
-    // there.
-    return;
-  }
-
-  info.fd_watcher->OnFDReady(info.fd, !!(events & POLLIN), !!(events & POLLOUT),
-                             false);
-}
-
-void MessageLoopZircon::OnProcessException(const WatchInfo& info,
-                                           const zx_port_packet_t& packet) {
-  if (ZX_PKT_IS_EXCEPTION(packet.type)) {
-    // All debug exceptions.
-    switch (packet.type) {
-      case ZX_EXCP_THREAD_STARTING:
-        info.exception_watcher->OnThreadStarting(info.task_koid,
-                                                 packet.exception.tid);
-        break;
-      case ZX_EXCP_THREAD_EXITING:
-        info.exception_watcher->OnThreadExiting(info.task_koid,
-                                                packet.exception.tid);
-        break;
-      case ZX_EXCP_GENERAL:
-      case ZX_EXCP_FATAL_PAGE_FAULT:
-      case ZX_EXCP_UNDEFINED_INSTRUCTION:
-      case ZX_EXCP_SW_BREAKPOINT:
-      case ZX_EXCP_HW_BREAKPOINT:
-      case ZX_EXCP_UNALIGNED_ACCESS:
-      case ZX_EXCP_POLICY_ERROR:
-        info.exception_watcher->OnException(info.task_koid,
-                                            packet.exception.tid, packet.type);
-        break;
-      default:
-        FXL_NOTREACHED();
-    }
-  } else if (ZX_PKT_IS_SIGNAL_REP(packet.type) &&
-             packet.signal.observed & ZX_PROCESS_TERMINATED) {
-    // This type of watcher also gets process terminated signals.
-    info.exception_watcher->OnProcessTerminated(info.task_koid);
-  } else {
-    FXL_NOTREACHED();
-  }
-}
-
-void MessageLoopZircon::OnJobException(const WatchInfo& info,
-                                       const zx_port_packet_t& packet) {
-  if (ZX_PKT_IS_EXCEPTION(packet.type)) {
-    // All debug exceptions.
-    switch (packet.type) {
-      case ZX_EXCP_PROCESS_STARTING:
-        info.exception_watcher->OnProcessStarting(
-            info.task_koid, packet.exception.pid, packet.exception.tid);
-        break;
-      default:
-        FXL_NOTREACHED();
-    }
-  } else {
-    FXL_NOTREACHED();
-  }
-}
-
-void MessageLoopZircon::OnSocketSignal(int watch_id, const WatchInfo& info,
-                                       const zx_port_packet_t& packet) {
-  if (!ZX_PKT_IS_SIGNAL_REP(packet.type))
-    return;
-
-  auto observed = packet.signal.observed;
-
-  // See if the socket was closed.
-  if ((observed & ZX_SOCKET_PEER_CLOSED) ||
-      (observed & ZX_SIGNAL_HANDLE_CLOSED)) {
-    info.socket_watcher->OnSocketError(info.socket_handle);
-    // |info| is can be deleted at this point, so don't use it anymore.
-    return;
-  }
-
-  // Dispatch readable signal.
-  if (observed & ZX_SOCKET_READABLE)
-    info.socket_watcher->OnSocketReadable(info.socket_handle);
-
-  // When signaling both readable and writable, make sure the readable handler
-  // didn't remove the watch.
-  if ((observed & ZX_SOCKET_READABLE) && (observed & ZX_SOCKET_WRITABLE)) {
-    std::lock_guard<std::mutex> guard(mutex_);
-    if (watches_.find(packet.key) == watches_.end())
-      return;
-  }
-
-  // Dispatch writable signal.
-  if (observed & ZX_SOCKET_WRITABLE)
-    info.socket_watcher->OnSocketWritable(info.socket_handle);
-}
-
-}  // namespace debug_ipc
diff --git a/src/developer/debug/shared/message_loop_zircon.h b/src/developer/debug/shared/message_loop_zircon.h
deleted file mode 100644
index e0704060968..00000000000
--- a/src/developer/debug/shared/message_loop_zircon.h
+++ /dev/null
@@ -1,94 +0,0 @@
-// Copyright 2018 The Fuchsia Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#pragma once
-
-#include <lib/fdio/unsafe.h>
-#include <lib/zx/event.h>
-#include <lib/zx/port.h>
-#include <lib/zx/thread.h>
-
-#include "src/developer/debug/shared/message_loop_target.h"
-
-namespace debug_ipc {
-
-class ZirconExceptionWatcher;
-class SocketWatcher;
-
-class MessageLoopZircon : public MessageLoopTarget {
- public:
-  MessageLoopZircon();
-  ~MessageLoopZircon();
-
-  void Init() override;
-  zx_status_t InitTarget() override;
-
-  void Cleanup() override;
-  void QuitNow() override;
-
-  Type GetType() const override { return Type::kZircon; }
-  bool SupportsFidl() const override { return false; }
-
-  // Returns the current message loop or null if there isn't one.
-  static MessageLoopZircon* Current();
-
-  // MessageLoop implementation.
-  WatchHandle WatchFD(WatchMode mode, int fd, FDWatcher* watcher) override;
-
-  zx_status_t WatchSocket(WatchMode mode, zx_handle_t socket_handle,
-                          SocketWatcher* watcher, WatchHandle* out) override;
-
-  zx_status_t WatchProcessExceptions(WatchProcessConfig config,
-                                     WatchHandle* out) override;
-
-  zx_status_t WatchJobExceptions(WatchJobConfig config,
-                                 WatchHandle* out) override;
-
-  zx_status_t ResumeFromException(zx_koid_t thread_koid, zx::thread& thread,
-                                  uint32_t options) override;
-
- private:
-  // Associated struct to track information about what type of resource a watch
-  // handle is following.
-  struct WatchInfo;
-
-  // MessageLoop protected implementation.
-  uint64_t GetMonotonicNowNS() const override;
-  void RunImpl() override;
-  void StopWatching(int id) override;
-  // Triggers an event signaling that there is a pending event.
-  void SetHasTasks() override;
-
-  // Check for any pending C++ tasks and process them.
-  // Returns true if there was an event pending to be processed.
-  bool CheckAndProcessPendingTasks();
-
-  // Handles WatchHandles event. These are all the events that are not C++
-  // tasks posted to the message loop.
-  void HandleException(zx_port_packet_t packet);
-
-  // Handle an event of the given type.
-  void OnFdioSignal(int watch_id, const WatchInfo& info,
-                    const zx_port_packet_t& packet);
-  void OnProcessException(const WatchInfo& info,
-                          const zx_port_packet_t& packet);
-  void OnJobException(const WatchInfo& info, const zx_port_packet_t& packet);
-  void OnSocketSignal(int watch_id, const WatchInfo& info,
-                      const zx_port_packet_t& packet);
-
-  using WatchMap = std::map<int, WatchInfo>;
-  WatchMap watches_;
-
-  // ID used as an index into watches_.
-  int next_watch_id_ = 1;
-
-  zx::port port_;
-
-  // This event is signaled when there are tasks to process.
-  zx::event task_event_;
-
-  FXL_DISALLOW_COPY_AND_ASSIGN(MessageLoopZircon);
-};
-
-}  // namespace debug_ipc
-- 
GitLab