910e62b5创建于 1月15日历史提交
// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "mojo/public/cpp/base/shared_memory_version.h"

#include <atomic>
#include <limits>

#include "base/check_op.h"
#include "base/logging.h"
#include "base/memory/read_only_shared_memory_region.h"
#include "base/memory/structured_shared_memory.h"
#include "base/metrics/histogram_functions.h"

namespace mojo {

SharedMemoryVersionController::SharedMemoryVersionController()
    : mapped_region_(base::StructuredSharedMemory<VersionState>::Create()) {
  if (mapped_region_.has_value()) {
    // Clients may use `kInvalidVersion` as a special value to indicate
    // the version in the absence of shared memory communication. Make
    // sure the version starts at `kInitialVersion` to avoid any
    // confusion.
    // Relaxed memory order because no other memory operation depends on the
    // version.
    mapped_region_->WritableRef().version.store(
        shared_memory_version::kInitialVersion, std::memory_order_relaxed);
  }
  base::UmaHistogramBoolean(
      "Mojo.SharedMemoryVersion.SharedMemoryAllocationSucceeded",
      mapped_region_.has_value());
}

SharedMemoryVersionController::~SharedMemoryVersionController() = default;

base::ReadOnlySharedMemoryRegion
SharedMemoryVersionController::GetSharedMemoryRegion() const {
  if (!mapped_region_.has_value()) {
    return base::ReadOnlySharedMemoryRegion();
  }

  return mapped_region_->DuplicateReadOnlyRegion();
}

VersionType SharedMemoryVersionController::GetSharedVersion() const {
  if (!mapped_region_.has_value()) {
    return shared_memory_version::kInvalidVersion;
  }

  // Relaxed memory order because no other memory operation depends on the
  // version.
  return mapped_region_->ReadOnlyRef().version.load(std::memory_order_relaxed);
}

void SharedMemoryVersionController::Increment() {
  if (!mapped_region_.has_value()) {
    return;
  }

  // Relaxed memory order because no other memory operation depends on the
  // version.
  const VersionType version = mapped_region_->WritableRef().version.fetch_add(
      1, std::memory_order_relaxed);

  // The version wrapping around is not supported and should not happen.
  CHECK_LE(version, std::numeric_limits<VersionType>::max());
}

void SharedMemoryVersionController::CommitWrite() {
  if (!mapped_region_.has_value()) {
    return;
  }
  // Relaxed memory order because no other memory operation depends on the
  // counter.
  const CountType count =
      mapped_region_->WritableRef().committed_writes_count.fetch_add(
          1, std::memory_order_relaxed);

  // The version wrapping around is not supported and should not happen.
  CHECK_LE(count, std::numeric_limits<CountType>::max());
}

void SharedMemoryVersionController::SetVersion(VersionType version) {
  if (!mapped_region_.has_value()) {
    return;
  }

  // The version wrapping around is not supported and should not happen.
  CHECK_LT(version, std::numeric_limits<VersionType>::max());

  // Version cannot decrease.
  CHECK_GE(version, GetSharedVersion());

  // Relaxed memory order because no other memory operation depends on the
  // version.
  mapped_region_->WritableRef().version.store(version,
                                              std::memory_order_relaxed);
}

SharedMemoryVersionClient::SharedMemoryVersionClient(
    base::ReadOnlySharedMemoryRegion shared_region)
    : read_only_mapping_(
          base::StructuredSharedMemory<VersionState>::MapReadOnlyRegion(
              std::move(shared_region))) {}

SharedMemoryVersionClient::~SharedMemoryVersionClient() = default;

bool SharedMemoryVersionClient::SharedVersionIsLessThan(
    VersionType version) const {
  // Invalid version numbers cannot be compared. Default to IPC.
  if (version == shared_memory_version::kInvalidVersion) {
    return true;
  }

  return GetSharedVersion() < version;
}

bool SharedMemoryVersionClient::SharedVersionIsGreaterThan(
    VersionType version) const {
  // Invalid version numbers cannot be compared. Default to IPC.
  if (version == shared_memory_version::kInvalidVersion) {
    return true;
  }

  return GetSharedVersion() > version;
}

bool SharedMemoryVersionClient::CommittedWritesIsLessThan(
    CountType count) const {
  if (!read_only_mapping_.has_value()) {
    return true;
  }
  // Relaxed memory order because no other memory operation depends on the
  // version.
  const auto current_count =
      read_only_mapping_->ReadOnlyPtr()->committed_writes_count.load(
          std::memory_order_relaxed);
  return current_count < count;
}

VersionType SharedMemoryVersionClient::GetSharedVersion() const {
  if (!read_only_mapping_.has_value()) {
    return shared_memory_version::kInvalidVersion;
  }
  // Relaxed memory order because no other memory operation depends on the
  // version.
  return read_only_mapping_->ReadOnlyRef().version.load(
      std::memory_order_relaxed);
}

}  // namespace mojo