| // 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. |
| |
| #ifndef MOJO_EDK_SYSTEM_PORTS_NODE_H_ |
| #define MOJO_EDK_SYSTEM_PORTS_NODE_H_ |
| |
| #include <stddef.h> |
| #include <stdint.h> |
| |
| #include <queue> |
| #include <unordered_map> |
| |
| #include "base/macros.h" |
| #include "base/memory/ref_counted.h" |
| #include "base/synchronization/lock.h" |
| #include "mojo/edk/system/ports/event.h" |
| #include "mojo/edk/system/ports/message.h" |
| #include "mojo/edk/system/ports/name.h" |
| #include "mojo/edk/system/ports/port.h" |
| #include "mojo/edk/system/ports/port_ref.h" |
| #include "mojo/edk/system/ports/user_data.h" |
| |
| #undef SendMessage // Gah, windows |
| |
| namespace mojo { |
| namespace edk { |
| namespace ports { |
| |
| enum : int { |
| OK = 0, |
| ERROR_PORT_UNKNOWN = -10, |
| ERROR_PORT_EXISTS = -11, |
| ERROR_PORT_STATE_UNEXPECTED = -12, |
| ERROR_PORT_CANNOT_SEND_SELF = -13, |
| ERROR_PORT_PEER_CLOSED = -14, |
| ERROR_PORT_CANNOT_SEND_PEER = -15, |
| ERROR_NOT_IMPLEMENTED = -100, |
| }; |
| |
| struct PortStatus { |
| bool has_messages; |
| bool receiving_messages; |
| bool peer_closed; |
| }; |
| |
| class MessageFilter; |
| class NodeDelegate; |
| |
| class Node { |
| public: |
| enum class ShutdownPolicy { |
| DONT_ALLOW_LOCAL_PORTS, |
| ALLOW_LOCAL_PORTS, |
| }; |
| |
| // Does not take ownership of the delegate. |
| Node(const NodeName& name, NodeDelegate* delegate); |
| ~Node(); |
| |
| // Returns true iff there are no open ports referring to another node or ports |
| // in the process of being transferred from this node to another. If this |
| // returns false, then to ensure clean shutdown, it is necessary to keep the |
| // node alive and continue routing messages to it via AcceptMessage. This |
| // method may be called again after AcceptMessage to check if the Node is now |
| // ready to be destroyed. |
| // |
| // If |policy| is set to |ShutdownPolicy::ALLOW_LOCAL_PORTS|, this will return |
| // |true| even if some ports remain alive, as long as none of them are proxies |
| // to another node. |
| bool CanShutdownCleanly( |
| ShutdownPolicy policy = ShutdownPolicy::DONT_ALLOW_LOCAL_PORTS); |
| |
| // Lookup the named port. |
| int GetPort(const PortName& port_name, PortRef* port_ref); |
| |
| // Creates a port on this node. Before the port can be used, it must be |
| // initialized using InitializePort. This method is useful for bootstrapping |
| // a connection between two nodes. Generally, ports are created using |
| // CreatePortPair instead. |
| int CreateUninitializedPort(PortRef* port_ref); |
| |
| // Initializes a newly created port. |
| int InitializePort(const PortRef& port_ref, |
| const NodeName& peer_node_name, |
| const PortName& peer_port_name); |
| |
| // Generates a new connected pair of ports bound to this node. These ports |
| // are initialized and ready to go. |
| int CreatePortPair(PortRef* port0_ref, PortRef* port1_ref); |
| |
| // User data associated with the port. |
| int SetUserData(const PortRef& port_ref, scoped_refptr<UserData> user_data); |
| int GetUserData(const PortRef& port_ref, |
| scoped_refptr<UserData>* user_data); |
| |
| // Prevents further messages from being sent from this port or delivered to |
| // this port. The port is removed, and the port's peer is notified of the |
| // closure after it has consumed all pending messages. |
| int ClosePort(const PortRef& port_ref); |
| |
| // Returns the current status of the port. |
| int GetStatus(const PortRef& port_ref, PortStatus* port_status); |
| |
| // Returns the next available message on the specified port or returns a null |
| // message if there are none available. Returns ERROR_PORT_PEER_CLOSED to |
| // indicate that this port's peer has closed. In such cases GetMessage may |
| // be called until it yields a null message, indicating that no more messages |
| // may be read from the port. |
| // |
| // If |filter| is non-null, the next available message is returned only if it |
| // is matched by the filter. If the provided filter does not match the next |
| // available message, GetMessage() behaves as if there is no message |
| // available. Ownership of |filter| is not taken, and it must outlive the |
| // extent of this call. |
| int GetMessage(const PortRef& port_ref, |
| ScopedMessage* message, |
| MessageFilter* filter); |
| |
| // Sends a message from the specified port to its peer. Note that the message |
| // notification may arrive synchronously (via PortStatusChanged() on the |
| // delegate) if the peer is local to this Node. |
| int SendMessage(const PortRef& port_ref, ScopedMessage message); |
| |
| // Corresponding to NodeDelegate::ForwardMessage. |
| int AcceptMessage(ScopedMessage message); |
| |
| // Called to merge two ports with each other. If you have two independent |
| // port pairs A <=> B and C <=> D, the net result of merging B and C is a |
| // single connected port pair A <=> D. |
| // |
| // Note that the behavior of this operation is undefined if either port to be |
| // merged (B or C above) has ever been read from or written to directly, and |
| // this must ONLY be called on one side of the merge, though it doesn't matter |
| // which side. |
| // |
| // It is safe for the non-merged peers (A and D above) to be transferred, |
| // closed, and/or written to before, during, or after the merge. |
| int MergePorts(const PortRef& port_ref, |
| const NodeName& destination_node_name, |
| const PortName& destination_port_name); |
| |
| // Like above but merges two ports local to this node. Because both ports are |
| // local this can also verify that neither port has been written to before the |
| // merge. If this fails for any reason, both ports are closed. Otherwise OK |
| // is returned and the ports' receiving peers are connected to each other. |
| int MergeLocalPorts(const PortRef& port0_ref, const PortRef& port1_ref); |
| |
| // Called to inform this node that communication with another node is lost |
| // indefinitely. This triggers cleanup of ports bound to this node. |
| int LostConnectionToNode(const NodeName& node_name); |
| |
| private: |
| class LockedPort; |
| |
| // Note: Functions that end with _Locked require |ports_lock_| to be held |
| // before calling. |
| int OnUserMessage(ScopedMessage message); |
| int OnPortAccepted(const PortName& port_name); |
| int OnObserveProxy(const PortName& port_name, |
| const ObserveProxyEventData& event); |
| int OnObserveProxyAck(const PortName& port_name, uint64_t last_sequence_num); |
| int OnObserveClosure(const PortName& port_name, uint64_t last_sequence_num); |
| int OnMergePort(const PortName& port_name, const MergePortEventData& event); |
| |
| int AddPortWithName(const PortName& port_name, scoped_refptr<Port> port); |
| void ErasePort(const PortName& port_name); |
| void ErasePort_Locked(const PortName& port_name); |
| scoped_refptr<Port> GetPort(const PortName& port_name); |
| scoped_refptr<Port> GetPort_Locked(const PortName& port_name); |
| |
| int SendMessageInternal(const PortRef& port_ref, ScopedMessage* message); |
| int MergePorts_Locked(const PortRef& port0_ref, const PortRef& port1_ref); |
| void WillSendPort(const LockedPort& port, |
| const NodeName& to_node_name, |
| PortName* port_name, |
| PortDescriptor* port_descriptor); |
| int AcceptPort(const PortName& port_name, |
| const PortDescriptor& port_descriptor); |
| |
| int WillSendMessage_Locked(const LockedPort& port, |
| const PortName& port_name, |
| Message* message); |
| int BeginProxying_Locked(const LockedPort& port, const PortName& port_name); |
| int BeginProxying(PortRef port_ref); |
| int ForwardMessages_Locked(const LockedPort& port, const PortName& port_name); |
| void InitiateProxyRemoval(const LockedPort& port, const PortName& port_name); |
| void MaybeRemoveProxy_Locked(const LockedPort& port, |
| const PortName& port_name); |
| void TryRemoveProxy(PortRef port_ref); |
| void DestroyAllPortsWithPeer(const NodeName& node_name, |
| const PortName& port_name); |
| |
| ScopedMessage NewInternalMessage_Helper(const PortName& port_name, |
| const EventType& type, |
| const void* data, |
| size_t num_data_bytes); |
| |
| ScopedMessage NewInternalMessage(const PortName& port_name, |
| const EventType& type) { |
| return NewInternalMessage_Helper(port_name, type, nullptr, 0); |
| } |
| |
| template <typename EventData> |
| ScopedMessage NewInternalMessage(const PortName& port_name, |
| const EventType& type, |
| const EventData& data) { |
| return NewInternalMessage_Helper(port_name, type, &data, sizeof(data)); |
| } |
| |
| const NodeName name_; |
| NodeDelegate* const delegate_; |
| |
| // Guards |ports_| as well as any operation which needs to hold multiple port |
| // locks simultaneously. Usage of this is subtle: it must NEVER be acquired |
| // after a Port lock is acquired, and it must ALWAYS be acquired before |
| // calling WillSendMessage_Locked or ForwardMessages_Locked. |
| base::Lock ports_lock_; |
| std::unordered_map<PortName, scoped_refptr<Port>> ports_; |
| |
| DISALLOW_COPY_AND_ASSIGN(Node); |
| }; |
| |
| } // namespace ports |
| } // namespace edk |
| } // namespace mojo |
| |
| #endif // MOJO_EDK_SYSTEM_PORTS_NODE_H_ |