/*
 * Copyright (c) 2011 The Native Client Authors. All rights reserved.
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */


// ConditionVariableEvents provides a doubly-link-list of events for use
// exclusively by the ConditionVariable class.

// This custom container was crafted because no simple combination of STL
// classes appeared to support the functionality required.  The specific
// unusual requirement for a linked-list-class is support for the Extract()
// method, which can remove an element from a list, potentially for insertion
// into a second list.  Most critically, the Extract() method is idempotent,
// turning the indicated element into an extracted singleton whether it was
// contained in a list or not.  This functionality allows one (or more) of
// threads to do the extraction.  The iterator that identifies this extractable
// element (in this case, a pointer to the list element) can be used after
// arbitrary manipulation of the (possibly) enclosing list container.  In
// general, STL containers do not provide iterators that can be used across
// modifications (insertions/extractions) of the enclosing containers, and
// certainly don't provide iterators that can be used if the identified
// element is *deleted* (removed) from the container.

// It is possible to use multiple redundant containers, such as an STL list,
// and an STL map, to achieve similar container semantics.  This container has
// only O(1) methods, while the corresponding (multiple) STL container approach
// would have more complex O(log(N)) methods (yeah... N isn't that large).
// Multiple containers also makes correctness more difficult to assert, as
// data is redundantly stored and maintained, which is generally evil.

#ifndef NATIVE_CLIENT_SRC_TRUSTED_PLATFORM_WIN_CONDITION_VARIABLE_EVENTS_H_
#define NATIVE_CLIENT_SRC_TRUSTED_PLATFORM_WIN_CONDITION_VARIABLE_EVENTS_H_

#include "native_client/src/shared/platform/win/time.h"
#include "native_client/src/shared/platform/win/lock.h"
#include "native_client/src/shared/platform/nacl_log.h"
#include "native_client/src/shared/platform/nacl_check.h"

namespace NaCl {

class ConditionVariable;

// Define elements that are used in a circular linked list.
// The list container is an element with zero as handle_ value.
// The actual list elements will have a non-zero HANDLE as their handle_.
// All access to methods MUST be done under protection of a lock so that links
// can be validated (and not asynchronously changing) during the method calls.
class ConditionVariableEvent {
 private:
  friend class ConditionVariable;

  HANDLE handle_;
  ConditionVariableEvent* next_;  // Doubly linked list.
  ConditionVariableEvent* prev_;

  // Default constructor with no arguments creates a list container.
  explicit ConditionVariableEvent(bool is_list_element = false) {
    if (is_list_element) {
      handle_ = CreateEvent(NULL, false, false, NULL);
      // DCHECK(0 != handle_);  // InitCheck will validate in production.
    } else {
      handle_ = 0;
    }
    next_ = prev_ = this;  // Self referencing circular.
  }

  ~ConditionVariableEvent() {
    // DCHECK(IsSingleton());
    if (0 != handle_) {
      if (0 == CloseHandle(handle_)) {
        NaClLog(LOG_ERROR, "CloseHandle returned 0"
                "in ~ConditionVariableEvent()");
      }
    }
  }

  // Methods for use on lists.
  bool IsEmpty() {
    // DCHECK(ValidateAsList());
    return IsSingleton();
  }

  void PushBack(ConditionVariableEvent* other) {
    // DCHECK(ValidateAsList());
    // DCHECK(other->ValidateAsItem());
    // DCHECK(other->IsSingleton());
    // Prepare other for insertion.
    other->prev_ = prev_;
    other->next_ = this;
    // Cut into list.
    prev_->next_ = other;
    prev_ = other;
    // DCHECK(ValidateAsDistinct(other));
  }

  ConditionVariableEvent* PopFront() {
    // DCHECK(ValidateAsList());
    // DCHECK(!IsSingleton());
    return next_->Extract();
  }

  ConditionVariableEvent* PopBack() {
    // DCHECK(ValidateAsList());
    // DCHECK(!IsSingleton());
    return prev_->Extract();
  }

  // Methods for use on list elements.
  // Accessor method.
  HANDLE handle() {
    // DCHECK(ValidateAsItem());
    return handle_;
  }

  // Pull an element from a list (if it's in one).
  ConditionVariableEvent* Extract() {
    // DCHECK(ValidateAsItem());
    if (!IsSingleton()) {
      // Stitch neighbors together.
      next_->prev_ = prev_;
      prev_->next_ = next_;
      // Make extractee into a singleton.
      prev_ = next_ = this;
    }
    // DCHECK(IsSingleton());
    return this;
  }

  // Method for use on a list element or on a list.
  bool IsSingleton() {
    // DCHECK(ValidateLinks());
    return next_ == this;
  }

  // Provide pre/post conditions to validate correct manipulations.
  bool ValidateAsDistinct(ConditionVariableEvent* other) {
    return ValidateLinks() && other->ValidateLinks() && (this != other);
  }

  bool ValidateAsItem() {
    return (0 != handle_) && ValidateLinks();
  }

  bool ValidateAsList() {
    return (0 == handle_) && ValidateLinks();
  }

  bool ValidateLinks() {
    // Make sure our neighbor's link to us.
    return (next_->prev_ == this) && (prev_->next_ == this);
  }

  NACL_DISALLOW_COPY_AND_ASSIGN(ConditionVariableEvent);
};

}  // namespace NaCl

#endif  // NATIVE_CLIENT_SRC_TRUSTED_PLATFORM_WIN_CONDITION_VARIABLE_EVENTS_H_
