/* -*- 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/. */

#ifndef mozilla_CondVar_h
#define mozilla_CondVar_h

#include "prcvar.h"

#include "mozilla/BlockingResourceBase.h"
#include "mozilla/Mutex.h"

#ifdef MOZILLA_INTERNAL_API
#include "GeckoProfiler.h"
#endif //MOZILLA_INTERNAL_API

namespace mozilla {


/**
 * CondVar
 * Vanilla condition variable.  Please don't use this unless you have a
 * compelling reason --- Monitor provides a simpler API.
 */
class CondVar : BlockingResourceBase
{
public:
  /**
   * CondVar
   *
   * The CALLER owns |aLock|.
   *
   * @param aLock A Mutex to associate with this condition variable.
   * @param aName A name which can reference this monitor
   * @returns If failure, nullptr.
   *          If success, a valid Monitor* which must be destroyed
   *          by Monitor::DestroyMonitor()
   **/
  CondVar(Mutex& aLock, const char* aName)
    : BlockingResourceBase(aName, eCondVar)
    , mLock(&aLock)
  {
    MOZ_COUNT_CTOR(CondVar);
    // |aLock| must necessarily already be known to the deadlock detector
    mCvar = PR_NewCondVar(mLock->mLock);
    if (!mCvar) {
      NS_RUNTIMEABORT("Can't allocate mozilla::CondVar");
    }
  }

  /**
   * ~CondVar
   * Clean up after this CondVar, but NOT its associated Mutex.
   **/
  ~CondVar()
  {
    NS_ASSERTION(mCvar && mLock,
                 "improperly constructed CondVar or double free");
    PR_DestroyCondVar(mCvar);
    mCvar = 0;
    mLock = 0;
    MOZ_COUNT_DTOR(CondVar);
  }

#ifndef DEBUG
  /**
   * Wait
   * @see prcvar.h
   **/
  nsresult Wait(PRIntervalTime aInterval = PR_INTERVAL_NO_TIMEOUT)
  {

#ifdef MOZILLA_INTERNAL_API
    GeckoProfilerSleepRAII profiler_sleep;
#endif //MOZILLA_INTERNAL_API
    // NSPR checks for lock ownership
    return PR_WaitCondVar(mCvar, aInterval) == PR_SUCCESS ? NS_OK :
                                                            NS_ERROR_FAILURE;
  }
#else
  nsresult Wait(PRIntervalTime aInterval = PR_INTERVAL_NO_TIMEOUT);
#endif // ifndef DEBUG

  /**
   * Notify
   * @see prcvar.h
   **/
  nsresult Notify()
  {
    // NSPR checks for lock ownership
    return PR_NotifyCondVar(mCvar) == PR_SUCCESS ? NS_OK : NS_ERROR_FAILURE;
  }

  /**
   * NotifyAll
   * @see prcvar.h
   **/
  nsresult NotifyAll()
  {
    // NSPR checks for lock ownership
    return PR_NotifyAllCondVar(mCvar) == PR_SUCCESS ? NS_OK : NS_ERROR_FAILURE;
  }

#ifdef DEBUG
  /**
   * AssertCurrentThreadOwnsMutex
   * @see Mutex::AssertCurrentThreadOwns
   **/
  void AssertCurrentThreadOwnsMutex()
  {
    mLock->AssertCurrentThreadOwns();
  }

  /**
   * AssertNotCurrentThreadOwnsMutex
   * @see Mutex::AssertNotCurrentThreadOwns
   **/
  void AssertNotCurrentThreadOwnsMutex()
  {
    mLock->AssertNotCurrentThreadOwns();
  }

#else
  void AssertCurrentThreadOwnsMutex() {}
  void AssertNotCurrentThreadOwnsMutex() {}

#endif  // ifdef DEBUG

private:
  CondVar();
  CondVar(CondVar&);
  CondVar& operator=(CondVar&);

  Mutex* mLock;
  PRCondVar* mCvar;
};


} // namespace mozilla


#endif  // ifndef mozilla_CondVar_h