bool
StartDBus()
{
  MOZ_ASSERT(!NS_IsMainThread());
  NS_ENSURE_TRUE(!gDBusThread, true);

  nsRefPtr<DBusThread> dbusThread(new DBusThread());

  bool eventLoopStarted = dbusThread->Initialize();
  NS_ENSURE_TRUE(eventLoopStarted, false);

  nsresult rv;

  if (!gDBusServiceThread) {
    nsIThread* dbusServiceThread;
    rv = NS_NewNamedThread("DBus Thread", &dbusServiceThread);
    NS_ENSURE_SUCCESS(rv, false);
    gDBusServiceThread = dbusServiceThread;
  }

#ifdef DEBUG
  LOG("DBus Thread Starting\n");
#endif

  nsRefPtr<nsIRunnable> pollTask(new DBusPollTask(dbusThread));
  NS_ENSURE_TRUE(pollTask, false);

  rv = gDBusServiceThread->Dispatch(pollTask, NS_DISPATCH_NORMAL);
  NS_ENSURE_SUCCESS(rv, false);

  gDBusThread = dbusThread;

  return true;
}
/* static */
void VideoDecoderManagerChild::InitForContent(
    Endpoint<PVideoDecoderManagerChild>&& aVideoManager) {
  InitializeThread();
  sVideoDecoderChildThread->Dispatch(
      NewRunnableFunction("InitForContentRunnable", &Open,
                          std::move(aVideoManager)),
      NS_DISPATCH_NORMAL);
}
void
VideoDecoderManagerChild::DeallocateSurfaceDescriptorGPUVideo(const SurfaceDescriptorGPUVideo& aSD)
{
  RefPtr<VideoDecoderManagerChild> ref = this;
  SurfaceDescriptorGPUVideo sd = Move(aSD);
  sVideoDecoderChildThread->Dispatch(NS_NewRunnableFunction([ref, sd]() {
    if (ref->CanSend()) {
      ref->SendDeallocateSurfaceDescriptorGPUVideo(sd);
    }
  }), NS_DISPATCH_NORMAL);
}
nsresult
DispatchToDBusThread(nsIRunnable* event)
{
  MOZ_ASSERT(gDBusServiceThread);
  MOZ_ASSERT(gDBusThread);

  nsresult rv = gDBusServiceThread->Dispatch(event, NS_DISPATCH_NORMAL);
  NS_ENSURE_SUCCESS(rv, rv);

  gDBusThread->WakeUp();

  return NS_OK;
}
bool
VideoDecoderManagerChild::DeallocShmem(mozilla::ipc::Shmem& aShmem)
{
  if (NS_GetCurrentThread() != sVideoDecoderChildThread) {
    RefPtr<VideoDecoderManagerChild> self = this;
    mozilla::ipc::Shmem shmem = aShmem;
    sVideoDecoderChildThread->Dispatch(NS_NewRunnableFunction([self, shmem]() {
      if (self->CanSend()) {
        mozilla::ipc::Shmem shmemCopy = shmem;
        self->DeallocShmem(shmemCopy);
      }
    }), NS_DISPATCH_NORMAL);
    return true;
  }
  return PVideoDecoderManagerChild::DeallocShmem(aShmem);
}
/* static */ void
VideoDecoderManagerChild::Shutdown()
{
  MOZ_ASSERT(NS_IsMainThread());

  if (sVideoDecoderChildThread) {
    sVideoDecoderChildThread->Dispatch(NS_NewRunnableFunction([]() {
      if (sDecoderManager && sDecoderManager->CanSend()) {
        sDecoderManager->Close();
        sDecoderManager = nullptr;
      }
    }), NS_DISPATCH_NORMAL);

    sVideoDecoderChildAbstractThread = nullptr;
    sVideoDecoderChildThread->Shutdown();
    sVideoDecoderChildThread = nullptr;

    sRecreateTasks = nullptr;
  }
}
already_AddRefed<SourceSurface> VideoDecoderManagerChild::Readback(
    const SurfaceDescriptorGPUVideo& aSD) {
  // We can't use NS_DISPATCH_SYNC here since that can spin the event
  // loop while it waits. This function can be called from JS and we
  // don't want that to happen.
  SynchronousTask task("Readback sync");

  RefPtr<VideoDecoderManagerChild> ref = this;
  SurfaceDescriptor sd;
  if (NS_FAILED(sVideoDecoderChildThread->Dispatch(
          NS_NewRunnableFunction("VideoDecoderManagerChild::Readback",
                                 [&]() {
                                   AutoCompleteTask complete(&task);
                                   if (ref->CanSend()) {
                                     ref->SendReadback(aSD, &sd);
                                   }
                                 }),
          NS_DISPATCH_NORMAL))) {
    return nullptr;
  }

  task.Wait();

  if (!IsSurfaceDescriptorValid(sd)) {
    return nullptr;
  }

  RefPtr<DataSourceSurface> source = GetSurfaceForDescriptor(sd);
  if (!source) {
    DestroySurfaceDescriptor(this, &sd);
    NS_WARNING("Failed to map SurfaceDescriptor in Readback");
    return nullptr;
  }

  static UserDataKey sSurfaceDescriptor;
  source->AddUserData(&sSurfaceDescriptor,
                      new SurfaceDescriptorUserData(this, sd),
                      DeleteSurfaceDescriptorUserData);

  return source.forget();
}