| // Copyright 2018 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| library fuchsia.sysmem; |
| |
| using zx; |
| |
| const MAX_COUNT_DUPLICATES uint32 = 64; |
| |
| /// A BufferCollectionToken is not a BufferCollection, but rather a way to |
| /// identify a potential shared BufferCollection prior to the BufferCollection |
| /// being allocated. |
| /// |
| /// We use a channel for the BufferCollectionToken instead of a single eventpair |
| /// (pair) because this way we can detect error conditions like a participant |
| /// dying mid-create. |
| protocol BufferCollectionToken { |
| /// This method can be used to add more participants prior to creating a |
| /// shared BufferCollection. A new token will be returned for each entry in |
| /// the `rights_attenuation_masks` array. The return value is the client |
| /// ends of each new participant token. |
| /// |
| /// In contrast to Duplicate(), no Sync() is needed after calling this |
| /// method. |
| /// |
| /// All tokens must be turned in via BindSharedCollection() or Close() for a |
| /// BufferCollection to be successfully created. |
| /// |
| /// In each entry of `rights_attenuation_masks`, rights bits that are zero |
| /// will be absent in the buffer VMO rights obtainable via the corresponding |
| /// returned token. This allows an initiator or intermediary participant to |
| /// attenuate the rights available to a participant. This does not allow a |
| /// participant to gain rights that the participant doesn't already have. |
| /// The value ZX_RIGHT_SAME_RIGHTS can be used to specify that no |
| /// attenuation should be applied. |
| DuplicateSync(struct { |
| rights_attenuation_masks vector<zx.rights>:MAX_COUNT_DUPLICATES; |
| }) -> (resource struct { |
| tokens vector<client_end:BufferCollectionToken>:MAX_COUNT_DUPLICATES; |
| }); |
| |
| /// This method can be used to add a participant prior to creating a shared |
| /// BufferCollection. It should only be used instead of DuplicateSync in |
| /// performance sensitive cases where it would be undesireable to wait for |
| /// sysmem to respond as part of each duplicate. |
| /// |
| /// After sending one or more Duplicate() messages, and before sending the |
| /// created tokens to other participants (or to other Allocator channels), |
| /// the client should send a Sync() and wait for its response. The Sync() |
| /// call can be made on the token, or on the BufferCollection obtained by |
| /// passing this token to BindSharedCollection(). Either will ensure that |
| /// the server knows about the tokens created via Duplicate() before the |
| /// other participant sends the token to the server via separate Allocator |
| /// channel. |
| /// |
| /// All tokens must be turned in via BindSharedCollection() or Close() for a |
| /// BufferCollection to be successfully created. |
| /// |
| /// When a client calls BindSharedCollection() to turn in a |
| /// BufferCollectionToken, the server will process all Duplicate() messages |
| /// before closing down the BufferCollectionToken. This allows the client |
| /// to Duplicate() and immediately turn in the BufferCollectionToken using |
| /// BindSharedCollection, then later transfer the client end of token_request |
| /// to another participant - the server will notice the existence of the |
| /// token_request before considering this BufferCollectionToken fully closed. |
| /// |
| /// `rights_attenuation_mask` rights bits that are zero in this mask will be |
| /// absent in the buffer VMO rights obtainable via the client end of |
| /// token_request. This allows an initiator or intermediary participant to |
| /// attenuate the rights available to a participant. This does not allow a |
| /// participant to gain rights that the participant doesn't already have. |
| /// The value ZX_RIGHT_SAME_RIGHTS can be used to specify that no |
| /// attenuation should be applied. |
| /// |
| /// These values for rights_attenuation_mask result in no attenuation: |
| /// * ZX_RIGHT_SAME_RIGHTS (preferred) |
| /// * 0xFFFFFFFF (this is reasonable when an attenuation mask is computed) |
| /// * 0 (deprecated - do not use 0 - an ERROR will go to the log) |
| /// |
| /// `token_request` is the server end of a BufferCollectionToken channel. |
| /// The client end of this channel acts as another participant in creating the |
| /// shared BufferCollection. |
| Duplicate(resource struct { |
| rights_attenuation_mask uint32; |
| token_request server_end:BufferCollectionToken; |
| }); |
| |
| /// Ensure that previous Duplicate() messages have been received server side, |
| /// so that it's safe to send the client end of token_request to another |
| /// participant knowing the server will recognize the token when it's sent |
| /// into BindSharedCollection by the other participant. |
| /// |
| /// Other options include waiting for each Duplicate() to complete |
| /// individually (using separate call to BufferCollectionToken.Sync() after |
| /// each), or calling Sync() on BufferCollection after this token has |
| /// been turned in via BindSharedCollection(). |
| /// |
| /// Calling BufferCollectionToken.Sync() on a token that isn't/wasn't a |
| /// valid sysmem token risks the Sync() hanging forever. See |
| /// ValidateBufferCollectionToken() for one way to mitigate the possibility |
| /// of a hostile/fake BufferCollectionToken at the cost of one round trip. |
| /// |
| /// Another way to mitigate is to avoid calling Sync() on the token, and |
| /// instead later deal with potential failure of BufferCollection.Sync() if |
| /// the original token was invalid. This option can be preferable from a |
| /// performance point of view, but requires client code to delay sending |
| /// tokens duplicated from this token until after client code has converted |
| /// this token to a BufferCollection and received successful response from |
| /// BufferCollection.Sync() (or received OnDuplicatedTokensKnownByServer()). |
| /// |
| /// Prefer using BufferCollection.Sync() instead, when feasible (see above). |
| /// When BufferCollection.Sync() isn't feasible, the caller must already |
| /// know that this token is/was valid, or BufferCollectionToken.Sync() may |
| /// hang forever. See ValidateBufferCollectionToken() to check token |
| /// validity first if the token isn't already known to be (is/was) valid. |
| Sync() -> (); |
| |
| /// Normally a participant will convert the token into a BufferCollection |
| /// view, but a particpant is also free to Close() the token (and then close |
| /// the channel immediately or shortly later in response to server closing |
| /// its end), which avoids causing logical buffer collection failure. |
| /// Normally an unexpected token channel close will cause |
| /// logical buffer collection failure. |
| Close(); |
| |
| /// Set a name for VMOs in this buffer collection. The name may be truncated shorter. The name |
| /// only affects VMOs allocated after it's set - this call does not rename existing VMOs. If |
| /// multiple clients set different names then the larger priority value will win. |
| SetName(struct { |
| priority uint32; |
| name string:64; |
| }); |
| |
| /// Set information about the current client that can be used by sysmem to |
| /// help debug leaking memory and hangs waiting for constraints. |name| can |
| /// be an arbitrary string, but the current process name (see |
| /// fsl::GetCurrentProcessName()) is a good default. |id| can be an arbitrary |
| /// id, but the current process ID (see fsl::GetCurrentProcessKoid()) is a |
| /// good default. |
| SetDebugClientInfo(struct { |
| name string:64; |
| id uint64; |
| }); |
| |
| /// Sysmem logs a warning if not all clients have set constraints 5 seconds |
| /// after creating a collection. Clients can call this method to change |
| /// when the log is printed. If multiple client set the deadline, it's |
| /// unspecified which deadline will take effect. |
| SetDebugTimeoutLogDeadline(struct { |
| deadline zx.time; |
| }); |
| |
| /// A dispensable token can fail after buffers are logically allocated |
| /// without causing failure of its parent (if any). |
| /// |
| /// The dispensable token participates in constraints aggregation along with |
| /// its parent before logical buffer allocation. If the dispensable token |
| /// fails before buffers are logically allocated, the failure propagates to |
| /// the dispensable token's parent. |
| /// |
| /// After buffers are logically allocated, failure of the dispensable token |
| /// (or any child of the dispensable token) does not propagate to the |
| /// dispensable token's parent. Failure does propagate from a normal |
| /// child of a dispensable token to the dispensable token. Failure |
| /// of a child is blocked from reaching its parent if the child is attached, |
| /// or if the child is dispensable and the failure occurred after logical |
| /// allocation. |
| /// |
| /// A dispensable token can be used in cases where a participant needs to |
| /// provide constraints, but after buffers are allocated, the participant |
| /// can fail without causing buffer collection failure from the parent's |
| /// point of view. |
| /// |
| /// In contrast, AttachToken() can be used to create a token which does not |
| /// participate in constraints aggregation with its parent, and whose |
| /// failure at any time does not propagate to its parent, and whose delay |
| /// providing constraints does not prevent the parent from completing its |
| /// buffer allocation. |
| /// |
| /// An initiator may in some scenarios choose to initially use a dispensable |
| /// token for a given instance of a participant, and then later if the first |
| /// instance of that participant fails, a new second instance of that |
| /// participant my be given a token created with AttachToken(). |
| /// |
| /// If a client uses this message, the client should not rely on the |
| /// client's own BufferCollectionToken or BufferCollection channel to close |
| /// from the server end due to abrupt failure of any BufferCollectionToken |
| /// or BufferCollection that the client has SetDispensable() and given out |
| /// to another process. For this reason, the client should take extra care |
| /// to notice failure of that other process via other means. |
| /// |
| /// SetDispensable() on an already-dispensable token is idempotent. |
| SetDispensable(); |
| }; |
| |
| /// BufferCollection is a connection directly from a participant to sysmem re. |
| /// a logical BufferCollection; typically the logical BufferCollection is shared |
| /// with other participants. In other words, an instance of the BufferCollection |
| /// interface is a view of a "logical buffer collection". |
| /// |
| /// This connection exists to facilitate async indication of when the logical |
| /// BufferCollection has been populated with buffers. |
| /// |
| /// Also, the channel's closure by the server is an indication to the client |
| /// that the client should close all VMO handles that were obtained from the |
| /// BufferCollection ASAP. |
| /// |
| /// Also, this interface may in future allow specifying constraints in other |
| /// ways, and may allow for back-and-forth negotiation of constraints to some |
| /// degree. |
| /// |
| /// This interface may in future allow for more than 64 VMO handles per |
| /// BufferCollection, but currently the limit is 64. |
| /// |
| /// This interface may in future allow for allocating/deallocating single |
| /// buffers. |
| /// |
| /// Some initiators may wait a short duration until all old logical |
| /// BufferCollection VMO handles have closed (or until the short duration times |
| /// out) before allocating a new BufferCollection, to help control physical |
| /// memory fragmentation and avoid overlap of buffer allocation lifetimes for |
| /// the old and new collections. Collections can be large enough that it's worth |
| /// avoiding allocation overlap (in time). |
| @for_deprecated_c_bindings |
| protocol BufferCollection { |
| /// See comments on BufferCollectionToken::Sync(). |
| Sync() -> (); |
| |
| /// Provide BufferCollectionConstraints to the logical BufferCollection. |
| /// |
| /// A participant may only call SetConstraints() once. |
| /// |
| /// Sometimes the initiator is a participant only in the sense of wanting to |
| /// keep an eye on success/failure to populate with buffers, and zx.status on |
| /// failure. In that case, `has_constraints` can be false, and `constraints` |
| /// will be ignored. |
| /// |
| /// VMO handles will not be provided to the client that sends null |
| /// constraints - that can be intentional for an initiator that doesn't need |
| /// VMO handles. Not having VMO handles doesn't prevent the initator from |
| /// adjusting which portion of a buffer is considered valid and similar, but |
| /// the initiator can't hold a VMO handle open to prevent the logical |
| /// BufferCollection from cleaning up if the logical BufferCollection needs |
| /// to go away regardless of the initiator's degree of involvement for |
| /// whatever reason. |
| /// |
| /// For population of buffers to be attempted, all holders of a |
| /// BufferCollection client channel need to call SetConstraints() before |
| /// sysmem will attempt to allocate buffers. |
| /// |
| /// `has_constraints` if false, the constraints are effectively null, and |
| /// `constraints` are ignored. The sender of null constraints won't get any |
| /// VMO handles in BufferCollectionInfo, but can still find out how many |
| /// buffers were allocated and can still refer to buffers by their |
| /// buffer_index. |
| /// |
| /// `constraints` are constraints on the buffer collection. |
| SetConstraints(struct { |
| has_constraints bool; |
| constraints BufferCollectionConstraints; |
| }); |
| |
| /// This request completes when buffers have been allocated, responds with |
| /// some failure detail if allocation has been attempted but failed. |
| /// |
| /// The following must occur before buffers will be allocated: |
| /// * All BufferCollectionToken(s) of the logical BufferCollectionToken |
| /// must be turned in via BindSharedCollection(). |
| /// * All BufferCollection(s) of the logical BufferCollection must have had |
| /// SetConstraints() sent to them. |
| /// |
| /// Returns `ZX_OK` if successful. |
| /// Returns `ZX_ERR_NO_MEMORY` if the request is valid but cannot be |
| /// fulfilled due to resource exhaustion. |
| /// Returns `ZX_ERR_ACCESS_DENIED` if the caller is not permitted to |
| /// obtain the buffers it requested. |
| /// Returns `ZX_ERR_INVALID_ARGS` if the request is malformed. |
| /// Returns `ZX_ERR_NOT_SUPPORTED` if request is valid but cannot be |
| /// satisfied, perhaps due to hardware limitations. |
| /// |
| /// `buffer_collection_info` has the VMO handles and other related info. |
| WaitForBuffersAllocated() -> (resource struct { |
| status zx.status; |
| buffer_collection_info BufferCollectionInfo_2; |
| }); |
| |
| /// This returns the same result code as WaitForBuffersAllocated if the |
| /// buffer collection has been allocated or failed, or `ZX_ERR_UNAVAILABLE` |
| /// if WaitForBuffersAllocated would block. |
| CheckBuffersAllocated() -> (struct { |
| status zx.status; |
| }); |
| |
| /// The CloseBuffer() doesn't immediately force all VMO handles to that |
| /// buffer to close, but it does close any handle held by sysmem, and does |
| /// notify all participants of the desire to close the buffer at which point |
| /// each participant that's listening may close their handle to the buffer. |
| /// |
| /// Only a particpant with write can do this. Coordination among multiple |
| /// participants with write is outside of the scope of this interface. |
| /// |
| /// `buffer_index` indicates which buffer to close. If the buffer is already |
| /// closed this has no effect (idempotent). |
| CloseSingleBuffer(struct { |
| buffer_index uint64; |
| }); |
| |
| /// This allocates a new buffer that is consistent with the most recent call |
| /// to SetConstraints(), if possible. If not possible, this indicates the |
| /// failure via OnNewBufferAllocated(). |
| /// |
| /// Only a participant with write can do this. Coordination among multiple |
| /// participants with write is outside the scope of this interface. |
| /// |
| /// The participant is (intentionally) never informed of other participant's |
| /// constraints. |
| AllocateSingleBuffer(struct { |
| buffer_index uint64; |
| }); |
| |
| /// Completes when AllocateBuffer is done. Callers who wish to avoid |
| /// blocking a thread while waiting can use OnAllocateSingleBufferDone() |
| /// instead. |
| WaitForSingleBufferAllocated(struct { |
| buffer_index uint64; |
| }) -> (resource struct { |
| status zx.status; |
| buffer_info SingleBufferInfo; |
| }); |
| |
| /// A participant can use this message to have sysmem verify that this |
| /// buffer_index exists. This message is intentionally ignored by the |
| /// server if the buffer_index _does_ exist. In that case, the client will |
| /// see OnAllocateSingleBufferDone() soon with status == `ZX_OK` (if the |
| /// client hasn't already seen that message). If on the other hand the |
| /// buffer_index does not exist, this message causes the server to send |
| /// OnAllocateSingleBufferDone() with status == `ZX_ERR_NOT_FOUND`. A |
| /// particpant will typically use this when the participant receives a new |
| /// buffer_index that the participant doesn't yet know about, to ensure that |
| /// the participant won't be waiting forever for the |
| /// OnAllocateSingleBufferDone() message regarding this buffer_index. |
| CheckSingleBufferAllocated(struct { |
| buffer_index uint64; |
| }); |
| |
| /// The server handles unexpected failure of a BufferCollection by failing |
| /// the whole logical buffer collection. Partly this is to expedite closing |
| /// VMO handles. If a participant would like to cleanly close a |
| /// BufferCollection view without causing logical buffer collection failure, |
| /// the participant can send Close() before closing the client end of the |
| /// BufferCollection channel. If this is the last BufferCollection view, the |
| /// logical buffer collection will still go away. |
| Close(); |
| |
| /// Set a name for VMOs in this buffer collection. The name may be truncated shorter. The name |
| /// only affects VMOs allocated after it's set - this call does not rename existing VMOs. If |
| /// multiple clients set different names then the larger priority value will win. |
| SetName(struct { |
| priority uint32; |
| name string:64; |
| }); |
| |
| /// See BufferCollectionToken.SetClientDebugInfo. |
| SetDebugClientInfo(struct { |
| name string:64; |
| id uint64; |
| }); |
| |
| /// Optionally sent before SetConstraints() to set constraints related to |
| /// clear (not encrypted, not in protected/secure memory) aux buffers. This |
| /// is only valid when sent before SetConstraints(). Invalid settings may |
| /// not result in channel closure until SetConstraints() is received by |
| /// sysmem. |
| SetConstraintsAuxBuffers(struct { |
| constraints BufferCollectionConstraintsAuxBuffers; |
| }); |
| |
| /// Allows getting any aux buffers allocated after using |
| /// SetConstraintsAuxBuffers(). |
| /// |
| /// Sending this message is not permitted until WaitForBuffersAllocated() completes. |
| /// |
| /// On failure, status will be a failing status and |
| /// buffer_collection_info_aux_buffers won't be filled out, and won't have |
| /// any handles. |
| /// |
| /// On success, status will be ZX_OK, and buffer_count will be the same as |
| /// the main buffer_count from WaitForBuffersAllocated(). |
| /// |
| /// If a participant specified "allow_clear_aux_buffers_for_secure" true but |
| /// "need_clear_aux_buffers_for_secure" false (or not set), the participant |
| /// can determine if aux buffers were allocated by looking at buffer[0].vmo. |
| /// If buffer 0 has no vmo, aux VMOs were not allocated. The resulting |
| /// status will still be ZX_OK in this case, and buffer_count will still be |
| /// filled out to match the main buffer_count. |
| /// |
| /// It's legal for a participant that set |
| /// "allow_clear_aux_buffers_for_secure" false to call GetAuxBuffers(), in |
| /// which case buffer[0].vmo will not be set. |
| GetAuxBuffers() -> (resource struct { |
| status zx.status; |
| buffer_collection_info_aux_buffers BufferCollectionInfo_2; |
| }); |
| |
| /// Create a new token, for trying to add a new participant to an existing |
| /// collection, if the existing collection's buffer counts, constraints, |
| /// and participants allow. |
| /// |
| /// This can be useful in replacing a failed participant, and/or in |
| /// adding/re-adding a participant after buffers have already been |
| /// allocated. |
| /// |
| /// Failure of an attached token / collection does not propagate to the |
| /// parent of the attached token. Failure does propagate from a normal |
| /// child of a dispensable token to the dispensable token. Failure |
| /// of a child is blocked from reaching its parent if the child is attached, |
| /// or if the child is dispensable and the failure occurred after logical |
| /// allocation. |
| /// |
| /// An initiator may in some scenarios choose to initially use a dispensable |
| /// token for a given instance of a participant, and then later if the first |
| /// instance of that participant fails, a new second instance of that |
| /// participant my be given a token created with AttachToken(). |
| /// |
| /// From the point of view of the client end of the BufferCollectionToken |
| /// channel, the token acts like any other token. The client can |
| /// Duplicate() the token as needed, and can send the token to a different |
| /// process. The token should be converted to a BufferCollection channel |
| /// as normal by calling BindSharedCollection(). SetConstraints() should |
| /// be called on that BufferCollection channel. |
| /// |
| /// A success result from WaitForBuffersAllocated() means the new |
| /// participant's constraints were satisfiable using the already-existing |
| /// buffer collection, the already-established BufferCollectionInfo |
| /// including image format constraints, and the already-existing other |
| /// participants and their buffer counts. A failure result means the new |
| /// participant's constraints cannot be satisfied using the existing |
| /// buffer collection and its already-logically-allocated participants. |
| /// Creating a new collection instead may allow all participant's |
| /// constraints to be satisfied, assuming SetDispensable() is used in place |
| /// of AttachToken(), or a normal token is used. |
| /// |
| /// A token created with AttachToken() performs constraints aggregation with |
| /// all constraints currently in effect on the buffer collection, plus the |
| /// attached token under consideration plus child tokens under the attached |
| /// token which are not themselves an attached token or under such a token. |
| /// |
| /// Allocation of buffer_count to min_buffer_count_for_camping etc is |
| /// first-come first-served, but a child can't logically allocate before |
| /// all its parents have sent SetConstraints(). |
| /// |
| /// See also SetDispensable(), which in contrast to AttachToken(), has the |
| /// created token + children participate in constraints aggregation along |
| /// with its parent. |
| /// |
| /// The newly created token needs to be Sync()ed to sysmem before the new |
| /// token can be passed to BindSharedCollection(). The Sync() of the new |
| /// token can be accomplished with BufferCollection.Sync() on this |
| /// BufferCollection. Alternately BufferCollectionToken.Sync() on the new |
| /// token also works. A BufferCollectionToken.Sync() can be started after |
| /// any BufferCollectionToken.Duplicate() messages have been sent via the |
| /// newly created token, to also sync those additional tokens to sysmem |
| /// using a single round-trip. |
| /// |
| /// These values for rights_attenuation_mask result in no attenuation (note |
| /// that 0 is not on this list; 0 will output an ERROR to the system log |
| /// to help diagnose the bug in client code): |
| /// * ZX_RIGHT_SAME_RIGHTS (preferred) |
| /// * 0xFFFFFFFF (this is reasonable when an attenuation mask is computed) |
| AttachToken(resource struct { |
| rights_attenuation_mask uint32; |
| token_request server_end:BufferCollectionToken; |
| }); |
| |
| /// AttachLifetimeTracking: |
| /// |
| /// AttachLifetimeTracking() is intended to allow a client to wait until an |
| /// old logical buffer collection is fully or mostly deallocated before |
| /// attempting allocation of a new logical buffer collection. |
| /// |
| /// Attach an eventpair endpoint to the logical buffer collection, so that |
| /// the server_end will be closed when the number of buffers allocated |
| /// drops to 'buffers_remaining'. The server_end won't close until after |
| /// logical allocation has completed. |
| /// |
| /// If logical allocation fails, such as for an attached sub-tree (using |
| /// AttachToken()), the server_end will close during that failure regardless |
| /// of the number of buffers potenitally allocated in the overall logical |
| /// buffer collection. |
| /// |
| /// Multiple eventpair endpoints can be attached, with an enforced limit of |
| /// SYSMEM_LIFETIME_TRACKING_EVENTPAIR_PER_BUFFER_COLLECTION_CHANNEL_MAX. |
| /// |
| /// The lifetime signalled by this event includes asynchronous cleanup of |
| /// allocated buffers, and this asynchronous cleanup cannot occur until all |
| /// holders of VMO handles to the buffers have closed those VMO handles. |
| /// Therefore clients should take care not to become blocked forever waiting |
| /// for ZX_EVENTPAIR_PEER_CLOSED to be signalled, especially if any of the |
| /// participants using the logical buffer collection are less trusted or |
| /// less reliable. |
| /// |
| /// The buffers_remaining parameter allows waiting for all but |
| /// buffers_remaining buffers to be fully deallocated. This can be useful |
| /// in situations where a known number of buffers are intentionally not |
| /// closed so that the data can continue to be used, such as for keeping the |
| /// last available video picture displayed in the UI even if the video |
| /// stream was using protected output buffers. It's outside the scope of |
| /// the BufferCollection interface (at least for now) to determine how many |
| /// buffers may be held without closing, but it'll typically be in the range |
| /// 0-2. |
| /// |
| /// This mechanism is meant to be compatible with other protocols providing |
| /// a similar AttachLifetimeTracking() mechanism, in that duplicates of the |
| /// same event can be sent to more than one AttachLifetimeTracking(), and |
| /// the ZX_EVENTPAIR_PEER_CLOSED will be signalled when all the lifetime |
| /// over conditions are met (all holders of duplicates have closed their |
| /// handle(s)). |
| /// |
| /// A maximum of |
| /// SYSMEM_LIFETIME_TRACKING_EVENTPAIR_PER_BUFFER_COLLECTION_CHANNEL_MAX |
| /// AttachLifetimeTracking() messages are allowed per BufferCollection |
| /// channel. |
| /// |
| /// There is no way to cancel an attach. Closing the client end of the |
| /// eventpair doesn't subtract from the number of pending attach(es). |
| /// |
| /// Closing the client's end doesn't result in any action by the server. |
| /// If the server listens to events from the client end at all, it is for |
| /// debug logging only. |
| /// |
| /// The server intentionally doesn't "trust" any bits signalled by the |
| /// client. This mechanism intentionally uses only ZX_EVENTPAIR_PEER_CLOSED |
| /// which can't be triggered early, and is only triggered when all handles |
| /// to server_end are closed. No meaning is associated with any of the |
| /// other signal bits, and clients should functionally ignore any other |
| /// signal bits on either end of the eventpair or its peer. |
| /// |
| /// The server_end may lack ZX_RIGHT_SIGNAL or ZX_RIGHT_SIGNAL_PEER, but |
| /// must have ZX_RIGHT_DUPLICATE (and must have ZX_RIGHT_TRANSFER to |
| /// transfer without causing CodecFactory channel failure). |
| AttachLifetimeTracking(resource struct { |
| server_end zx.handle:EVENTPAIR; |
| buffers_remaining uint32; |
| }); |
| }; |