// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
// vim: ts=8 sw=2 smarttab
/*
 * Ceph - scalable distributed file system
 *
 * Copyright (C) 2004-2006 Sage Weil <sage@newdream.net>
 *
 * This is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License version 2.1, as published by the Free Software
 * Foundation.  See file COPYING.
 *
 */


#ifndef CEPH_COND_H
#define CEPH_COND_H

#include "common/Clock.h"
#include "common/ceph_mutex.h"
#include "include/Context.h"

/**
 * context to signal a cond
 *
 * Generic context to signal a cond and store the return value.  We
 * assume the caller is holding the appropriate lock.
 */
class C_Cond : public Context {
  ceph::condition_variable& cond;   ///< Cond to signal
  bool *done;   ///< true if finish() has been called
  int *rval;    ///< return value
public:
  C_Cond(ceph::condition_variable &c, bool *d, int *r) : cond(c), done(d), rval(r) {
    *done = false;
  }
  void finish(int r) override {
    *done = true;
    *rval = r;
    cond.notify_all();
  }
};

/**
 * context to signal a cond, protected by a lock
 *
 * Generic context to signal a cond under a specific lock. We take the
 * lock in the finish() callback, so the finish() caller must not
 * already hold it.
 */
class C_SafeCond : public Context {
  ceph::mutex& lock;    ///< Mutex to take
  ceph::condition_variable& cond;     ///< Cond to signal
  bool *done;     ///< true after finish() has been called
  int *rval;      ///< return value (optional)
public:
  C_SafeCond(ceph::mutex& l, ceph::condition_variable& c, bool *d, int *r=0)
    : lock(l), cond(c), done(d), rval(r) {
    *done = false;
  }
  void finish(int r) override {
    std::lock_guard l{lock};
    if (rval)
      *rval = r;
    *done = true;
    cond.notify_all();
  }
};

/**
 * Context providing a simple wait() mechanism to wait for completion
 *
 * The context will not be deleted as part of complete and must live
 * until wait() returns.
 */
class C_SaferCond : public Context {
  ceph::mutex lock;  ///< Mutex to take
  ceph::condition_variable cond;     ///< Cond to signal
  bool done = false; ///< true after finish() has been called
  int rval = 0;      ///< return value
public:
  C_SaferCond() :
    C_SaferCond("C_SaferCond")
  {}
  explicit C_SaferCond(const std::string &name)
    : lock(ceph::make_mutex(name)) {}
  void finish(int r) override { complete(r); }

  /// We overload complete in order to not delete the context
  void complete(int r) override {
    std::lock_guard l(lock);
    done = true;
    rval = r;
    cond.notify_all();
  }

  /// Returns rval once the Context is called
  int wait() {
    std::unique_lock l{lock};
    cond.wait(l, [this] { return done;});
    return rval;
  }

  /// Wait until the \c secs expires or \c complete() is called
  int wait_for(double secs) {
    return wait_for(ceph::make_timespan(secs));
  }

  int wait_for(ceph::timespan secs) {
    std::unique_lock l{lock};
    if (done) {
      return rval;
    }
    if (cond.wait_for(l, secs, [this] { return done; })) {
      return rval;
    } else {
      return ETIMEDOUT;
    }
  }
};

#endif
