Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 52ea25cf authored by Corey Tabaka's avatar Corey Tabaka Committed by Jiwen 'Steve' Cai
Browse files

Add shared memory based buffer metadata

This CLs reduces BufferHub CPU consumption by adding asynchronous
state transition so that out-of-process VR composition can run on 2016
pixel devices smoothly. In addition, this CL addresses a couple corner
cases in the existing bufferhub logic, which fixes various blackscreen
issues.

1/ Tracks buffer transition states (gained, posted, acquired, released)
   from the client side via atomic shared memory and adds
   PostAsync/AcquireAsync/ReleaseAsync/GainAsync with metadata  and
   fence support.
2/ Adds dequeue order guarantee for buffers enqueued with
   dvrWriteBufferQueuePostBuffer.
3/ Synchronous BuffeHub operations are still supported.
4/ Bump up the bufferhubd's soft limit of open file descriptor.
5/ Handle orphaned consumer in acquired state. This is a corner case
   that consumer process goes aways (most likely due to a crash) leaving
   buffer stuck in acquired state with inconsistent buffer state.
6/ Fixes a race condition for released buffer to be Gain'ed and
   Acquire'd when a new consumer is created in released state.
7/ Improve silent consumer queue efficiency: Silent queues no longer
   import buffers or receive signals about new buffers and they are
   limited to only spawning other consumers and notifications about
   producers hanging up.
8/ Modify PDX/UDS channel event signaling to work around epoll
   behavior. PDX UDS uses a combination of an eventfd and an epoll set
   to simulate the original PDX transport channel events. An odd
   behavior discovered in the kernel implementation of epoll was found
   that causes the epoll fd to "unsignal" itself whenever epoll_wait()
   is called on it, regardless of whether it should still be
   pending. This breaks the edge triggerd behavior in nested epoll sets
   that channel events depend on. Since this is unlikely to ever be
   fixed in the kernel we work around the behavior by using the epoll
   set only as a logical OR of two eventfds and never calling
   epoll_wait() on it. When polling is required we use regluar poll()
   with the eventfds and data fd to avoid the bad behavior in
   epoll_wait().
9/ Keep reading data after PDX hangup signal. UDS will signal hangup
   when the other end of the socket closes. However, data could still be
   in the kerenl buffer and should be consumed. Fix an issue where the
   service misses an impulse sent right before the socket is closed.

Bug: 65455724
Bug: 65458354
Bug: 65458312
Bug: 64027135
Bug: 67424527
Test: libpdx_uds_tests
      bufferhub_tests
      buffer_hub_queue-test
      buffer_hub_queue_producer-test
      dvr_api-test

Change-Id: Id07db1f206ccf4e06f7ee3c671193334408971ca
parent 35b5114b
Loading
Loading
Loading
Loading
+6 −3
Original line number Diff line number Diff line
@@ -37,7 +37,8 @@ sharedLibraries = [
    "libnativewindow"
]

HeaderLibraries = [
headerLibraries = [
    "libdvr_headers",
    "libnativebase_headers",
]

@@ -45,12 +46,13 @@ cc_library {
    srcs: sourceFiles,
    cflags: [
        "-DLOG_TAG=\"libbufferhub\"",
        "-DTRACE=0"
        "-DTRACE=0",
        "-DATRACE_TAG=ATRACE_TAG_GRAPHICS",
    ],
    export_include_dirs: localIncludeFiles,
    static_libs: staticLibraries,
    shared_libs: sharedLibraries,
    header_libs: HeaderLibraries,
    header_libs: headerLibraries,
    name: "libbufferhub",
    export_header_lib_headers: [
        "libnativebase_headers",
@@ -62,6 +64,7 @@ cc_test {
    srcs: ["bufferhub_tests.cpp"],
    static_libs: ["libbufferhub"] + staticLibraries,
    shared_libs: sharedLibraries,
    header_libs: headerLibraries,
    name: "bufferhub_tests",
}
+385 −43
Original line number Diff line number Diff line
@@ -2,7 +2,7 @@

#include <log/log.h>
#include <poll.h>
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
#include <sys/epoll.h>
#include <utils/Trace.h>

#include <mutex>
@@ -12,9 +12,8 @@

#include "include/private/dvr/bufferhub_rpc.h"

using android::pdx::LocalHandle;
using android::pdx::LocalChannelHandle;
using android::pdx::rpc::WrapBuffer;
using android::pdx::LocalHandle;
using android::pdx::Status;

namespace android {
@@ -29,7 +28,11 @@ BufferHubBuffer::BufferHubBuffer(const std::string& endpoint_path)
          endpoint_path)},
      id_(-1) {}

BufferHubBuffer::~BufferHubBuffer() {}
BufferHubBuffer::~BufferHubBuffer() {
  if (metadata_header_ != nullptr) {
    metadata_buffer_.Unlock();
  }
}

Status<LocalChannelHandle> BufferHubBuffer::CreateConsumer() {
  Status<LocalChannelHandle> status =
@@ -43,7 +46,7 @@ Status<LocalChannelHandle> BufferHubBuffer::CreateConsumer() {
int BufferHubBuffer::ImportBuffer() {
  ATRACE_NAME("BufferHubBuffer::ImportBuffer");

  Status<NativeBufferHandle<LocalHandle>> status =
  Status<BufferDescription<LocalHandle>> status =
      InvokeRemoteMethod<BufferHubRPC::GetBuffer>();
  if (!status) {
    ALOGE("BufferHubBuffer::ImportBuffer: Failed to get buffer: %s",
@@ -54,24 +57,135 @@ int BufferHubBuffer::ImportBuffer() {
    return -EIO;
  }

  auto buffer_handle = status.take();
  auto buffer_desc = status.take();

  // Stash the buffer id to replace the value in id_.
  const int new_id = buffer_handle.id();
  const int new_id = buffer_desc.id();

  // Import the buffer.
  IonBuffer ion_buffer;
  ALOGD_IF(
      TRACE, "BufferHubBuffer::ImportBuffer: id=%d FdCount=%zu IntCount=%zu",
      buffer_handle.id(), buffer_handle.FdCount(), buffer_handle.IntCount());
  ALOGD_IF(TRACE, "BufferHubBuffer::ImportBuffer: id=%d.", buffer_desc.id());

  const int ret = buffer_handle.Import(&ion_buffer);
  if (ret < 0)
  if (const int ret = buffer_desc.ImportBuffer(&ion_buffer))
    return ret;

  // If the import succeeds, replace the previous buffer and id.
  // Import the metadata.
  IonBuffer metadata_buffer;
  if (const int ret = buffer_desc.ImportMetadata(&metadata_buffer)) {
    ALOGE("Failed to import metadata buffer, error=%d", ret);
    return ret;
  }
  size_t metadata_buf_size = metadata_buffer.width();
  if (metadata_buf_size < BufferHubDefs::kMetadataHeaderSize) {
    ALOGE("BufferHubBuffer::ImportBuffer: metadata buffer too small: %zu",
          metadata_buf_size);
    return -ENOMEM;
  }

  // If all imports succee, replace the previous buffer and id.
  buffer_ = std::move(ion_buffer);
  metadata_buffer_ = std::move(metadata_buffer);
  metadata_buf_size_ = metadata_buf_size;
  user_metadata_size_ = metadata_buf_size_ - BufferHubDefs::kMetadataHeaderSize;

  void* metadata_ptr = nullptr;
  if (const int ret =
          metadata_buffer_.Lock(BufferHubDefs::kMetadataUsage, /*x=*/0,
                                /*y=*/0, metadata_buf_size_,
                                /*height=*/1, &metadata_ptr)) {
    ALOGE("BufferHubBuffer::ImportBuffer: Failed to lock metadata.");
    return ret;
  }

  // Set up shared fences.
  shared_acquire_fence_ = buffer_desc.take_acquire_fence();
  shared_release_fence_ = buffer_desc.take_release_fence();
  if (!shared_acquire_fence_ || !shared_release_fence_) {
    ALOGE("BufferHubBuffer::ImportBuffer: Failed to import shared fences.");
    return -EIO;
  }

  metadata_header_ =
      reinterpret_cast<BufferHubDefs::MetadataHeader*>(metadata_ptr);
  if (user_metadata_size_) {
    user_metadata_ptr_ =
        reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(metadata_ptr) +
                                BufferHubDefs::kMetadataHeaderSize);
  } else {
    user_metadata_ptr_ = nullptr;
  }

  id_ = new_id;
  buffer_state_bit_ = buffer_desc.buffer_state_bit();

  // Note that here the buffer state is mapped from shared memory as an atomic
  // object. The std::atomic's constructor will not be called so that the
  // original value stored in the memory region will be preserved.
  buffer_state_ = &metadata_header_->buffer_state;
  ALOGD_IF(TRACE,
           "BufferHubBuffer::ImportBuffer: id=%d, buffer_state=%" PRIx64 ".",
           id(), buffer_state_->load());
  fence_state_ = &metadata_header_->fence_state;
  ALOGD_IF(TRACE,
           "BufferHubBuffer::ImportBuffer: id=%d, fence_state=%" PRIx64 ".",
           id(), fence_state_->load());

  return 0;
}

inline int BufferHubBuffer::CheckMetadata(size_t user_metadata_size) const {
  if (user_metadata_size && !user_metadata_ptr_) {
    ALOGE("BufferHubBuffer::CheckMetadata: doesn't support custom metadata.");
    return -EINVAL;
  }
  if (user_metadata_size > user_metadata_size_) {
    ALOGE("BufferHubBuffer::CheckMetadata: too big: %zu, maximum: %zu.",
          user_metadata_size, user_metadata_size_);
    return -E2BIG;
  }
  return 0;
}

int BufferHubBuffer::UpdateSharedFence(const LocalHandle& new_fence,
                                       const LocalHandle& shared_fence) {
  if (pending_fence_fd_.Get() != new_fence.Get()) {
    // First, replace the old fd if there was already one. Skipping if the new
    // one is the same as the old.
    if (pending_fence_fd_.IsValid()) {
      const int ret = epoll_ctl(shared_fence.Get(), EPOLL_CTL_DEL,
                                pending_fence_fd_.Get(), nullptr);
      ALOGW_IF(ret,
               "BufferHubBuffer::UpdateSharedFence: failed to remove old fence "
               "fd from epoll set, error: %s.",
               strerror(errno));
    }

    if (new_fence.IsValid()) {
      // If ready fence is valid, we put that into the epoll set.
      epoll_event event;
      event.events = EPOLLIN;
      event.data.u64 = buffer_state_bit();
      pending_fence_fd_ = new_fence.Duplicate();
      if (epoll_ctl(shared_fence.Get(), EPOLL_CTL_ADD, pending_fence_fd_.Get(),
                    &event) < 0) {
        const int error = errno;
        ALOGE(
            "BufferHubBuffer::UpdateSharedFence: failed to add new fence fd "
            "into epoll set, error: %s.",
            strerror(error));
        return -error;
      }
      // Set bit in fence state to indicate that there is a fence from this
      // producer or consumer.
      fence_state_->fetch_or(buffer_state_bit());
    } else {
      // Unset bit in fence state to indicate that there is no fence, so that
      // when consumer to acquire or producer to acquire, it knows no need to
      // check fence for this buffer.
      fence_state_->fetch_and(~buffer_state_bit());
    }
  }

  return 0;
}

@@ -131,31 +245,144 @@ std::unique_ptr<BufferConsumer> BufferConsumer::Import(
                       : LocalChannelHandle{nullptr, -status.error()});
}

int BufferConsumer::LocalAcquire(DvrNativeBufferMetadata* out_meta,
                                 LocalHandle* out_fence) {
  if (!out_meta)
    return -EINVAL;

  // Only check producer bit and this consumer buffer's particular consumer bit.
  // The buffer is can be acquired iff: 1) producer bit is set; 2) consumer bit
  // is not set.
  uint64_t buffer_state = buffer_state_->load();
  if (!BufferHubDefs::IsBufferPosted(buffer_state, buffer_state_bit())) {
    ALOGE("BufferConsumer::LocalAcquire: not posted, id=%d state=%" PRIx64
          " buffer_state_bit=%" PRIx64 ".",
          id(), buffer_state, buffer_state_bit());
    return -EBUSY;
  }

  // Copy the canonical metadata.
  void* metadata_ptr = reinterpret_cast<void*>(&metadata_header_->metadata);
  memcpy(out_meta, metadata_ptr, sizeof(DvrNativeBufferMetadata));
  // Fill in the user_metadata_ptr in address space of the local process.
  if (out_meta->user_metadata_size) {
    out_meta->user_metadata_ptr =
        reinterpret_cast<uint64_t>(user_metadata_ptr_);
  } else {
    out_meta->user_metadata_ptr = 0;
  }

  uint64_t fence_state = fence_state_->load();
  // If there is an acquire fence from producer, we need to return it.
  if (fence_state & BufferHubDefs::kProducerStateBit) {
    *out_fence = shared_acquire_fence_.Duplicate();
  }

  // Set the consumer bit unique to this consumer.
  BufferHubDefs::ModifyBufferState(buffer_state_, 0ULL, buffer_state_bit());
  return 0;
}

int BufferConsumer::Acquire(LocalHandle* ready_fence) {
  return Acquire(ready_fence, nullptr, 0);
}

int BufferConsumer::Acquire(LocalHandle* ready_fence, void* meta,
                            size_t meta_size_bytes) {
                            size_t user_metadata_size) {
  ATRACE_NAME("BufferConsumer::Acquire");
  LocalFence fence;
  auto return_value =
      std::make_pair(std::ref(fence), WrapBuffer(meta, meta_size_bytes));
  auto status = InvokeRemoteMethodInPlace<BufferHubRPC::ConsumerAcquire>(
      &return_value, meta_size_bytes);
  if (status && ready_fence)
    *ready_fence = fence.take();
  return status ? 0 : -status.error();

  if (const int error = CheckMetadata(user_metadata_size))
    return error;

  DvrNativeBufferMetadata canonical_meta;
  if (const int error = LocalAcquire(&canonical_meta, ready_fence))
    return error;

  if (meta && user_metadata_size) {
    void* metadata_src =
        reinterpret_cast<void*>(canonical_meta.user_metadata_ptr);
    if (metadata_src) {
      memcpy(meta, metadata_src, user_metadata_size);
    } else {
      ALOGW("BufferConsumer::Acquire: no user-defined metadata.");
    }
  }

  auto status = InvokeRemoteMethod<BufferHubRPC::ConsumerAcquire>();
  if (!status)
    return -status.error();
  return 0;
}

int BufferConsumer::AcquireAsync(DvrNativeBufferMetadata* out_meta,
                                 LocalHandle* out_fence) {
  ATRACE_NAME("BufferConsumer::AcquireAsync");

  if (const int error = LocalAcquire(out_meta, out_fence))
    return error;

  auto status = SendImpulse(BufferHubRPC::ConsumerAcquire::Opcode);
  if (!status)
    return -status.error();
  return 0;
}

int BufferConsumer::LocalRelease(const DvrNativeBufferMetadata* meta,
                                 const LocalHandle& release_fence) {
  if (const int error = CheckMetadata(meta->user_metadata_size))
    return error;

  // Check invalid state transition.
  uint64_t buffer_state = buffer_state_->load();
  if (!BufferHubDefs::IsBufferAcquired(buffer_state)) {
    ALOGE("BufferConsumer::LocalRelease: not acquired id=%d state=%" PRIx64 ".",
          id(), buffer_state);
    return -EBUSY;
  }

  // On release, only the user requested metadata is copied back into the shared
  // memory for metadata. Since there are multiple consumers, it doesn't make
  // sense to send the canonical metadata back to the producer. However, one of
  // the consumer can still choose to write up to user_metadata_size bytes of
  // data into user_metadata_ptr.
  if (meta->user_metadata_ptr && meta->user_metadata_size) {
    void* metadata_src = reinterpret_cast<void*>(meta->user_metadata_ptr);
    memcpy(user_metadata_ptr_, metadata_src, meta->user_metadata_size);
  }

  // Send out the release fence through the shared epoll fd. Note that during
  // releasing the producer is not expected to be polling on the fence.
  if (const int error = UpdateSharedFence(release_fence, shared_release_fence_))
    return error;

  // For release operation, the client don't need to change the state as it's
  // bufferhubd's job to flip the produer bit once all consumers are released.
  return 0;
}

int BufferConsumer::Release(const LocalHandle& release_fence) {
  ATRACE_NAME("BufferConsumer::Release");

  DvrNativeBufferMetadata meta;
  if (const int error = LocalRelease(&meta, release_fence))
    return error;

  return ReturnStatusOrError(InvokeRemoteMethod<BufferHubRPC::ConsumerRelease>(
      BorrowedFence(release_fence.Borrow())));
}

int BufferConsumer::ReleaseAsync() {
  DvrNativeBufferMetadata meta;
  return ReleaseAsync(&meta, LocalHandle());
}

int BufferConsumer::ReleaseAsync(const DvrNativeBufferMetadata* meta,
                                 const LocalHandle& release_fence) {
  ATRACE_NAME("BufferConsumer::ReleaseAsync");

  if (const int error = LocalRelease(meta, release_fence))
    return error;

  return ReturnStatusOrError(
      SendImpulse(BufferHubRPC::ConsumerRelease::Opcode));
}
@@ -168,24 +395,25 @@ int BufferConsumer::SetIgnore(bool ignore) {
}

BufferProducer::BufferProducer(uint32_t width, uint32_t height, uint32_t format,
                               uint32_t usage, size_t metadata_size)
    : BufferProducer(width, height, format, usage, usage, metadata_size) {}
                               uint32_t usage, size_t user_metadata_size)
    : BufferProducer(width, height, format, usage, usage, user_metadata_size) {}

BufferProducer::BufferProducer(uint32_t width, uint32_t height, uint32_t format,
                               uint64_t producer_usage, uint64_t consumer_usage,
                               size_t metadata_size)
                               size_t user_metadata_size)
    : BASE(BufferHubRPC::kClientPath) {
  ATRACE_NAME("BufferProducer::BufferProducer");
  ALOGD_IF(TRACE,
           "BufferProducer::BufferProducer: fd=%d width=%u height=%u format=%u "
           "producer_usage=%" PRIx64 " consumer_usage=%" PRIx64
           " metadata_size=%zu",
           " user_metadata_size=%zu",
           event_fd(), width, height, format, producer_usage, consumer_usage,
           metadata_size);
           user_metadata_size);

  // (b/37881101) Deprecate producer/consumer usage
  auto status = InvokeRemoteMethod<BufferHubRPC::CreateBuffer>(
      width, height, format, (producer_usage | consumer_usage), metadata_size);
      width, height, format, (producer_usage | consumer_usage),
      user_metadata_size);
  if (!status) {
    ALOGE(
        "BufferProducer::BufferProducer: Failed to create producer buffer: %s",
@@ -206,27 +434,28 @@ BufferProducer::BufferProducer(uint32_t width, uint32_t height, uint32_t format,
BufferProducer::BufferProducer(const std::string& name, int user_id,
                               int group_id, uint32_t width, uint32_t height,
                               uint32_t format, uint32_t usage,
                               size_t meta_size_bytes)
                               size_t user_metadata_size)
    : BufferProducer(name, user_id, group_id, width, height, format, usage,
                     usage, meta_size_bytes) {}
                     usage, user_metadata_size) {}

BufferProducer::BufferProducer(const std::string& name, int user_id,
                               int group_id, uint32_t width, uint32_t height,
                               uint32_t format, uint64_t producer_usage,
                               uint64_t consumer_usage, size_t meta_size_bytes)
                               uint64_t consumer_usage,
                               size_t user_metadata_size)
    : BASE(BufferHubRPC::kClientPath) {
  ATRACE_NAME("BufferProducer::BufferProducer");
  ALOGD_IF(TRACE,
           "BufferProducer::BufferProducer: fd=%d name=%s user_id=%d "
           "group_id=%d width=%u height=%u format=%u producer_usage=%" PRIx64
           " consumer_usage=%" PRIx64 " meta_size_bytes=%zu",
           " consumer_usage=%" PRIx64 " user_metadata_size=%zu",
           event_fd(), name.c_str(), user_id, group_id, width, height, format,
           producer_usage, consumer_usage, meta_size_bytes);
           producer_usage, consumer_usage, user_metadata_size);

  // (b/37881101) Deprecate producer/consumer usage
  auto status = InvokeRemoteMethod<BufferHubRPC::CreatePersistentBuffer>(
      name, user_id, group_id, width, height, format,
      (producer_usage | consumer_usage), meta_size_bytes);
      (producer_usage | consumer_usage), user_metadata_size);
  if (!status) {
    ALOGE(
        "BufferProducer::BufferProducer: Failed to create/get persistent "
@@ -260,12 +489,12 @@ BufferProducer::BufferProducer(uint64_t producer_usage, uint64_t consumer_usage,
  const int width = static_cast<int>(size);
  const int height = 1;
  const int format = HAL_PIXEL_FORMAT_BLOB;
  const size_t meta_size_bytes = 0;
  const size_t user_metadata_size = 0;

  // (b/37881101) Deprecate producer/consumer usage
  auto status = InvokeRemoteMethod<BufferHubRPC::CreateBuffer>(
      width, height, format, (producer_usage | consumer_usage),
      meta_size_bytes);
      user_metadata_size);
  if (!status) {
    ALOGE("BufferProducer::BufferProducer: Failed to create blob: %s",
          status.GetErrorMessage().c_str());
@@ -299,12 +528,12 @@ BufferProducer::BufferProducer(const std::string& name, int user_id,
  const int width = static_cast<int>(size);
  const int height = 1;
  const int format = HAL_PIXEL_FORMAT_BLOB;
  const size_t meta_size_bytes = 0;
  const size_t user_metadata_size = 0;

  // (b/37881101) Deprecate producer/consumer usage
  auto status = InvokeRemoteMethod<BufferHubRPC::CreatePersistentBuffer>(
      name, user_id, group_id, width, height, format,
      (producer_usage | consumer_usage), meta_size_bytes);
      (producer_usage | consumer_usage), user_metadata_size);
  if (!status) {
    ALOGE(
        "BufferProducer::BufferProducer: Failed to create persistent "
@@ -360,28 +589,141 @@ BufferProducer::BufferProducer(LocalChannelHandle channel)
  }
}

int BufferProducer::LocalPost(const DvrNativeBufferMetadata* meta,
                              const LocalHandle& ready_fence) {
  if (const int error = CheckMetadata(meta->user_metadata_size))
    return error;

  // Check invalid state transition.
  uint64_t buffer_state = buffer_state_->load();
  if (!BufferHubDefs::IsBufferGained(buffer_state)) {
    ALOGE("BufferProducer::LocalPost: not gained, id=%d state=%" PRIx64 ".",
          id(), buffer_state);
    return -EBUSY;
  }

  // Copy the canonical metadata.
  void* metadata_ptr = reinterpret_cast<void*>(&metadata_header_->metadata);
  memcpy(metadata_ptr, meta, sizeof(DvrNativeBufferMetadata));
  // Copy extra user requested metadata.
  if (meta->user_metadata_ptr && meta->user_metadata_size) {
    void* metadata_src = reinterpret_cast<void*>(meta->user_metadata_ptr);
    memcpy(user_metadata_ptr_, metadata_src, meta->user_metadata_size);
  }

  // Send out the acquire fence through the shared epoll fd. Note that during
  // posting no consumer is not expected to be polling on the fence.
  if (const int error = UpdateSharedFence(ready_fence, shared_acquire_fence_))
    return error;

  // Set the producer bit atomically to transit into posted state.
  BufferHubDefs::ModifyBufferState(buffer_state_, 0ULL,
                                   BufferHubDefs::kProducerStateBit);
  return 0;
}

int BufferProducer::Post(const LocalHandle& ready_fence, const void* meta,
                         size_t meta_size_bytes) {
                         size_t user_metadata_size) {
  ATRACE_NAME("BufferProducer::Post");

  // Populate cononical metadata for posting.
  DvrNativeBufferMetadata canonical_meta;
  canonical_meta.user_metadata_ptr = reinterpret_cast<uint64_t>(meta);
  canonical_meta.user_metadata_size = user_metadata_size;

  if (const int error = LocalPost(&canonical_meta, ready_fence))
    return error;

  return ReturnStatusOrError(InvokeRemoteMethod<BufferHubRPC::ProducerPost>(
      BorrowedFence(ready_fence.Borrow()), WrapBuffer(meta, meta_size_bytes)));
      BorrowedFence(ready_fence.Borrow())));
}

int BufferProducer::PostAsync(const DvrNativeBufferMetadata* meta,
                              const LocalHandle& ready_fence) {
  ATRACE_NAME("BufferProducer::PostAsync");

  if (const int error = LocalPost(meta, ready_fence))
    return error;

  return ReturnStatusOrError(SendImpulse(BufferHubRPC::ProducerPost::Opcode));
}

int BufferProducer::LocalGain(DvrNativeBufferMetadata* out_meta,
                              LocalHandle* out_fence) {
  uint64_t buffer_state = buffer_state_->load();
  ALOGD_IF(TRACE, "BufferProducer::LocalGain: buffer=%d, state=%" PRIx64 ".",
           id(), buffer_state);

  if (!out_meta)
    return -EINVAL;

  if (!BufferHubDefs::IsBufferReleased(buffer_state)) {
    if (BufferHubDefs::IsBufferGained(buffer_state)) {
      // We don't want to log error when gaining a newly allocated
      // buffer.
      ALOGI("BufferProducer::LocalGain: already gained id=%d.", id());
      return -EALREADY;
    }
    ALOGE("BufferProducer::LocalGain: not released id=%d state=%" PRIx64 ".",
          id(), buffer_state);
    return -EBUSY;
  }

  // Canonical metadata is undefined on Gain. Except for user_metadata and
  // release_fence_mask. Fill in the user_metadata_ptr in address space of the
  // local process.
  if (metadata_header_->metadata.user_metadata_size && user_metadata_ptr_) {
    out_meta->user_metadata_size =
        metadata_header_->metadata.user_metadata_size;
    out_meta->user_metadata_ptr =
        reinterpret_cast<uint64_t>(user_metadata_ptr_);
  } else {
    out_meta->user_metadata_size = 0;
    out_meta->user_metadata_ptr = 0;
  }

  uint64_t fence_state = fence_state_->load();
  // If there is an release fence from consumer, we need to return it.
  if (fence_state & BufferHubDefs::kConsumerStateMask) {
    *out_fence = shared_release_fence_.Duplicate();
    out_meta->release_fence_mask =
        fence_state & BufferHubDefs::kConsumerStateMask;
  }

  // Clear out all bits and the buffer is now back to gained state.
  buffer_state_->store(0ULL);
  return 0;
}

int BufferProducer::Gain(LocalHandle* release_fence) {
  ATRACE_NAME("BufferProducer::Gain");

  DvrNativeBufferMetadata meta;
  if (const int error = LocalGain(&meta, release_fence))
    return error;

  auto status = InvokeRemoteMethod<BufferHubRPC::ProducerGain>();
  if (!status)
    return -status.error();
  if (release_fence)
    *release_fence = status.take().take();
  return 0;
}

int BufferProducer::GainAsync() {
int BufferProducer::GainAsync(DvrNativeBufferMetadata* out_meta,
                              LocalHandle* release_fence) {
  ATRACE_NAME("BufferProducer::GainAsync");

  if (const int error = LocalGain(out_meta, release_fence))
    return error;

  return ReturnStatusOrError(SendImpulse(BufferHubRPC::ProducerGain::Opcode));
}

int BufferProducer::GainAsync() {
  DvrNativeBufferMetadata meta;
  LocalHandle fence;
  return GainAsync(&meta, &fence);
}

std::unique_ptr<BufferProducer> BufferProducer::Import(
    LocalChannelHandle channel) {
  ALOGD_IF(TRACE, "BufferProducer::Import: channel=%d", channel.value());
Loading