chromium/third_party/ipcz/include/ipcz/ipcz.h

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

#ifndef IPCZ_INCLUDE_IPCZ_IPCZ_H_
#define IPCZ_INCLUDE_IPCZ_IPCZ_H_

// ipcz is a cross-platform C library for interprocess communication (IPC) which
// supports efficient routing and data transfer over a large number of
// dynamically relocatable messaging endpoints.
//
// ipcz operates in terms of a small handful of abstractions encapsulated in
// this header: nodes, portals, parcels, drivers, boxes, and traps.
//
// NOTE: This header is intended to compile under C++11 or newer, and C99 or
// newer. The ABI defined here can be considered stable.
//
// Glossary
// --------
// *Nodes* are used by ipcz to model isolated units of an application. A typical
// application will create one ipcz node within each OS process it controls.
//
// *Portals* are messaging endpoints which belong to a specific node. Portals
// are created in entangled pairs: whatever goes into one portal comes out the
// other (its "peer"). Pairs may be created local to a single node, or they may
// be created to span two nodes. Portals may also be transferred freely through
// other portals.
//
// *Parcels* are the unit of communication between portals. Parcels can contain
// arbitrary application data as well as ipcz handles. Handles within a parcel
// are used to transfer objects (namely other portals, or driver-defined
// objects) from one portal to another, potentially on a different node.
//
// *Traps* provide a flexible mechanism to observe interesting portal state
// changes such as new parcels arriving or a portal's peer being closed.
//
// *Drivers* are provided by applications to implement platform-specific IPC
// details. They may also define new types of objects to be transmitted in
// parcels via boxes.
//
// *Boxes* are opaque objects used to wrap driver- or application-defined
// objects for seamless transmission across portals. Applications use the Box()
// and Unbox() APIs to go between concrete objects and transferrable box
// handles, and ipcz delegates to the driver or application to serialize boxed
// objects as needed for transmission.
//
// Overview
// --------
// To use ipcz effectively, an application must create multiple nodes to be
// interconnected. One node must be designated as the "broker" by the
// application (see CreateNode() flags). The broker is used by ipcz to
// coordinate certain types of internal transactions which demand a heightened
// level of trust and capability, so a broker node should always live in a more
// trustworthy process. For example in Chrome, the browser process is
// designated as the broker.
//
// In order for a node to communicate with other nodes in the system, the
// application must explicitly connect it to ONE other node using the
// ConnectNode() API. Once this is done, ipcz can automatically connect the node
// to additional other nodes as needed for efficient portal operation.
//
// In the example below, assume node A is designated as the broker. Nodes A and
// B have been connected directly by ConnectNode() calls in the application.
// Nodes A and C have been similarly connected:
//
//                    ┌───────┐
//     ConnectNode()  │       │  ConnectNode()
//        ┌──────────>O   A   O<───────────┐
//        │           │       │            │
//        │           └───────┘            │
//        │                                │
//        v ConnectNode()                  v ConnectNode()
//    ┌───O───┐                        ┌───O───┐
//    │       │                        │       │
//    │   B   │                        │   C   │
//    │       │                        │       │
//    └───────┘                        └───────┘
//
// ConnectNode() establishes initial portal pairs to link the two nodes
// together, illustrated above as "O"s. Once ConnectNode() returns, the
// application may immediately begin transmitting parcels through these portals.
//
// Now suppose node B creates a new local pair of portals (using OpenPortals())
// and sends one of those new portals in a parcel through its
// already-established portal to node A. The sent portal is effectively
// transferred to node A, and because its entangled peer still lives on node B
// there are now TWO portal pairs between nodes A and B:
//
//                    ┌───────┐
//                    │       │
//        ┌──────────>O   A   O<───────────┐
//        │ ┌────────>O       │            │
//        │ │         └───────┘            │
//        │ │                              │
//        v v                              v
//    ┌───O─O─┐                        ┌───O───┐
//    │       │                        │       │
//    │   B   │                        │   C   │
//    │       │                        │       │
//    └───────┘                        └───────┘
//
// Finally, suppose now the application takes this new portal on node A and
// sends it further along, through node A's already-established portal to node
// C. Because the transferred portal's peer still lives on node B, there is now
// a portal pair spanning nodes B and C:
//
//                    ┌───────┐
//                    │       │
//        ┌──────────>O   A   O<───────────┐
//        │           │       │            │
//        │           └───────┘            │
//        │                                │
//        v                                v
//    ┌───O───┐                        ┌───O───┐
//    │       │                        │       │
//    │   B   O────────────────────────O   C   │
//    │       │                        │       │
//    └───────┘                        └───────┘
//
// These two nodes were never explicitly connected by the application, but ipcz
// ensures that the portals will operate as expected. Behind the scenes, ipcz
// achieves this by establishing a direct, secure, and efficient communication
// channel between nodes B and C.

#include <stddef.h>
#include <stdint.h>

#define IPCZ_NO_FLAGS

// Helper to clarify flag definitions.
#define IPCZ_FLAG_BIT(bit)

// Opaque handle to an ipcz object.
IpczHandle;

// An IpczHandle value which is always invalid. Note that arbitrary non-zero
// values are not necessarily valid either, but zero is never valid.
#define IPCZ_INVALID_HANDLE

// Generic result code for all ipcz operations. See IPCZ_RESULT_* values below.
IpczResult;

// Specific meaning of each value depends on context, but IPCZ_RESULT_OK always
// indicates success. These values are derived from common status code
// definitions across Google software.
#define IPCZ_RESULT_OK
#define IPCZ_RESULT_CANCELLED
#define IPCZ_RESULT_UNKNOWN
#define IPCZ_RESULT_INVALID_ARGUMENT
#define IPCZ_RESULT_DEADLINE_EXCEEDED
#define IPCZ_RESULT_NOT_FOUND
#define IPCZ_RESULT_ALREADY_EXISTS
#define IPCZ_RESULT_PERMISSION_DENIED
#define IPCZ_RESULT_RESOURCE_EXHAUSTED
#define IPCZ_RESULT_FAILED_PRECONDITION
#define IPCZ_RESULT_ABORTED
#define IPCZ_RESULT_OUT_OF_RANGE
#define IPCZ_RESULT_UNIMPLEMENTED
#define IPCZ_RESULT_INTERNAL
#define IPCZ_RESULT_UNAVAILABLE
#define IPCZ_RESULT_DATA_LOSS

// Helper to specify explicit struct alignment across C and C++ compilers.
#if defined(__cplusplus)
#define IPCZ_ALIGN(alignment)
#elif defined(__GNUC__)
#define IPCZ_ALIGN
#elif defined(_MSC_VER)
#define IPCZ_ALIGN
#else
#error "IPCZ_ALIGN() is not defined for your compiler."
#endif

// Helper to generate the smallest constant value which is aligned with
// `alignment` and at least as large as `value`.
#define IPCZ_ALIGNED(value, alignment)

// Helper used to explicitly specify calling convention or other
// compiler-specific annotations for each API function.
#if defined(IPCZ_API_OVERRIDE)
#define IPCZ_API
#elif defined(_WIN32)
#define IPCZ_API
#else
#define IPCZ_API
#endif

// An opaque handle value created by an IpczDriver implementation. ipcz uses
// such handles to provide relevant context when calling back into the driver.
IpczDriverHandle;

#define IPCZ_INVALID_DRIVER_HANDLE

// Flags which may be passed by a driver to an IpczTransportActivityHandler when
// notifying ipcz about transport activity.
IpczTransportActivityFlags;

// Indicates that the driver encountered an unrecoverable error while using the
// transport. This generally results in ipcz deactivating the transport via the
// driver's DeactivateTransport().
#define IPCZ_TRANSPORT_ACTIVITY_ERROR

// Informs ipcz that the driver will no longer invoke the activity handler for
// a given listener, as the driver is no longer listening for activity on the
// corresponding transport.
#define IPCZ_TRANSPORT_ACTIVITY_DEACTIVATED

#if defined(__cplusplus)
extern "C" {
#endif

// Notifies ipcz of activity on a transport. `listener` must be a handle to an
// active transport's listener, as provided to the driver by ipcz via
// ActivateTransport().
//
// Drivers use this function to feed incoming data and driver handles from a
// transport to ipcz, or to inform ipcz of any unrecoverable dysfunction of the
// transport. In the latter case, drivers specify IPCZ_TRANSPORT_ACTIVITY_ERROR
// in `flags` to instigate deactivation and disposal of the transport by ipcz.
//
// If IPCZ_TRANSPORT_ACTIVITY_DEACTIVATED is set in `flags`, this must be the
// last call made by the driver for the given `listener`. See also
// DeactivateTransport() defined on IpczDriver below.
//
// `options` is currently unused and must be null.
//
// IMPORTANT: Drivers must ensure that all calls to this handler for the same
// `listener` are mutually exclusive. Overlapping calls are unsafe and will
// result in undefined behavior.
IpczTransportActivityHandler;                    // in

// Structure to be filled in by a driver's GetSharedMemoryInfo().
struct IPCZ_ALIGN(8) IpczSharedMemoryInfo {};

// IpczDriver
// ==========
//
// IpczDriver is a function table to be populated by the application and
// provided to ipcz when creating a new node. The driver implements concrete
// I/O operations to facilitate communication between nodes, giving embedding
// systems full control over choice of OS-specific transport mechanisms and I/O
// scheduling decisions.
struct IPCZ_ALIGN(8) IpczDriver {};

#if defined(__cplusplus)
}  // extern "C"
#endif

// Flags which may be passed via the `memory_flags` field of
// IpczCreateNodeOptions to configure features of ipcz internal memory
// allocation and usage.
IpczMemoryFlags;

// If this flag is set, the node will not attempt to expand the shared memory
// pools it uses to allocate parcel data between itself and other nodes.
//
// This means more application messages may fall back onto driver I/O for
// transmission, but also that ipcz' memory footprint will remain largely
// constant. Note that memory may still be expanded to facilitate new portal
// links as needed.
#define IPCZ_MEMORY_FIXED_PARCEL_CAPACITY

// Feature identifiers which may be passed through IpczCreateNodeOptions to
// control dynamic runtime features.
IpczFeature;

// When this feature is enabled, ipcz will use alternative shared memory layout
// and allocation behavior intended to be more efficient than the v1 scheme.
#define IPCZ_FEATURE_MEM_V2

// Options given to CreateNode() to configure the new node's behavior.
struct IPCZ_ALIGN(8) IpczCreateNodeOptions {};

// See CreateNode() and the IPCZ_CREATE_NODE_* flag descriptions below.
IpczCreateNodeFlags;

// Indicates that the created node will serve as the broker in its cluster.
//
// Brokers are expected to live in relatively trusted processes -- not
// necessarily elevated in privilege but also generally not restricted by
// sandbox constraints and not prone to processing risky, untrusted data -- as
// they're responsible for helping other nodes establish direct lines of
// communication as well as in some cases relaying data and driver handles
// between lesser-privileged nodes.
//
// Broker nodes do not expose any additional ipcz APIs or require much other
// special care on the part of the application**, but every cluster of connected
// nodes must have a node designated as the broker. Typically this is the first
// node created by an application's main process or a system-wide service
// coordinator, and all other nodes are created in processes spawned by that one
// or in processes which otherwise trust it.
//
// ** See notes on Close() regarding destruction of broker nodes.
#define IPCZ_CREATE_NODE_AS_BROKER

// See ConnectNode() and the IPCZ_CONNECT_NODE_* flag descriptions below.
IpczConnectNodeFlags;

// Indicates that the remote node for this connection is expected to be a broker
// node, and it will be treated as such. Do not use this flag when connecting to
// any untrusted process.
#define IPCZ_CONNECT_NODE_TO_BROKER

// Indicates that the remote node for this connection is expected not to be a
// broker, but to already have a link to a broker; and that the calling node
// wishes to inherit the remote node's broker as well. This flag must only be
// used when connecting to a node the caller trusts. The calling node must not
// already have an established broker from a previous ConnectNode() call. The
// remote node must specify IPCZ_CONNECT_NODE_SHARE_BROKER as well.
#define IPCZ_CONNECT_NODE_INHERIT_BROKER

// Indicates that the calling node already has a broker, and that this broker
// will be inherited by the remote node. The remote node must also specify
// IPCZ_CONNECT_NODE_INHERIT_BROKER in its corresponding ConnectNode() call.
#define IPCZ_CONNECT_NODE_SHARE_BROKER

// ipcz may periodically allocate shared memory regions to facilitate
// communication between two nodes. In many runtime environments, even within
// a security sandbox, the driver can do this safely and directly by interfacing
// with the OS. In some environments however, direct allocation is not possible.
// In such cases a node must delegate this responsibility to some other trusted
// node in the system, typically the broker node.
//
// Specifying this flag ensures that all shared memory allocation elicited by
// the connecting node will be delegated to the connectee.
#define IPCZ_CONNECT_NODE_TO_ALLOCATION_DELEGATE

// An opaque handle to a transaction returned by BeginGet() or BeginPut().
IpczTransaction;

// See BeginPut() and the IPCZ_BEGIN_PUT_* flags described below.
IpczBeginPutFlags;

// Indicates that the caller is willing to produce less data than originally
// requested by their `*num_bytes` argument to BeginPut(). If the implementation
// would prefer a smaller chunk of data, passing this flag may allow the call to
// succeed while returning a smaller acceptable value in `*num_bytes`, rather
// than simply failing the call with IPCZ_RESULT_RESOURCE_EXHAUSTED.
#define IPCZ_BEGIN_PUT_ALLOW_PARTIAL

// See EndPut() and the IPCZ_END_PUT_* flags described below.
IpczEndPutFlags;

// If this flag is given to EndPut(), the referenced transaction is aborted
// without committing its parcel to the portal.
#define IPCZ_END_PUT_ABORT

// See Get() and the IPCZ_GET_* flag descriptions below.
IpczGetFlags;

// When given to Get(), this flag indicates that the caller is willing to accept
// a partial retrieval of the next available parcel. This means that in
// situations where Get() would normally return IPCZ_RESULT_RESOURCE_EXHAUSTED,
// it will instead return IPCZ_RESULT_OK with as much data and handles as the
// caller indicated they could accept.
#define IPCZ_GET_PARTIAL

// See BeginGet() and the IPCZ_BEGIN_GET_* flag descriptions below.
IpczBeginGetFlags;

// Indicates that the caller will accept partial retrieval of a parcel's
// attached handles. When this flag is given handles are only transferred to
// the caller as output capacity allows, and it is not an error for the caller
// to provide insufficient output capacity. See notes on BeginGet().
#define IPCZ_BEGIN_GET_PARTIAL

// Indicates that BeginGet() should begin an "overlapped" get-transaction on its
// source, meaning that additional overlapped get-transactions may begin on the
// same source before this one is terminated. Only valid when the source is a
// portal.
#define IPCZ_BEGIN_GET_OVERLAPPED

// See EndGet() and the IPCZ_END_GET_* flag descriptions below.
IpczEndGetFlags;

// If this flag is given to EndGet() for a non-overlapped transaction on a
// portal, the transaction's parcel is left intact in the portal's queue instead
// of being dequeued. Note that if handles were transferred to the caller via
// BeginGet(), they still remain property of the caller and will no longer be
// attached to the parcel even if the transaction is aborted.
#define IPCZ_END_GET_ABORT

// Enumerates the type of contents within a box.
IpczBoxType;

// A box which contains an opaque driver object.
#define IPCZ_BOX_TYPE_DRIVER_OBJECT

// A box which contains an opaque application-defined object with a custom
// serializer.
#define IPCZ_BOX_TYPE_APPLICATION_OBJECT

// A box which contains a collection of bytes and ipcz handles.
#define IPCZ_BOX_TYPE_SUBPARCEL

// A function passed to Box() when boxing application objects. This function
// implements serialization for the object identified by `object` in a manner
// similar to IpczDriver's Serialize() function. If the object is not
// serializable this must return IPCZ_RESULT_FAILED_PRECONDITION and ignore
// other arguments.
//
// This may be called with insufficient capacity (`num_bytes` and `num_handles`)
// in which case it should update those values with capacity requirements and
// return IPCZ_RESULT_RESOURCE_EXHAUSTED.
//
// Otherwise the function must fill in `bytes` and `handles` with values that
// effectively represent the opaque object identified by `object`, and return
// IPCZ_RESULT_OK to indicate success.
IpczApplicationObjectSerializer;

// A function passed to Box() when boxing application objects. This function
// must clean up any resources associated with the opaque object identified by
// `object`.
IpczApplicationObjectDestructor;

// Describes the contents of a box. Boxes may contain driver objects, arbitrary
// application-defined objects, or collections of bytes and ipcz handles
// (portals or other boxes).
struct IPCZ_ALIGN(8) IpczBoxContents {};

// See Unbox() and the IPCZ_UNBOX_* flags described below.
IpczUnboxFlags;

// If set, the box is not consumed and the driver handle returned is not removed
// from the box.
#define IPCZ_UNBOX_PEEK

// Flags given by the `flags` field in IpczPortalStatus.
IpczPortalStatusFlags;

// Indicates that the opposite portal is closed. Subsequent put-transactions on
// this portal will always fail with IPCZ_RESULT_NOT_FOUND. If there are not
// currently any unretrieved parcels in the portal either, subsequent
// get-transactions will also fail with the same error.
#define IPCZ_PORTAL_STATUS_PEER_CLOSED

// Indicates that the opposite portal is closed AND no more parcels can be
// expected to arrive from it. If this bit is set on a portal's status, the
// portal is essentially useless. Such portals no longer support Put() or
// Get() operations, and those operations will subsequently always return
// IPCZ_RESULT_NOT_FOUND.
#define IPCZ_PORTAL_STATUS_DEAD

// Information returned by QueryPortalStatus() or provided to
// IpczTrapEventHandlers when a trap's conditions are met on a portal.
struct IPCZ_ALIGN(8) IpczPortalStatus {};

// Flags given to IpczTrapConditions to indicate which types of conditions a
// trap should observe.
//
// Note that each type of condition may be considered edge-triggered or
// level-triggered. An edge-triggered condition is one which is only
// observable momentarily in response to a state change, while a level-triggered
// condition is continuously observable as long as some constraint about a
// portal's state is met.
//
// Level-triggered conditions can cause a Trap() attempt to fail if they're
// already satisfied when attempting to install a trap to monitor them.
IpczTrapConditionFlags;

// Triggers a trap event when the trap's portal is itself closed. This condition
// is always observed even if not explicitly set in the IpczTrapConditions given
// to the Trap() call. If a portal is closed while a trap is installed on it,
// an event will fire for the trap with this condition flag set. This condition
// is effectively edge-triggered, because as soon as it becomes true, any
// observing trap as well as its observed subject cease to exist.
#define IPCZ_TRAP_REMOVED

// Triggers a trap event whenever the opposite portal is closed. Typically
// applications are interested in the more specific IPCZ_TRAP_DEAD.
// Level-triggered.
#define IPCZ_TRAP_PEER_CLOSED

// Triggers a trap event whenever there are no more parcels available to
// retrieve from this portal AND the opposite portal is closed. This means the
// portal will never again have parcels to retrieve and is effectively useless.
// Level-triggered.
#define IPCZ_TRAP_DEAD

// Triggers a trap event whenever the number of parcels queued for retrieval by
// this portal exceeds the threshold given by `min_local_parcels` in
// IpczTrapConditions. Level-triggered.
#define IPCZ_TRAP_ABOVE_MIN_LOCAL_PARCELS

// Triggers a trap event whenever the number of bytes queued for retrieval by
// this portal exceeds the threshold given by `min_local_bytes` in
// IpczTrapConditions. Level-triggered.
#define IPCZ_TRAP_ABOVE_MIN_LOCAL_BYTES

// Triggers a trap event whenever the number of locally available parcels
// increases by any amount. Edge-triggered.
#define IPCZ_TRAP_NEW_LOCAL_PARCEL

// Indicates that the trap event is being fired from within the extent of an
// ipcz API call (i.e., as opposed to being fired from within the extent of an
// incoming driver transport notification.) For example if a trap is monitoring
// a portal for incoming parcels, and the application puts a parcel into the
// portal's peer on the same node, the trap event will be fired within the
// extent of the corresponding Put() call, and this flag will be set on the
// event.
//
// This flag is ignored when specifying conditions to watch for Trap(), and it
// may be set on any event dispatched to an IpczTrapEventHandler.
#define IPCZ_TRAP_WITHIN_API_CALL

// A structure describing portal conditions necessary to trigger a trap and
// invoke its event handler.
struct IPCZ_ALIGN(8) IpczTrapConditions {};

// Structure passed to each IpczTrapEventHandler invocation with details about
// the event.
struct IPCZ_ALIGN(8) IpczTrapEvent {};

// An application-defined function to be invoked by a trap when its observed
// conditions are satisfied on the monitored portal.
IpczTrapEventHandler;

#if defined(__cplusplus)
extern "C" {
#endif

// IpczAPI
// =======
//
// Table of API functions defined by ipcz. Instances of this structure may be
// populated by passing them to an implementation of IpczGetAPIFn.
//
// Note that all functions follow a consistent parameter ordering:
//
//   1. Object handle (node or portal) if applicable
//   2. Function-specific strict input values
//   3. Flags - possibly untyped and unused
//   4. Options struct - possibly untyped and unused
//   5. Function-specific in/out values
//   6. Function-specific strict output values
//
// The rationale behind this convention is generally to have order flow from
// input to output. Flags are inputs, and options provide an extension point for
// future versions of these APIs; as such they skirt the boundary between strict
// input values and in/out values.
//
// The order and signature (ABI) of functions defined here must never change,
// but new functions may be added to the end.
struct IPCZ_ALIGN(8) IpczAPI {};

// A function which populates `api` with a table of ipcz API functions. The
// `size` field must be set by the caller to the size of the structure before
// issuing this call.
//
// In practice ipcz defines IpczGetAPI() as an implementation of this function
// type. How applications acquire a reference to that function depends on how
// the application builds and links against ipcz.
//
// Upon return, `api->size` indicates the size of the function table actually
// populated and therefore which version of the ipcz implementation is in use.
// Note that this size will never exceed the input value of `api->size`: if the
// caller is built against an older version than what is available, the
// available implementation will only populate the functions appropriate for
// that older version. Conversely if the caller is built against a newer version
// than what is available, `api->size` on output may be smaller than its value
// was on input.
//
// Returns:
//
//    IPCZ_RESULT_OK if `api` was successfully populated. In this case
//       `api->size` effectively indicates the API version provided, and the
//       appropriate function pointers within `api` are filled in.
//
//    IPCZ_RESULT_INVALID_ARGUMENT if `api` is null or the caller's provided
//       `api->size` is less than the size of the function table required to
//       host API version 0.
IpczGetAPIFn;

#if defined(__cplusplus)
}  // extern "C"
#endif

#endif  // IPCZ_INCLUDE_IPCZ_IPCZ_H_