Repository: open-telemetry/opentelemetry-java
File: sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/internal/state/DefaultSynchronousMetricStorage.java (Java)

Code before:
          Level.FINE,
          "Instrument "
              + metricDescriptor.getSourceInstrument().getName()
              + " has recorded measurement Not-a-Number (NaN) value with attributes "
              + attributes
              + ". Dropping measurement.");
      return;
    }
    AggregatorHolder<T, U> aggregatorHolder = getHolderForRecord();
    try {
      AggregatorHandle<T, U> handle = getAggregatorHandle(aggregatorHolder, attributes, context);
      handle.recordDouble(value, attributes, context);
    } finally {
      releaseHolderForRecord(aggregatorHolder);
    }
  }

  @Override
  public void setEnabled(boolean enabled) {
    if (enabled) {
      AggregatorHolder<T, U> localAggregatorHolder = Objects.requireNonNull(aggregatorHolder.get());
      if (localAggregatorHolder.aggregator == Aggregator.drop()) {
        AggregatorHolder<T, U> newHolder = new AggregatorHolder<>(originalAggregator);
        previousCollectionAggregatorHandles.clear();
        // If this fails, another thread called `setEnabled` and we can discard the current call
        aggregatorHolder.compareAndSet(localAggregatorHolder, newHolder);
      }
    } else {
      aggregatorHolder.set(new AggregatorHolder<>(Aggregator.drop()));
    }
  }

  @Override
  public boolean isEnabled() {
    return Objects.requireNonNull(this.aggregatorHolder.get()).aggregator != Aggregator.drop();
  }

  /**
   * Obtain the AggregatorHolder for recording measurements, re-reading the volatile
   * this.aggregatorHolder until we access one where recordsInProgress is even. Collect sets
   * recordsInProgress to odd as a signal that AggregatorHolder is stale and is being replaced.
   * Record operations increment recordInProgress by 2. Callers MUST call {@link
   * #releaseHolderForRecord(AggregatorHolder)} when record operation completes to signal to that
   * its safe to proceed with Collect operations.
   */
  private AggregatorHolder<T, U> getHolderForRecord() {
    do {
      AggregatorHolder<T, U> localAggregatorHolder =
          Objects.requireNonNull(this.aggregatorHolder.get());
      int recordsInProgress = localAggregatorHolder.activeRecordingThreads.addAndGet(2);
      if (recordsInProgress % 2 == 0) {

Review comment (bug):
```suggestion
        // If this fails, another thread called `setEnabled` and we can discard the current call
        if (aggregatorHolder.compareAndSet(localAggregatorHolder, newHolder)) {
                previousCollectionAggregatorHandles.clear();
        }
```

Code after:
  }

  @Override
  public void recordDouble(double value, Attributes attributes, Context context) {
    if (!enabled) {
      return;
    }
    if (Double.isNaN(value)) {
      logger.log(
          Level.FINE,
          "Instrument "
              + metricDescriptor.getSourceInstrument().getName()
              + " has recorded measurement Not-a-Number (NaN) value with attributes "
              + attributes
              + ". Dropping measurement.");
      return;
    }
    AggregatorHolder<T, U> aggregatorHolder = getHolderForRecord();
    try {
      AggregatorHandle<T, U> handle =
          getAggregatorHandle(aggregatorHolder.aggregatorHandles, attributes, context);
      handle.recordDouble(value, attributes, context);
    } finally {
      releaseHolderForRecord(aggregatorHolder);
    }
  }

  @Override
  public void setEnabled(boolean enabled) {
    this.enabled = enabled;
  }

  @Override
  public boolean isEnabled() {
    return enabled;
  }

  /**
   * Obtain the AggregatorHolder for recording measurements, re-reading the volatile
   * this.aggregatorHolder until we access one where recordsInProgress is even. Collect sets
   * recordsInProgress to odd as a signal that AggregatorHolder is stale and is being replaced.
   * Record operations increment recordInProgress by 2. Callers MUST call {@link
   * #releaseHolderForRecord(AggregatorHolder)} when record operation completes to signal to that
   * its safe to proceed with Collect operations.
   */
  private AggregatorHolder<T, U> getHolderForRecord() {
    do {
      AggregatorHolder<T, U> aggregatorHolder = this.aggregatorHolder;
      int recordsInProgress = aggregatorHolder.activeRecordingThreads.addAndGet(2);
      if (recordsInProgress % 2 == 0) {
        return aggregatorHolder;