Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/CODEOWNERS
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@
/internal-api/src/*/*/datadog/trace/api/env/ @DataDog/apm-java
/internal-api/src/*/*/datadog/trace/api/flare/ @DataDog/apm-java
/internal-api/src/*/*/datadog/trace/api/function/ @DataDog/apm-java
/internal-api/src/*/*/datadog/contract/ @DataDog/apm-java
/internal-api/src/*/*/datadog/perfcontract/ @DataDog/apm-java
/internal-api/src/*/*/datadog/trace/api/intake/ @DataDog/apm-java
/internal-api/src/*/*/datadog/trace/api/logging/ @DataDog/apm-java
Expand Down
63 changes: 63 additions & 0 deletions internal-api/src/main/java/datadog/contract/Confined.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package datadog.contract;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
* Declares where an instance of the annotated type remains valid. Using an instance outside its
* {@link Extent} is a bug: the type relies on that confinement for its own correctness, for example
* by completing through an unsynchronized flag or by carrying state that is only meaningful during
* one call.
*
* <p>Extents are ordered from narrowest to widest: {@link Extent#CALL}, then {@link Extent#FRAME}.
* Both imply the instance is never used from another thread. An unannotated type is unconfined.
*
* <p>This marker changes no runtime behavior. The checker contract below guides AI review; no
* static checker currently enforces it. As a {@link CorrectnessContract}, it has no exemption.
*
* <p>Compare {@link datadog.trace.api.function.NoEscape}, which says an instance <i>should</i> not
* be retained, for performance. A confined instance must not be retained.
*
* <p><b>Checker contract.</b> Applies to production source sets. Tests and benchmarks that misuse a
* type deliberately, to verify its behavior, are outside the rule.
*
* <ul>
* <li><b>Violation, every extent:</b> storing an instance in a field or collection, or capturing
* it in a lambda or anonymous class.
* <li><b>Violation, {@code CALL}:</b> returning an instance, except from the method that creates
* it.
* <li><b>Violation, {@code FRAME} on an {@link AutoCloseable} type:</b> obtaining an instance
* anywhere but as a try-with-resources resource, unless the obtaining method returns it. The
* caller it is returned to inherits the obligation.
* <li><b>Accepted:</b> local variables, synchronous calls that receive the instance as an
* argument, a creating method returning its new instance, and returning a {@code FRAME}
* instance to the caller.
* <li><b>Out of scope (v1):</b> completion of a {@code FRAME} instance that is not {@link
* AutoCloseable}, escape through raw containers, and values stored several calls after they
* were obtained. Widen this contract only when a real case shows it is insufficient.
* </ul>
*/
@Documented
@CorrectnessContract
@Retention(RetentionPolicy.CLASS)
@Target(ElementType.TYPE)
public @interface Confined {

/** The extent instances of the annotated type are confined to. */
Extent value();

/** Where a confined instance remains valid, from narrowest to widest. */
enum Extent {
/** Valid only for the duration of the call it was passed into. */
CALL,

/**
* Valid only in the frame that obtained it and the calls that frame makes, and completed on
* every path before that frame exits.
*/
FRAME
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package datadog.contract;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
* Marks an annotation as a correctness contract for documentation and static analysis. A
* correctness contract states a rule that code must follow, in the RFC 2119 sense: a violation is a
* bug, not a tradeoff. Contract annotations change no runtime behavior.
*
* <p>Unlike {@link datadog.perfcontract.PerfContract}, a correctness contract has no suppression. A
* declaration that needs to break the rule shows that the contract is wrong for its type; change
* the annotation rather than exempting the use. A {@link datadog.perfcontract.SuppressPerfContract}
* that names a correctness contract is invalid, since every class in its {@code value} must be a
* perf contract.
*
* <p>Tools use this meta-annotation to discover contracts. This annotation defines no rule itself.
*
* <p>{@link RetentionPolicy#CLASS} lets tools discover contracts in dependency class files without
* exposing them through runtime reflection.
*/
@Documented
@Retention(RetentionPolicy.CLASS)
@Target(ElementType.ANNOTATION_TYPE)
public @interface CorrectnessContract {}
Original file line number Diff line number Diff line change
Expand Up @@ -375,7 +375,7 @@ public void fill(T element) {

@Override
public void close() {
// Reservation confines fill and close to the reserving thread, so a plain flag suffices.
// Reservation confines fill and close to the reserving frame, so a plain flag suffices.
if (!done) {
done = true;
queue.releasePlace();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
package datadog.common.queue;

import static datadog.contract.Confined.Extent.FRAME;

import datadog.contract.Confined;

/**
* A claimed place in a {@link WorkQueue}, for a caller whose work between claiming and filling
* cannot be expressed as a {@link Producer}.
Expand All @@ -10,9 +14,10 @@
* quietly and permanently, which is why this is an {@link AutoCloseable} meant for
* try-with-resources.
*
* <p>A reservation is confined to the thread that reserved it: only that thread may fill or close
* it. Completion is not synchronized, so filling or closing from another thread can store two
* elements against one place, or store an element whose place was already given back.
* <p>A reservation is confined to the frame that reserved it: fill or close it there, in a
* try-with-resources, and never from another thread. Completion is not synchronized, so filling or
* closing from another thread can store two elements against one place, or store an element whose
* place was already given back.
*
* <p>A refused claim is a reservation too, rather than a {@code null}, and one that quietly
* discards whatever is filled into it. Nothing about the failed path throws, so the shortest
Expand All @@ -35,6 +40,7 @@
* }
* }</pre>
*/
@Confined(FRAME)
public interface Reservation<T> extends AutoCloseable {

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,18 @@
package datadog.common.queue;

import static datadog.contract.Confined.Extent.CALL;

import datadog.contract.Confined;
import javax.annotation.Nonnull;

/**
* The capability to resubmit work after a consumer failure.
*
* <p>Only obtainable inside {@link RetryStrategy#onFailure}, never from a plain consumer, so
* re-enqueue-after-failure stays visibly distinct from ordinary admission.
* re-enqueue-after-failure stays visibly distinct from ordinary admission. Valid only during that
* call: it carries the failed item's attempt count, which a later or different use would misreport.
*/
@Confined(CALL)
public interface RetryQueue<T> {
/**
* Resubmits the failed item.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -144,8 +144,8 @@ <E, C> int tryPutBatch(
*
* <p>The reservation holds no position in the backing. Its element joins the queue when filled,
* so an open reservation does not block consumers. Use try-with-resources to release unfilled
* reservations; abandoning one permanently consumes its capacity. Keep the reservation on the
* reserving thread.
* reservations; abandoning one permanently consumes its capacity. Keep the reservation in the
* reserving frame.
*
* <p>A refused claim returns a non-null reservation that ignores fill. Check {@link
* Reservation#granted()} before building the element.
Expand Down
Loading