NodeController.h (7491B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this file, 5 * You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #ifndef mozilla_ipc_NodeController_h 8 #define mozilla_ipc_NodeController_h 9 10 #include "mojo/core/ports/event.h" 11 #include "mojo/core/ports/name.h" 12 #include "mojo/core/ports/node.h" 13 #include "mojo/core/ports/node_delegate.h" 14 #include "chrome/common/ipc_message.h" 15 #include "mozilla/ipc/ProtocolUtils.h" 16 #include "nsTHashMap.h" 17 #include "mozilla/Queue.h" 18 #include "mozilla/DataMutex.h" 19 #include "mozilla/UniquePtr.h" 20 #include "mozilla/ipc/NodeChannel.h" 21 22 namespace mozilla::ipc { 23 24 class GeckoChildProcessHost; 25 26 class NodeController final : public mojo::core::ports::NodeDelegate, 27 public NodeChannel::Listener { 28 using NodeName = mojo::core::ports::NodeName; 29 using PortName = mojo::core::ports::PortName; 30 using PortRef = mojo::core::ports::PortRef; 31 using Event = mojo::core::ports::Event; 32 using Node = mojo::core::ports::Node; 33 using UserData = mojo::core::ports::UserData; 34 using PortStatus = mojo::core::ports::PortStatus; 35 using UserMessageEvent = mojo::core::ports::UserMessageEvent; 36 using UserMessage = mojo::core::ports::UserMessage; 37 38 public: 39 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(NodeController, override) 40 41 // Return the global singleton instance. The returned value is only valid 42 // while the IO thread is alive. 43 static NodeController* GetSingleton(); 44 45 class PortObserver : public UserData { 46 public: 47 virtual void OnPortStatusChanged() = 0; 48 49 protected: 50 ~PortObserver() override = default; 51 }; 52 53 // NOTE: For now there will always be a single broker process, and all 54 // processes in the graph need to be able to talk to it (the parent process). 55 // Give it a fixed node name for now to simplify things. 56 // 57 // If we ever decide to have multiple node networks intercommunicating (e.g. 58 // multiple instances or background services), we may need to change this. 59 static constexpr NodeName kBrokerNodeName{0x1, 0x1}; 60 61 bool IsBroker() const { return mName == kBrokerNodeName; } 62 63 // Mint a new connected pair of ports within the current process. 64 std::pair<ScopedPort, ScopedPort> CreatePortPair(); 65 66 // Get a reference to the port with the given name. Returns an invalid 67 // `PortRef` if the name wasn't found. 68 PortRef GetPort(const PortName& aName); 69 70 // Set the observer for the given port. This observer will be notified when 71 // the status of the port changes. 72 void SetPortObserver(const PortRef& aPort, PortObserver* aObserver); 73 74 // See `mojo::core::ports::Node::GetStatus` 75 Maybe<PortStatus> GetStatus(const PortRef& aPort); 76 77 // See `mojo::core::ports::Node::ClosePort` 78 void ClosePort(const PortRef& aPort); 79 80 // Send a message to the the port's connected peer. 81 bool SendUserMessage(const PortRef& aPort, UniquePtr<IPC::Message> aMessage); 82 83 // Get the next message from the port's message queue. 84 // Will set `*aMessage` to the found message, or `nullptr`. 85 // Returns `false` and sets `*aMessage` to `nullptr` if no further messages 86 // will be delivered to this port as its peer has been closed. 87 bool GetMessage(const PortRef& aPort, UniquePtr<IPC::Message>* aMessage); 88 89 // Called in the broker process from GeckoChildProcessHost to introduce a new 90 // child process into the network. On success, `aInitialPort` will be a 91 // `PortRef` which can be used to communicate with the `PortRef` returned from 92 // `InitChildProcess`, `aNodeChannel` will be a reference to the `NodeChannel` 93 // created for the new process, and `aClientHandle` will be the 94 // `ChannelHandle` to pass to the child process on the command line. 95 // The port can immediately have messages sent to it. 96 bool InviteChildProcess(GeckoChildProcessHost* aChildProcessHost, 97 IPC::Channel::ChannelHandle* aClientHandle, 98 ScopedPort* aInitialPort, NodeChannel** aNodeChannel); 99 100 // Called as the IO thread is started in the parent process. 101 static void InitBrokerProcess(const IPC::Channel::ChannelKind* aChannelKind); 102 103 // Called as the IO thread is started in a child process. 104 static ScopedPort InitChildProcess( 105 IPC::Channel::ChannelHandle&& aChannelHandle, base::ProcessId aParentPid); 106 107 // Called when the IO thread is torn down. 108 static void CleanUp(); 109 110 private: 111 NodeController(const NodeName& aName, 112 const IPC::Channel::ChannelKind* aChannelKind); 113 ~NodeController(); 114 115 UniquePtr<IPC::Message> SerializeEventMessage( 116 UniquePtr<Event> aEvent, const NodeName* aRelayTarget = nullptr, 117 uint32_t aType = EVENT_MESSAGE_TYPE); 118 UniquePtr<Event> DeserializeEventMessage(UniquePtr<IPC::Message> aMessage, 119 NodeName* aRelayTarget = nullptr); 120 121 // Get the `NodeChannel` for the named node. 122 already_AddRefed<NodeChannel> GetNodeChannel(const NodeName& aName); 123 124 // Stop communicating with this peer. Must be called on the IO thread. 125 void DropPeer(NodeName aNodeName); 126 127 // Ensure that there is a direct connection to a remote node, requesting an 128 // introduction if there is not. 129 // If provided, will optionally send an event to the remote node. 130 void ContactRemotePeer(const NodeName& aNode, UniquePtr<Event> aEvent); 131 132 // Message Handlers 133 void OnEventMessage(const NodeName& aFromNode, 134 UniquePtr<IPC::Message> aMessage) override; 135 void OnBroadcast(const NodeName& aFromNode, 136 UniquePtr<IPC::Message> aMessage) override; 137 void OnIntroduce(const NodeName& aFromNode, 138 NodeChannel::Introduction aIntroduction) override; 139 void OnRequestIntroduction(const NodeName& aFromNode, 140 const NodeName& aName) override; 141 void OnAcceptInvite(const NodeName& aFromNode, const NodeName& aRealName, 142 const PortName& aInitialPort) override; 143 void OnChannelError(const NodeName& aFromNode) override; 144 145 // NodeDelegate Implementation 146 void ForwardEvent(const NodeName& aNode, UniquePtr<Event> aEvent) override; 147 void BroadcastEvent(UniquePtr<Event> aEvent) override; 148 void PortStatusChanged(const PortRef& aPortRef) override; 149 void ObserveRemoteNode(const NodeName& aNode) override; 150 151 const NodeName mName; 152 const UniquePtr<Node> mNode; 153 const IPC::Channel::ChannelKind* const mChannelKind; 154 155 template <class T> 156 using NodeMap = nsTHashMap<NodeNameHashKey, T>; 157 158 struct Invite { 159 // The channel which is being invited. This will have a temporary name until 160 // the invite is completed. 161 RefPtr<NodeChannel> mChannel; 162 // The port which will be merged with the port information from the new 163 // child process when recieved. 164 PortRef mToMerge; 165 }; 166 167 struct State { 168 // Channels for connecting to all known peers. 169 NodeMap<RefPtr<NodeChannel>> mPeers; 170 171 // Messages which are queued for peers which we been introduced to yet. 172 NodeMap<Queue<UniquePtr<IPC::Message>, 64>> mPendingMessages; 173 174 // Connections for peers being invited to the network. 175 NodeMap<Invite> mInvites; 176 177 // Ports which are waiting to be merged by a particular peer node. 178 NodeMap<nsTArray<PortRef>> mPendingMerges; 179 }; 180 181 DataMutex<State> mState{"NodeController::mState"}; 182 }; 183 184 } // namespace mozilla::ipc 185 186 #endif