//  Copyright (c) 2013, Facebook, Inc.  All rights reserved.
//  This source code is licensed under the BSD-style license found in the
//  LICENSE file in the root directory of this source tree. An additional grant
//  of patent rights can be found in the PATENTS file in the same directory.

#pragma once

#include <stdint.h>
#include <deque>
#include <limits>
#include "rocksdb/status.h"
#include "db/write_batch_internal.h"
#include "util/autovector.h"
#include "port/port.h"
#include "util/instrumented_mutex.h"

namespace rocksdb {

class WriteThread {
 public:
  static const uint64_t kNoTimeOut = std::numeric_limits<uint64_t>::max();
  // Information kept for every waiting writer
  struct Writer {
    Status status;
    WriteBatch* batch;
    bool sync;
    bool disableWAL;
    bool in_batch_group;
    bool done;
    uint64_t timeout_hint_us;
    InstrumentedCondVar cv;

    explicit Writer(InstrumentedMutex* mu)
        : batch(nullptr),
          sync(false),
          disableWAL(false),
          in_batch_group(false),
          done(false),
          timeout_hint_us(kNoTimeOut),
          cv(mu) {}
  };

  WriteThread() = default;
  ~WriteThread() = default;

  // Before applying write operation (such as DBImpl::Write, DBImpl::Flush)
  // thread should grab the mutex_ and be the first on writers queue.
  // EnterWriteThread is used for it.
  // Be aware! Writer's job can be done by other thread (see DBImpl::Write
  // for examples), so check it via w.done before applying changes.
  //
  // Writer* w:                writer to be placed in the queue
  // uint64_t expiration_time: maximum time to be in the queue
  // See also: ExitWriteThread
  // REQUIRES: db mutex held
  Status EnterWriteThread(Writer* w, uint64_t expiration_time);

  // After doing write job, we need to remove already used writers from
  // writers_ queue and notify head of the queue about it.
  // ExitWriteThread is used for this.
  //
  // Writer* w:           Writer, that was added by EnterWriteThread function
  // Writer* last_writer: Since we can join a few Writers (as DBImpl::Write
  //                      does)
  //                      we should pass last_writer as a parameter to
  //                      ExitWriteThread
  //                      (if you don't touch other writers, just pass w)
  // Status status:       Status of write operation
  // See also: EnterWriteThread
  // REQUIRES: db mutex held
  void ExitWriteThread(Writer* w, Writer* last_writer, Status status);

  void BuildBatchGroup(Writer** last_writer,
                       autovector<WriteBatch*>* write_batch_group);

 private:
  // Queue of writers.
  std::deque<Writer*> writers_;
};

}  // namespace rocksdb
