Blob Blame History Raw
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#include "JobScheduler.h"
#include "mozilla/gfx/Logging.h"

using namespace std;

namespace mozilla {
namespace gfx {

DWORD __stdcall ThreadCallback(void* threadData);

class WorkerThreadWin32 : public WorkerThread {
 public:
  explicit WorkerThreadWin32(MultiThreadedJobQueue* aJobQueue)
      : WorkerThread(aJobQueue) {
    mThread = ::CreateThread(nullptr, 0, ThreadCallback,
                             static_cast<WorkerThread*>(this), 0, nullptr);
  }

  ~WorkerThreadWin32() {
    ::WaitForSingleObject(mThread, INFINITE);
    ::CloseHandle(mThread);
  }

 protected:
  HANDLE mThread;
};

DWORD __stdcall ThreadCallback(void* threadData) {
  WorkerThread* thread = static_cast<WorkerThread*>(threadData);
  thread->Run();
  return 0;
}

WorkerThread* WorkerThread::Create(MultiThreadedJobQueue* aJobQueue) {
  return new WorkerThreadWin32(aJobQueue);
}

bool MultiThreadedJobQueue::PopJob(Job*& aOutJob, AccessType aAccess) {
  for (;;) {
    while (aAccess == BLOCKING && mJobs.empty()) {
      {
        CriticalSectionAutoEnter lock(&mSection);
        if (mShuttingDown) {
          return false;
        }
      }

      HANDLE handles[] = {mAvailableEvent, mShutdownEvent};
      ::WaitForMultipleObjects(2, handles, FALSE, INFINITE);
    }

    CriticalSectionAutoEnter lock(&mSection);

    if (mShuttingDown) {
      return false;
    }

    if (mJobs.empty()) {
      if (aAccess == NON_BLOCKING) {
        return false;
      }
      continue;
    }

    Job* task = mJobs.front();
    MOZ_ASSERT(task);

    mJobs.pop_front();

    if (mJobs.empty()) {
      ::ResetEvent(mAvailableEvent);
    }

    aOutJob = task;
    return true;
  }
}

void MultiThreadedJobQueue::SubmitJob(Job* aJob) {
  MOZ_ASSERT(aJob);
  CriticalSectionAutoEnter lock(&mSection);
  mJobs.push_back(aJob);
  ::SetEvent(mAvailableEvent);
}

void MultiThreadedJobQueue::ShutDown() {
  {
    CriticalSectionAutoEnter lock(&mSection);
    mShuttingDown = true;
  }
  while (mThreadsCount) {
    ::SetEvent(mAvailableEvent);
    ::WaitForSingleObject(mShutdownEvent, INFINITE);
  }
}

size_t MultiThreadedJobQueue::NumJobs() {
  CriticalSectionAutoEnter lock(&mSection);
  return mJobs.size();
}

bool MultiThreadedJobQueue::IsEmpty() {
  CriticalSectionAutoEnter lock(&mSection);
  return mJobs.empty();
}

void MultiThreadedJobQueue::RegisterThread() { mThreadsCount += 1; }

void MultiThreadedJobQueue::UnregisterThread() {
  mSection.Enter();
  mThreadsCount -= 1;
  bool finishShutdown = mThreadsCount == 0;
  mSection.Leave();

  if (finishShutdown) {
    // Can't touch mSection or any other member from now on because this object
    // may get deleted on the main thread after mShutdownEvent is set.
    ::SetEvent(mShutdownEvent);
  }
}

}  // namespace gfx
}  // namespace mozilla