already_AddRefed<TextureClient>
TextureClientRecycleAllocator::CreateOrRecycle(ITextureClientAllocationHelper& aHelper)
{
  // TextureAllocationFlags is actually used only by ContentClient.
  // This class does not handle ContentClient's TextureClient allocation.
  MOZ_ASSERT(aHelper.mAllocationFlags == TextureAllocationFlags::ALLOC_DEFAULT ||
             aHelper.mAllocationFlags == TextureAllocationFlags::ALLOC_DISALLOW_BUFFERTEXTURECLIENT ||
             aHelper.mAllocationFlags == TextureAllocationFlags::ALLOC_FOR_OUT_OF_BAND_CONTENT ||
             aHelper.mAllocationFlags == TextureAllocationFlags::ALLOC_MANUAL_SYNCHRONIZATION);
  MOZ_ASSERT(aHelper.mTextureFlags & TextureFlags::RECYCLE);

  RefPtr<TextureClientHolder> textureHolder;

  {
    MutexAutoLock lock(mLock);
    if (mIsDestroyed) {
      return nullptr;
    }
    if (!mPooledClients.empty()) {
      textureHolder = mPooledClients.top();
      mPooledClients.pop();
      // If a pooled TextureClient is not compatible, release it.
      if (!aHelper.IsCompatible(textureHolder->GetTextureClient())) {
        // Release TextureClient.
        RefPtr<Runnable> task = new TextureClientReleaseTask(textureHolder->GetTextureClient());
        textureHolder->ClearTextureClient();
        textureHolder = nullptr;
        mSurfaceAllocator->GetMessageLoop()->PostTask(task.forget());
      } else {
        textureHolder->GetTextureClient()->RecycleTexture(aHelper.mTextureFlags);
      }
    }
  }

  if (!textureHolder) {
    // Allocate new TextureClient
    RefPtr<TextureClient> texture = aHelper.Allocate(mSurfaceAllocator);
    if (!texture) {
      return nullptr;
    }
    textureHolder = new TextureClientHolder(texture);
  }

  {
    MutexAutoLock lock(mLock);
    MOZ_ASSERT(mInUseClients.find(textureHolder->GetTextureClient()) == mInUseClients.end());
    // Register TextureClient
    mInUseClients[textureHolder->GetTextureClient()] = textureHolder;
  }
  RefPtr<TextureClient> client(textureHolder->GetTextureClient());

  // Make sure the texture holds a reference to us, and ask it to call RecycleTextureClient when its
  // ref count drops to 1.
  client->SetRecycleAllocator(this);
  return client.forget();
}
already_AddRefed<TextureClient>
TextureClientRecycleAllocator::CreateOrRecycle(ITextureClientAllocationHelper& aHelper)
{
  MOZ_ASSERT(aHelper.mTextureFlags & TextureFlags::RECYCLE);

  RefPtr<TextureClientHolder> textureHolder;

  {
    MutexAutoLock lock(mLock);
    if (mIsDestroyed) {
      return nullptr;
    }
    if (!mPooledClients.empty()) {
      textureHolder = mPooledClients.top();
      mPooledClients.pop();
      // If the texture's allocator is not open or a pooled TextureClient is
      // not compatible, release it.
      if (!textureHolder->GetTextureClient()->GetAllocator()->IPCOpen() ||
          !aHelper.IsCompatible(textureHolder->GetTextureClient())) {
        // Release TextureClient.
        RefPtr<Runnable> task = new TextureClientReleaseTask(textureHolder->GetTextureClient());
        textureHolder->ClearTextureClient();
        textureHolder = nullptr;
        mSurfaceAllocator->GetTextureForwarder()->GetMessageLoop()->PostTask(task.forget());
      } else {
        textureHolder->GetTextureClient()->RecycleTexture(aHelper.mTextureFlags);
      }
    }
  }

  if (!textureHolder) {
    // Allocate new TextureClient
    RefPtr<TextureClient> texture = aHelper.Allocate(mSurfaceAllocator);
    if (!texture) {
      return nullptr;
    }
    textureHolder = new TextureClientHolder(texture);
  }

  {
    MutexAutoLock lock(mLock);
    MOZ_ASSERT(mInUseClients.find(textureHolder->GetTextureClient()) == mInUseClients.end());
    // Register TextureClient
    mInUseClients[textureHolder->GetTextureClient()] = textureHolder;
  }
  RefPtr<TextureClient> client(textureHolder->GetTextureClient());

  // Make sure the texture holds a reference to us, and ask it to call RecycleTextureClient when its
  // ref count drops to 1.
  client->SetRecycleAllocator(this);
  return client.forget();
}