普通文本  |  114行  |  3.51 KB

// Copyright 2016 The Chromium 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 "mojo/public/cpp/bindings/sync_handle_registry.h"

#include "base/lazy_instance.h"
#include "base/logging.h"
#include "base/stl_util.h"
#include "base/threading/thread_local.h"
#include "mojo/public/c/system/core.h"

namespace mojo {
namespace {

base::LazyInstance<base::ThreadLocalPointer<SyncHandleRegistry>>
    g_current_sync_handle_watcher = LAZY_INSTANCE_INITIALIZER;

}  // namespace

// static
scoped_refptr<SyncHandleRegistry> SyncHandleRegistry::current() {
  scoped_refptr<SyncHandleRegistry> result(
      g_current_sync_handle_watcher.Pointer()->Get());
  if (!result) {
    result = new SyncHandleRegistry();
    DCHECK_EQ(result.get(), g_current_sync_handle_watcher.Pointer()->Get());
  }
  return result;
}

bool SyncHandleRegistry::RegisterHandle(const Handle& handle,
                                        MojoHandleSignals handle_signals,
                                        const HandleCallback& callback) {
  DCHECK(thread_checker_.CalledOnValidThread());

  if (ContainsKey(handles_, handle))
    return false;

  MojoResult result = MojoAddHandle(wait_set_handle_.get().value(),
                                    handle.value(), handle_signals);
  if (result != MOJO_RESULT_OK)
    return false;

  handles_[handle] = callback;
  return true;
}

void SyncHandleRegistry::UnregisterHandle(const Handle& handle) {
  DCHECK(thread_checker_.CalledOnValidThread());
  if (!ContainsKey(handles_, handle))
    return;

  MojoResult result =
      MojoRemoveHandle(wait_set_handle_.get().value(), handle.value());
  DCHECK_EQ(MOJO_RESULT_OK, result);
  handles_.erase(handle);
}

bool SyncHandleRegistry::WatchAllHandles(const bool* should_stop[],
                                         size_t count) {
  DCHECK(thread_checker_.CalledOnValidThread());

  MojoResult result;
  uint32_t num_ready_handles;
  MojoHandle ready_handle;
  MojoResult ready_handle_result;

  scoped_refptr<SyncHandleRegistry> preserver(this);
  while (true) {
    for (size_t i = 0; i < count; ++i)
      if (*should_stop[i])
        return true;
    do {
      result = Wait(wait_set_handle_.get(), MOJO_HANDLE_SIGNAL_READABLE,
                    MOJO_DEADLINE_INDEFINITE, nullptr);
      if (result != MOJO_RESULT_OK)
        return false;

      // TODO(yzshen): Theoretically it can reduce sync call re-entrancy if we
      // give priority to the handle that is waiting for sync response.
      num_ready_handles = 1;
      result = MojoGetReadyHandles(wait_set_handle_.get().value(),
                                   &num_ready_handles, &ready_handle,
                                   &ready_handle_result, nullptr);
      if (result != MOJO_RESULT_OK && result != MOJO_RESULT_SHOULD_WAIT)
        return false;
    } while (result == MOJO_RESULT_SHOULD_WAIT);

    const auto iter = handles_.find(Handle(ready_handle));
    iter->second.Run(ready_handle_result);
  };

  return false;
}

SyncHandleRegistry::SyncHandleRegistry() {
  MojoHandle handle;
  MojoResult result = MojoCreateWaitSet(&handle);
  CHECK_EQ(MOJO_RESULT_OK, result);
  wait_set_handle_.reset(Handle(handle));
  CHECK(wait_set_handle_.is_valid());

  DCHECK(!g_current_sync_handle_watcher.Pointer()->Get());
  g_current_sync_handle_watcher.Pointer()->Set(this);
}

SyncHandleRegistry::~SyncHandleRegistry() {
  DCHECK(thread_checker_.CalledOnValidThread());
  g_current_sync_handle_watcher.Pointer()->Set(nullptr);
}

}  // namespace mojo