// 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_CORE_PORTS_PORT_H_ #define MOJO_CORE_PORTS_PORT_H_ #include #include #include #include #include "mojo/core/ports/event.h" #include "mojo/core/ports/message_queue.h" #include "mojo/core/ports/user_data.h" #include "mozilla/Atomics.h" #include "mozilla/PlatformMutex.h" #include "mozilla/RefPtr.h" #include "nsISupportsImpl.h" namespace mojo { namespace core { namespace ports { class PortLocker; namespace detail { // Ports cannot use mozilla::Mutex, as the acquires-before relationships handled // by PortLocker can overload the debug-only deadlock detector. class MOZ_CAPABILITY("mutex") PortMutex : private ::mozilla::detail::MutexImpl { public: void AssertCurrentThreadOwns() const MOZ_ASSERT_CAPABILITY(this) { #ifdef DEBUG MOZ_ASSERT(mOwningThread == PR_GetCurrentThread()); #endif } private: // PortMutex should only be locked/unlocked via PortLocker friend class ::mojo::core::ports::PortLocker; void Lock() MOZ_CAPABILITY_ACQUIRE() { ::mozilla::detail::MutexImpl::lock(); #ifdef DEBUG mOwningThread = PR_GetCurrentThread(); #endif } void Unlock() MOZ_CAPABILITY_RELEASE() { #ifdef DEBUG MOZ_ASSERT(mOwningThread == PR_GetCurrentThread()); mOwningThread = nullptr; #endif ::mozilla::detail::MutexImpl::unlock(); } #ifdef DEBUG mozilla::Atomic mOwningThread; #endif }; } // namespace detail // A Port is essentially a node in a circular list of addresses. For the sake of // this documentation such a list will henceforth be referred to as a "route." // Routes are the fundamental medium upon which all Node event circulation takes // place and are thus the backbone of all Mojo message passing. // // Each Port is identified by a 128-bit address within a Node (see node.h). A // Port doesn't really *do* anything per se: it's a named collection of state, // and its owning Node manages all event production, transmission, routing, and // processing logic. See Node for more details on how Ports may be used to // transmit arbitrary user messages as well as other Ports. // // Ports may be in any of a handful of states (see State below) which dictate // how they react to system events targeting them. In the simplest and most // common case, Ports are initially created as an entangled pair (i.e. a simple // cycle consisting of two Ports) both in the |kReceiving| State. Consider Ports // we'll label |A| and |B| here, which may be created using // Node::CreatePortPair(): // // +-----+ +-----+ // | |--------->| | // | A | | B | // | |<---------| | // +-----+ +-----+ // // |A| references |B| via |peer_node_name| and |peer_port_name|, while |B| in // turn references |A|. Note that a Node is NEVER aware of who is sending events // to a given Port; it is only aware of where it must route events FROM a given // Port. // // For the sake of documentation, we refer to one receiving port in a route as // the "conjugate" of the other. A receiving port's conjugate is also its peer // upon initial creation, but because of proxying this may not be the case at a // later time. // // ALL access to this data structure must be guarded by |lock_| acquisition, // which is only possible using a PortLocker. PortLocker ensures that // overlapping Port lock acquisitions on a single thread are always acquired in // a globally consistent order. class Port { NS_INLINE_DECL_THREADSAFE_REFCOUNTING(Port) public: // The state of a given Port. A Port may only exist in one of these states at // any given time. enum State { // The Port is not yet paired with a peer and is therefore unusable. See // Node::CreateUninitializedPort and Node::InitializePort for motivation. kUninitialized, // The Port is publicly visible outside of its Node and may be used to send // and receive user messages. There are always AT MOST two |kReceiving| // Ports along any given route. A user message event sent from a receiving // port is always circulated along the Port's route until it reaches either // a dead-end -- in which case the route is broken -- or it reaches the // other receiving Port in the route -- in which case it lands in that // Port's incoming message queue which can by read by user code. kReceiving, // The Port has been taken out of the |kReceiving| state in preparation for // proxying to a new destination. A Port enters this state immediately when // it's attached to a user message and may only leave this state when // transitioning to |kProxying|. See Node for more details. kBuffering, // The Port is forwarding all user messages (and most other events) to its // peer without discretion. Ports in the |kProxying| state may never leave // this state and only exist temporarily until their owning Node has // established that no more events will target them. See Node for more // details. kProxying, // The Port has been closed and is now permanently unusable. Only // |kReceiving| ports can be closed. kClosed }; // The current State of the Port. State state; // The Node and Port address to which events should be routed FROM this Port. // Note that this is NOT necessarily the address of the Port currently sending // events TO this Port. NodeName peer_node_name; PortName peer_port_name; // The next available sequence number to use for outgoing user message events // originating from this port. uint64_t next_sequence_num_to_send; // The largest acknowledged user message event sequence number. uint64_t last_sequence_num_acknowledged; // The interval for which acknowledge requests will be sent. A value of N will // cause an acknowledge request for |last_sequence_num_acknowledged| + N when // initially set and on received acknowledge. This means that the lower bound // for unread or in-transit messages is |next_sequence_num_to_send| - // |last_sequence_num_acknowledged| + |sequence_number_acknowledge_interval|. // If zero, no acknowledge requests are sent. uint64_t sequence_num_acknowledge_interval; // The sequence number of the last message this Port should ever expect to // receive in its lifetime. May be used to determine that a proxying port is // ready to be destroyed or that a receiving port's conjugate has been closed // and we know the sequence number of the last message it sent. uint64_t last_sequence_num_to_receive; // The sequence number of the message for which this Port should send an // acknowledge message. In the buffering state, holds the acknowledge request // value that is forwarded to the peer on transition to proxying. // This is zero in any port that's never received an acknowledge request, and // in proxies that have forwarded a stored acknowledge. uint64_t sequence_num_to_acknowledge; // The queue of incoming user messages received by this Port. Only non-empty // for buffering or receiving Ports. When a buffering port enters the proxying // state, it flushes its queue and the proxy then bypasses the queue // indefinitely. // // A receiving port's queue only has elements removed by user code reading // messages from the port. // // Note that this is a priority queue which only exposes messages to consumers // in strict sequential order. MessageQueue message_queue; // In some edge cases, a Node may need to remember to route a single special // event upon destruction of this (proxying) Port. That event is stashed here // in the interim. mozilla::UniquePtr> send_on_proxy_removal; // Arbitrary user data attached to the Port. In practice, Mojo uses this to // stash an observer interface which can be notified about various Port state // changes. RefPtr user_data; // Indicates that this (proxying) Port has received acknowledgement that no // new user messages will be routed to it. If |true|, the proxy will be // removed once it has received and forwarded all sequenced messages up to and // including the one numbered |last_sequence_num_to_receive|. bool remove_proxy_on_last_message; // Indicates that this Port is aware that its nearest (in terms of forward, // non-zero cyclic routing distance) receiving Port has been closed. bool peer_closed; // Indicates that this Port lost its peer unexpectedly (e.g. via process death // rather than receiving an ObserveClosure event). In this case // |peer_closed| will be true but |last_sequence_num_to_receive| cannot be // known. Such ports will continue to make message available until their // message queue is empty. bool peer_lost_unexpectedly; Port(uint64_t next_sequence_num_to_send, uint64_t next_sequence_num_to_receive); Port(const Port&) = delete; void operator=(const Port&) = delete; void AssertLockAcquired() { lock_.AssertCurrentThreadOwns(); } private: friend class PortLocker; ~Port(); // This lock guards all fields in Port, but is locked in a unique way which is // unfortunately somewhat difficult to get to work with the thread-safety // analysis. detail::PortMutex lock_ MOZ_ANNOTATED; }; } // namespace ports } // namespace core } // namespace mojo #endif // MOJO_CORE_PORTS_PORT_H_