1
    2
    3
    4
    5
    6
    7
    8
    9
   10
   11
   12
   13
   14
   15
   16
   17
   18
   19
   20
   21
   22
   23
   24
   25
   26
   27
   28
   29
   30
   31
   32
   33
   34
   35
   36
   37
   38
   39
   40
   41
   42
   43
   44
   45
   46
   47
   48
   49
   50
   51
   52
   53
   54
   55
   56
   57
   58
   59
   60
   61
   62
   63
   64
   65
   66
   67
   68
   69
   70
   71
   72
   73
   74
   75
   76
   77
   78
   79
   80
   81
   82
   83
   84
   85
   86
   87
   88
   89
   90
   91
   92
   93
   94
   95
   96
   97
   98
   99
  100
  101
  102
  103
  104
  105
  106
  107
  108
  109
  110
  111
  112
  113
  114
  115
  116
  117
  118
  119
  120
  121
  122
  123
  124
  125
  126
  127
  128
  129
  130
  131

mojo / core / handle_table.h [blame]

// Copyright 2014 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef MOJO_CORE_HANDLE_TABLE_H_
#define MOJO_CORE_HANDLE_TABLE_H_

#include <stdint.h>

#include <unordered_map>
#include <vector>

#include "base/gtest_prod_util.h"
#include "base/synchronization/lock.h"
#include "base/trace_event/memory_dump_provider.h"
#include "mojo/core/dispatcher.h"
#include "mojo/core/system_impl_export.h"
#include "mojo/public/c/system/types.h"

namespace mojo {
namespace core {

class MOJO_SYSTEM_IMPL_EXPORT HandleTable
    : public base::trace_event::MemoryDumpProvider {
 public:
  HandleTable();

  HandleTable(const HandleTable&) = delete;
  HandleTable& operator=(const HandleTable&) = delete;

  ~HandleTable() override;

  // HandleTable is thread-hostile. All access should be gated by GetLock().
  base::Lock& GetLock();

  MojoHandle AddDispatcher(scoped_refptr<Dispatcher> dispatcher);

  // Inserts multiple dispatchers received from message transit, populating
  // |handles| with their newly allocated handles. Returns |true| on success.
  bool AddDispatchersFromTransit(
      const std::vector<Dispatcher::DispatcherInTransit>& dispatchers,
      MojoHandle* handles);

  scoped_refptr<Dispatcher> GetDispatcher(MojoHandle handle);
  MojoResult GetAndRemoveDispatcher(MojoHandle,
                                    scoped_refptr<Dispatcher>* dispatcher);

  // Marks handles as busy and populates |dispatchers|. Returns MOJO_RESULT_BUSY
  // if any of the handles are already in transit; MOJO_RESULT_INVALID_ARGUMENT
  // if any of the handles are invalid; or MOJO_RESULT_OK if successful.
  MojoResult BeginTransit(
      const MojoHandle* handles,
      size_t num_handles,
      std::vector<Dispatcher::DispatcherInTransit>* dispatchers);

  void CompleteTransitAndClose(
      const std::vector<Dispatcher::DispatcherInTransit>& dispatchers);
  void CancelTransit(
      const std::vector<Dispatcher::DispatcherInTransit>& dispatchers);

  void GetActiveHandlesForTest(std::vector<MojoHandle>* handles);

 private:
  FRIEND_TEST_ALL_PREFIXES(HandleTableTest, OnMemoryDump);

  // MemoryDumpProvider implementation.
  bool OnMemoryDump(const base::trace_event::MemoryDumpArgs& args,
                    base::trace_event::ProcessMemoryDump* pmd) override;

  struct Entry {
    explicit Entry(scoped_refptr<Dispatcher> dispatcher);
    ~Entry();
    Entry(const Entry& entry);
    Entry& operator=(const Entry&) = delete;

    const scoped_refptr<Dispatcher> dispatcher;
    bool busy = false;
  };

  // A helper class for storing dispatchers that caches the last fetched
  // dispatcher. This is an optimization for the common case that the same
  // dispatcher is fetched repeatedly. Please see https://crbug.com/1295449 for
  // more details.
  class EntriesAccessor {
   public:
    EntriesAccessor();
    ~EntriesAccessor();

    // Returns whether an Entry was inserted.
    bool Add(MojoHandle handle, Entry entry);

    // Returns nullptr if a dispatcher is not found.
    const scoped_refptr<Dispatcher>* GetDispatcher(MojoHandle handle);

    // Returns nullptr if an entry is not found.
    Entry* GetMutable(MojoHandle handle);

    // See `Remove` below.
    enum RemovalCondition { kRemoveOnlyIfBusy, kRemoveOnlyIfNotBusy };

    // Returns whether an entry was found, and if found, `MOJO_RESULT_BUSY` if
    // `Entry.busy` is true and `MOJO_RESULT_OK` if `Entry.busy` is false. If an
    // entry is not found, `MOJO_RESULT_NOT_FOUND` is returned.
    //
    // If an entry is found, and if `removal_condition` matches `Entry.busy`, it
    // is removed from storage and -- if `dispatcher` is not nullptr -- the
    // corresponding dispatcher is returned in `dispatcher`. Otherwise,
    // `dispatcher` is left unchanged.
    MojoResult Remove(MojoHandle handle,
                      RemovalCondition removal_condition,
                      scoped_refptr<Dispatcher>* dispatcher);

    const std::unordered_map<MojoHandle, Entry>& GetUnderlyingMap() const;

   private:
    std::unordered_map<MojoHandle, Entry> handles_;
    scoped_refptr<Dispatcher> last_read_dispatcher_;
    MojoHandle last_read_handle_ = MOJO_HANDLE_INVALID;
  };

  EntriesAccessor entries_;

  base::Lock lock_;

  uintptr_t next_available_handle_ = 1;
};

}  // namespace core
}  // namespace mojo

#endif  // MOJO_CORE_HANDLE_TABLE_H_