/*
   +----------------------------------------------------------------------+
   | HipHop for PHP                                                       |
   +----------------------------------------------------------------------+
   | Copyright (c) 2010-2014 Facebook, Inc. (http://www.facebook.com)     |
   +----------------------------------------------------------------------+
   | This source file is subject to version 3.01 of the PHP license,      |
   | that is bundled with this package in the file LICENSE, and is        |
   | available through the world-wide-web at the following url:           |
   | http://www.php.net/license/3_01.txt                                  |
   | If you did not receive a copy of the PHP license and are unable to   |
   | obtain it through the world-wide-web, please send a note to          |
   | license@php.net so we can mail you a copy immediately.               |
   +----------------------------------------------------------------------+
*/
#ifndef incl_HPHP_WRITELEASE_H_
#define incl_HPHP_WRITELEASE_H_

#include "hphp/util/compilation-flags.h"
#include "hphp/runtime/base/runtime-option.h"

#include <pthread.h>

namespace HPHP { namespace jit {

/*
 * The write Lease guards write access to the translation caches,
 * srcDB, and TransDB. The term "lease" is meant to indicate that
 * the right of ownership is conferred for a long, variable time:
 * often the entire length of a request. If a request is not
 * actively translating, it will perform a "hinted drop" of the lease:
 * the lease is unlocked but all calls to acquire(false) from other
 * threads will fail for a short period of time.
 */

struct LeaseHolderBase;
struct Lease {
  friend struct LeaseHolderBase;

  static const int64_t kStandardHintExpireInterval = 1500; // in microseconds
  pthread_t       m_owner;
  pthread_mutex_t m_lock;
  // m_held: since there's no portable, universally invalid pthread_t,
  // explicitly represent the held <-> unheld state machine.
  volatile bool   m_held;
  int64_t           m_hintExpire;
  int64_t           m_hintKept;
  int64_t           m_hintGrabbed;

  Lease() : m_held(false), m_hintExpire(0), m_hintKept(0), m_hintGrabbed(0) {
    pthread_mutex_init(&m_lock, nullptr);
  }
  ~Lease() {
    if (m_held && m_owner == pthread_self()) {
      // Can happen, e.g., in exception scenarios.
      pthread_mutex_unlock(&m_lock);
    }
    pthread_mutex_destroy(&m_lock);
  }
  bool amOwner() const;

  /*
   * A malevolent entity sometimes takes the write lease out from under us
   * for debugging purposes.
   */
  void gremlinLock();
  void gremlinUnlock() {
    if (debug) { gremlinUnlockImpl(); }
  }

  static bool mayLock(bool f);
private:
  // acquire: also returns true if we are already the writer.
  bool acquire(bool blocking = false);
  void drop(int64_t hintExpireDelay = 0);
  void gremlinUnlockImpl();
};

enum class LeaseAcquire {
  ACQUIRE,
  NO_ACQUIRE,
  BLOCKING
};

struct LeaseHolderBase {
  protected:
    LeaseHolderBase(Lease& l, LeaseAcquire acquire);

  public:
    ~LeaseHolderBase();
    explicit operator bool() const { return m_haveLock; }
    bool acquire();

  private:
    Lease& m_lease;
    bool m_haveLock;
    bool m_acquired;
};
struct LeaseHolder : public LeaseHolderBase {
  explicit LeaseHolder(Lease& l, LeaseAcquire acquire)
    : LeaseHolderBase(l, acquire) {}
  explicit LeaseHolder(Lease& l)
    : LeaseHolderBase(l,
                      RuntimeOption::EvalJitRequireWriteLease ?
                      LeaseAcquire::BLOCKING : LeaseAcquire::ACQUIRE)
    {}
};
struct BlockingLeaseHolder : public LeaseHolderBase {
  explicit BlockingLeaseHolder(Lease& l)
    : LeaseHolderBase(l, LeaseAcquire::BLOCKING) {}
};

}} // HPHP::jit

#endif /* incl_HPHP_WRITELEASE_H_ */
